Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2026-02-08 22:16:18 +01:00
padre 12b4c0ebd7
commit 54807b9982
Se han modificado 14 ficheros con 2326 adiciones y 72 borrados

54
.gitignore vendido
Ver fichero

@@ -1,2 +1,56 @@
# Base de datos SQLite (repositorio)
*.sqlite *.sqlite
skin/icons/ skin/icons/
# Archivos de build
dist/
*.xpi
*.zip
*.tar.gz
*.crx
# Dependencias Node.js
node_modules/
package-lock.json
yarn.lock
# Archivos del editor
.vscode/
.idea/
*.swp
*.swo
*~
# Sistema operativo
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Thumbs.db
Desktop.ini
# Archivos temporales
*.tmp
*.log
*.cache
.cache/
# Logs y artifacts
npm-debug.log*
yarn-debug.log*
yarn-error.log*
web-ext-artifacts/
# Archivos de backup
*.bak
*.backup
# Cobertura de tests
coverage/
.nyc_output/
# sql.js descargado localmente (opcional)
content/sql-wasm.js
content/sql-wasm.wasm

39
.webextignore Archivo normal
Ver fichero

@@ -0,0 +1,39 @@
# Archivos legacy de XUL/XPCOM (no necesarios en WebExtensions)
install.rdf
chrome.manifest
bootstrap.js
update.xml
# Documentación (opcional, pero puede incluirse)
README.md
MIGRATION.md
INSTALL.md
# Control de versiones
.git/
.gitignore
.gitattributes
# Archivos de desarrollo
.vscode/
.idea/
*.swp
*~
.DS_Store
Thumbs.db
# Archivos de build
*.xpi
*.zip
*.tar.gz
# Node modules (si se usan herramientas de build)
node_modules/
package.json
package-lock.json
yarn.lock
# Archivos temporales
*.tmp
*.log
.cache/

196
CHANGELOG.md Archivo normal
Ver fichero

@@ -0,0 +1,196 @@
# Changelog
Todos los cambios notables de este proyecto serán documentados en este archivo.
El formato está basado en [Keep a Changelog](https://keepachangelog.com/es/1.0.0/),
y este proyecto adhiere a [Semantic Versioning](https://semver.org/lang/es/).
## [3.0.0] - 2026-02-08
### 🎉 Reescritura completa a WebExtensions
Esta es una actualización mayor que moderniza completamente la extensión desde el sistema legacy XUL/XPCOM a WebExtensions modernas.
### ✨ Añadido
- **manifest.json** - Manifiesto WebExtensions Manifest v2
- **background.js** - Script de fondo con APIs modernas
- **db-webext.js** - Wrapper para base de datos usando sql.js
- **Hash routing** - Navegación usando `#` en lugar de protocolo personalizado
- **Compatibilidad con Chrome** - Ahora funciona en Chrome, Edge, Brave, Vivaldi
- **Documentación moderna**:
- `MIGRATION.md` - Guía completa de migración desde v2
- `INSTALL.md` - Instrucciones de instalación
- `README-v3.md` - Documentación actualizada
- `CHANGELOG.md` - Este archivo
- **package.json** - Scripts de build y desarrollo
- **.webextignore** - Exclusión de archivos legacy del paquete
- Permiso `unlimitedStorage` para bases de datos grandes
- Carga dinámica de módulos JavaScript
- Compatibilidad Chrome/Firefox con detección automática de API
### 🔄 Cambiado
- **Protocolo de navegación**: `caa:` → hash routing (`#list`, `#addon/...`)
- **URLs de recursos**: `chrome://ca-archive/` → rutas relativas
- **Base de datos**: mozIStorageService → sql.js (WebAssembly)
- **Storage**: Perfil de Firefox → `browser.storage.local`
- **Imports**: `Cu.import()` → Carga dinámica de scripts
- **APIs**: Components/XPCOM → WebExtensions APIs estándar
- **ca-archive.html** - Actualizado con rutas relativas y hash links
- **ca-archive.js** - Refactorizado sin Components, async/await
- Versión mínima de Firefox: 45 → 57 (Quantum)
### ❌ Eliminado (deprecado, aún en carpeta pero no usado)
- **install.rdf** - Reemplazado por manifest.json
- **chrome.manifest** - Ya no necesario en WebExtensions
- **bootstrap.js** - Reemplazado por background.js
- **update.xml** - Actualizaciones vía stores oficiales
- Soporte para Firefox <57 (ESR 45-52)
- Soporte para Pale Moon, Basilisk, SeaMonkey, Waterfox Classic
- Protocolo personalizado `caa:`
- Botón personalizable en barra de herramientas
- Menú en Tools
- Modificación de páginas de addons.mozilla.org
- Compatibilidad con e10s deshabilitado
### 🐛 Corregido
- Ya no requiere deshabilitar firma de extensiones en Firefox
- Compatible con multi-proceso (e10s) obligatorio
- No conflictos con Content Security Policy moderna
- Carga correcta en navegadores basados en Chromium
### 🔒 Seguridad
- Uso de APIs estándar y seguras de WebExtensions
- Eliminación de acceso a componentes internos del navegador
- CSP estricto con excepciones mínimas necesarias
### 📊 Compatibilidad
#### ✅ Ahora compatible con:
- Firefox 57+ (Quantum)
- Chrome 80+
- Microsoft Edge 80+ (Chromium)
- Brave 1.20+
- Vivaldi 3.0+
- Opera 67+
#### ❌ Ya no compatible con:
- Firefox <57 (ESR 45-56, Firefox 45-56)
- Pale Moon
- Basilisk
- Waterfox Classic (pre-Current)
- SeaMonkey
- IceCat/IceApe legacy
**Nota:** Para navegadores legacy, seguir usando la versión 2.x.
---
## [2.0.3] - 2018 (Legacy)
Última versión del sistema XUL/XPCOM legacy.
### Características
- Protocolo personalizado `caa:`
- Base de datos SQLite nativa
- Compatible con Firefox 45-56
- Compatible con Pale Moon, Basilisk, SeaMonkey
- Botón personalizable en toolbar
- Menú en Tools
- Modificación de páginas AMO (inyección de hints)
- 93,598 versiones de 19,450 addons
### Limitaciones conocidas
- No funciona con e10s en Firefox (excepto Waterfox)
- Requiere deshabilitar firma de extensiones en Firefox 48+
- No compatible con Firefox 57+ (Quantum)
---
## Notas de migración
### De 2.x a 3.0
**⚠️ ATENCIÓN: Cambios que rompen compatibilidad**
Esta no es una actualización compatible hacia atrás. La versión 3.0 es una reescritura completa.
#### Si usas Firefox <57
**Quedate en v2.0.3**. La v3.0 no funcionará.
#### Si usas Firefox 57+ o Chrome
**Actualiza a v3.0**. La v2.0.3 no funcionará.
#### Migración de datos
No hay migración automática de datos porque:
- Las APIs de storage son incompatibles
- La base de datos se descarga nueva
- No hay configuraciones persistentes importantes
#### Enlaces rotos
Si tienes bookmarks con URLs `caa:*`, necesitarás recrearlos con:
```
moz-extension://[id]/content/ca-archive.html#[ruta]
```
El ID de la extensión se puede obtener de `about:debugging`.
### Pasos recomendados
1. **Backup** (opcional): Exportar favoritos si tienes links `caa:`
2. **Desinstalar** v2.x desde `about:addons`
3. **Instalar** v3.0 (ver [INSTALL.md](INSTALL.md))
4. **Recrear** bookmarks si es necesario
---
## Formato de versiones
Este proyecto usa [Semantic Versioning](https://semver.org/):
- **MAJOR** (3.x.x) - Cambios incompatibles
- **MINOR** (x.1.x) - Nuevas características compatibles
- **PATCH** (x.x.1) - Correcciones de bugs
---
## Roadmap futuro
### [3.1.0] - Planificado
- [ ] Dark mode / tema oscuro
- [ ] Mejora de búsqueda (índices full-text)
- [ ] Favoritos sincronizados
- [ ] Exportar/importar colecciones
- [ ] Optimización de carga de DB (chunks)
- [ ] Mejor manejo de errores
### [4.0.0] - Considerando
- [ ] Migración a Manifest V3 (cuando Firefox tenga soporte completo)
- [ ] Reescritura con framework moderno (React/Vue/Svelte)
- [ ] API pública para datos de addons
- [ ] Versión web (sin extensión)
- [ ] Integración con Archive.org API
---
## Enlaces
- [Código fuente](https://github.com/JustOff/ca-archive)
- [Releases](https://github.com/JustOff/ca-archive/releases)
- [Issues](https://github.com/JustOff/ca-archive/issues)
- [Discusiones](https://github.com/JustOff/ca-archive/discussions)
---
[3.0.0]: https://github.com/JustOff/ca-archive/releases/tag/v3.0.0
[2.0.3]: https://github.com/JustOff/ca-archive/releases/tag/2.0.3

171
INSTALL.md Archivo normal
Ver fichero

@@ -0,0 +1,171 @@
# Guía de Instalación Rápida - Classic Add-ons Archive v3.0
## Para Desarrolladores / Testing Local
### Firefox
**Opción 1: Carga temporal (recomendado para desarrollo)**
1. Abre Firefox
2. Escribe en la barra de direcciones: `about:debugging#/runtime/this-firefox`
3. Click en "Cargar complemento temporal..."
4. Navega a la carpeta del proyecto y selecciona el archivo `manifest.json`
La extensión se instalará hasta que cierres Firefox.
**Opción 2: Instalación permanente (requiere firmado)**
1. Empaquetar la extensión:
```bash
cd /home/ale/projects/firefox/ca-archive
zip -r ca-archive-3.0.xpi manifest.json background.js content/ skin/ -x "*.git*" -x "*~"
```
2. Firmar en addons.mozilla.org o usar Firefox Developer/Nightly con firma deshabilitada
### Chrome / Edge / Brave
**Modo desarrollador (sin empaquetar):**
1. Abre el navegador
2. Ve a: `chrome://extensions/` (o `edge://extensions/`)
3. Activa el interruptor "Modo de desarrollador" (esquina superior derecha)
4. Click en "Cargar extensión sin empaquetar"
5. Selecciona la carpeta: `/home/ale/projects/firefox/ca-archive`
La extensión quedará instalada permanentemente en modo desarrollo.
**Empaquetar para distribución:**
```bash
cd /home/ale/projects/firefox/ca-archive
zip -r ca-archive-3.0.zip manifest.json background.js content/ skin/ -x "*.git*" -x "*~"
```
## Verificación de Instalación
Después de instalar, deberías ver:
1. ✅ Icono de la extensión en la barra de herramientas
2. ✅ Click en el icono abre la página del catálogo
3. ✅ Mensaje de carga de base de datos (primera vez)
4. ✅ Navegación funcionando (categorías, búsqueda, etc.)
## Troubleshooting
### La base de datos no carga
**Error:** "Loading database for the first time. Please wait..."
**Solución:**
1. Asegúrate de tener la carpeta `content/db/` con el archivo SQLite
2. Verifica que el archivo `ca-archive-19030501.sqlite` exista y no esté corrupto
3. Revisa la consola del navegador (`F12` > Console) para errores específicos
### El icono no aparece
**Solución:**
1. Refresca la página `about:debugging` o `chrome://extensions/`
2. Verifica que `manifest.json` tiene el campo `browser_action` correctamente
3. Revisa que las imágenes en `skin/button.png` existan
### Errores de carga de scripts
**Error:** "Failed to load script: ..."
**Solución:**
1. Verifica que todos los archivos JS estén en `content/`
2. Revisa la política CSP en `manifest.json`
3. Asegúrate de que `db-webext.js` se carga antes de `ca-archive.js`
### sql.js no carga
**Error:** "Could not load SQL engine"
**Solución:**
1. Verifica conexión a internet (se descarga desde CDN)
2. O descarga sql.js localmente:
```bash
cd content/
wget https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js
wget https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.wasm
```
3. Actualiza las rutas en `content/db-webext.js`
## Para Usuarios Finales
### Instalar desde archivo .xpi/.zip
**Firefox:**
1. Descarga el archivo `.xpi`
2. Arrastra y suelta en Firefox
3. Autoriza la instalación
**Chrome:**
1. Descarga el archivo `.zip`
2. Descomprime en una carpeta
3. Sigue los pasos de "Modo desarrollador" arriba
### Instalar desde stores (cuando esté publicado)
**Firefox Add-ons (AMO):**
- Visita: https://addons.mozilla.org/firefox/addon/ca-archive/
- Click en "Añadir a Firefox"
**Chrome Web Store:**
- Visita: [URL de Chrome Web Store]
- Click en "Añadir a Chrome"
## Desinstalación
### Firefox
1. `about:addons` > Extensiones
2. Busca "Classic Add-ons Archive"
3. Click en "..." > Eliminar
### Chrome
1. `chrome://extensions/`
2. Busca la extensión
3. Click en "Eliminar"
## Archivos Necesarios
Archivos mínimos para que funcione la extensión:
```
ca-archive/
├── manifest.json ← REQUERIDO
├── background.js ← REQUERIDO
├── content/
│ ├── ca-archive.html ← REQUERIDO
│ ├── ca-archive.js ← REQUERIDO
│ ├── db-webext.js ← REQUERIDO
│ ├── db/
│ │ └── ca-archive-19030501.sqlite ← REQUERIDO
│ ├── addon.js
│ ├── list.js
│ ├── versions.js
│ ├── tcloud.js
│ ├── about.js
│ └── epl.js
└── skin/
├── logo.png ← REQUERIDO
├── button.png ← REQUERIDO
├── ca-archive.css ← REQUERIDO
└── ...
```
## Siguientes Pasos
Una vez instalada y funcionando:
1. 📖 Lee [MIGRATION.md](MIGRATION.md) para entender los cambios
2. 🔧 Si eres desarrollador, revisa la arquitectura modernizada
3. 🐛 Reporta bugs en GitHub Issues
4. 🌟 Si te gusta, deja una valoración
## Soporte
- 📝 Documentación completa: [MIGRATION.md](MIGRATION.md)
- 🐛 Reportar problemas: https://github.com/JustOff/ca-archive/issues
- 💬 Discusiones: https://github.com/JustOff/ca-archive/discussions

237
MIGRATION.md Archivo normal
Ver fichero

@@ -0,0 +1,237 @@
# Migración a WebExtensions (Versión 3.0)
## Resumen de cambios
La extensión Classic Add-ons Archive ha sido modernizada desde el sistema legacy XUL/XPCOM a WebExtensions para ser compatible con navegadores modernos Firefox (57+) y Chrome.
## Cambios principales
### 1. Sistema de manifiesto
**ANTES (Legacy):**
- `install.rdf` - Manifiesto XML
- `chrome.manifest` - Registro de recursos chrome://
- `bootstrap.js` - Script de arranque con XPCOM
**AHORA (WebExtensions):**
- `manifest.json` - Manifiesto estándar WebExtensions (Manifest V2)
- `background.js` - Script de fondo con APIs modernas
### 2. Protocolo de navegación
**ANTES:**
```
caa:list
caa:addon/some-addon
caa:about
```
**AHORA:**
```
chrome-extension://.../content/ca-archive.html#list
chrome-extension://.../content/ca-archive.html#addon/some-addon
chrome-extension://.../content/ca-archive.html#about
```
La navegación ahora usa hash (#) en lugar de un protocolo personalizado.
### 3. Acceso a recursos
**ANTES:**
```html
<link href="chrome://ca-archive/skin/ca-archive.css">
<script src="chrome://ca-archive/content/ca-archive.js">
```
**AHORA:**
```html
<link href="../skin/ca-archive.css">
<script src="ca-archive.js">
```
Todos los recursos usan rutas relativas.
### 4. Base de datos SQLite
**ANTES:**
- Acceso directo a SQLite usando `mozIStorageService`
- Almacenamiento en perfil del usuario
**AHORA:**
- Usa `sql.js` (SQLite compilado a JavaScript/WebAssembly)
- Base de datos almacenada en `browser.storage.local`
- Primera carga descarga y cachea la DB
### 5. APIs reemplazadas
| Legacy API | WebExtensions API |
|------------|-------------------|
| `Components.utils` | Scripts estándar ES6 |
| `Services.prefs` | `browser.storage.local` |
| `Services.wm` | `browser.tabs` y `browser.windows` |
| `mozIStorageService` | `sql.js` |
| `XPCOMUtils` | APIs nativas del navegador |
| `Cu.import()` | `import` / carga dinámica de scripts |
### 6. Interfaz de usuario
**ANTES:**
- Botón insertado directamente en la barra de herramientas
- Menú en Tools
**AHORA:**
- `browser.browserAction` - Botón estándar en la barra
- Sin menú en Tools (no disponible en WebExtensions)
## Estructura de archivos
### Archivos nuevos
- `manifest.json` - Manifiesto WebExtensions
- `background.js` - Script de fondo
- `content/db-webext.js` - Wrapper de base de datos moderno
### Archivos modificados
- `content/ca-archive.html` - Rutas relativas, navegación por hash
- `content/ca-archive.js` - Sin Components, carga dinámica de módulos
### Archivos obsoletos (ya no se usan)
- `install.rdf` - Reemplazado por manifest.json
- `chrome.manifest` - Ya no necesario
- `bootstrap.js` - Reemplazado por background.js
- `update.xml` - Actualizaciones ahora vía Firefox/Chrome stores
## Compatibilidad
### Navegadores soportados
| Navegador | Versión mínima | Notas |
|-----------|----------------|-------|
| Firefox | 57+ (Quantum) | Soporte completo |
| Chrome | 80+ | Requiere manifest V2 |
| Edge (Chromium) | 80+ | Compatible con Chrome |
| Brave | 1.20+ | Compatible con Chrome |
| Vivaldi | 3.0+ | Compatible con Chrome |
### Navegadores NO soportados
Los siguientes navegadores que usaban la versión legacy ya no son compatibles con la versión WebExtensions:
- Firefox ESR 45-52
- Firefox 45-56
- Pale Moon
- Basilisk
- Waterfox Classic (pre-Current)
- SeaMonkey
**Nota:** Para estos navegadores, seguir usando la versión 2.x legacy.
## Instalación
### Firefox
1. **Desde archivo local:**
```bash
# Empaquetar la extensión
cd /home/ale/projects/firefox/ca-archive
zip -r ca-archive-3.0.zip * -x ".*" -x "*.md" -x "update.xml" -x "install.rdf" -x "chrome.manifest" -x "bootstrap.js"
```
2. En Firefox, ir a `about:debugging` > Este Firefox > Cargar extensión temporal
3. Seleccionar el archivo `manifest.json` o el archivo `.zip`
### Chrome/Edge/Brave
1. Ir a `chrome://extensions/`
2. Activar "Modo de desarrollador"
3. Click en "Cargar extensión sin empaquetar"
4. Seleccionar la carpeta del proyecto
## Desarrollo
### Dependencias
La extensión requiere `sql.js` para el acceso a la base de datos SQLite:
```html
<!-- Cargado automáticamente desde CDN en db-webext.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js"></script>
```
**Para producción:** Descargar sql.js localmente y actualizar las rutas en `db-webext.js`.
### Debugging
**Firefox:**
- `about:debugging` > Inspeccionar
- Console del navegador: `Ctrl+Shift+J`
**Chrome:**
- `chrome://extensions/` > Detalles > Inspeccionar vistas: background page
- DevTools: `F12`
### Testing
1. Abrir la extensión
2. Verificar que la base de datos se carga correctamente
3. Probar navegación: listados, búsquedas, detalles de addons
4. Verificar que las descargas funcionan
## Limitaciones conocidas
### WebExtensions vs Legacy
1. **Multi-proceso (e10s):** Ahora sólo funciona con e10s activado
2. **Protocolo personalizado:** Ya no disponible, usa rutas de extensión
3. **Modificación de AMO pages:** No puede inyectar hints en addons.mozilla.org
4. **Barra de herramientas:** Botón en ubicación fija, no personalizable
5. **Storage:** Base de datos limitada por cuota de storage del navegador
### Tamaño de base de datos
Chrome/Firefox tienen límites de storage:
- Chrome: ~10MB en `storage.local` (puede ampliarse con `unlimitedStorage`)
- Firefox: Sin límite práctico en `storage.local`
Si la base de datos es muy grande (>10MB), considerar:
1. Agregar permiso `unlimitedStorage` en manifest.json
2. Usar chunks/compresión
3. Almacenar en IndexedDB
## Migración de datos de usuario
La nueva versión NO migra automáticamente datos de la versión legacy porque:
1. Las APIs de storage son completamente diferentes
2. La base de datos se descarga nueva en cada instalación
3. No hay configuraciones de usuario persistentes
## Próximos pasos
### Para publicar en stores
1. **Firefox Add-ons (AMO):**
- Crear cuenta en addons.mozilla.org
- Subir archivo .zip
- Esperar revisión
2. **Chrome Web Store:**
- Crear cuenta de desarrollador ($5 USD único)
- Subir archivo .zip
- Completar listing
### Mejoras futuras
- [ ] Migrar a Manifest V3 (cuando Firefox tenga mejor soporte)
- [ ] Offline-first: cachear más contenido
- [ ] Sincronización de favoritos/bookmarks
- [ ] Dark mode
- [ ] Búsqueda mejorada con índices
## Soporte
- GitHub: https://github.com/JustOff/ca-archive/
- Issues: https://github.com/JustOff/ca-archive/issues
## Licencia
La extensión continúa bajo Mozilla Public License 2.0 (MPL-2.0).

287
README-v3.md Archivo normal
Ver fichero

@@ -0,0 +1,287 @@
# Classic Add-ons Archive v3.0 - WebExtensions Edition
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
[![Firefox](https://img.shields.io/badge/Firefox-57%2B-orange.svg)](https://www.mozilla.org/firefox/)
[![Chrome](https://img.shields.io/badge/Chrome-80%2B-blue.svg)](https://www.google.com/chrome/)
## 🎉 Nueva versión modernizada
Esta es la versión 3.0 de Classic Add-ons Archive, completamente reescrita usando **WebExtensions** para ser compatible con navegadores modernos.
### ✨ Características principales
- 📦 **93,598 versiones** de **19,450 extensiones clásicas de Firefox**
- 🔍 Búsqueda y navegación por categorías
- 📊 Ordenamiento por popularidad, valoración, fecha, etc.
- 💾 Base de datos SQLite embebida
- 🌐 Compatible con Firefox 57+, Chrome 80+, Edge, Brave, Vivaldi
### 🆕 Novedades de la versión 3.0
- ✅ Compatible con Firefox Quantum (57+) y Chrome
- ✅ Usa APIs modernas de WebExtensions
- ✅ Ya no requiere Firefox legacy
- ✅ Navegación mejorada con hash routing
- ✅ Base de datos en memoria con sql.js
- ✅ Sin dependencias de XPCOM/XUL
## 🚀 Instalación rápida
### Para usuarios
**Firefox:**
```bash
# Descargar release
# Ir a about:debugging
# Cargar extensión temporal > seleccionar manifest.json
```
**Chrome/Edge/Brave:**
```bash
# Ir a chrome://extensions/
# Activar "Modo de desarrollador"
# "Cargar extensión sin empaquetar" > seleccionar carpeta
```
Documentación completa: [INSTALL.md](INSTALL.md)
### Para desarrolladores
```bash
# Clonar repositorio
git clone https://github.com/JustOff/ca-archive.git
cd ca-archive
# (Opcional) Instalar web-ext para testing
npm install
# Probar en Firefox
npm run start:firefox
# O probar en Chrome
npm run start:chrome
# Construir paquete
npm run build:firefox # Crea .xpi para Firefox
npm run build:chrome # Crea .zip para Chrome
```
## 📁 Estructura del proyecto
```
ca-archive/
├── manifest.json # Manifiesto WebExtensions v2
├── background.js # Script de fondo
├── package.json # Scripts de build (opcional)
├── content/ # Scripts y páginas de contenido
│ ├── ca-archive.html # Página principal de la extensión
│ ├── ca-archive.js # Router y controlador principal
│ ├── db-webext.js # Wrapper de base de datos (sql.js)
│ ├── db.js # [Legacy] No usado en v3
│ ├── addon.js # Vista de detalle de addon
│ ├── list.js # Vista de listado
│ ├── versions.js # Vista de versiones
│ ├── tcloud.js # Nube de tags
│ ├── about.js # Página "Acerca de"
│ ├── epl.js # EULA/Privacy/License
│ └── db/
│ └── ca-archive-*.sqlite # Base de datos SQLite
├── skin/ # Assets visuales
│ ├── ca-archive.css # Estilos principales
│ ├── button.css # [Legacy] No usado en v3
│ ├── logo.png # Logo de la extensión
│ ├── button.png # Icono del botón
│ └── ... # Otros assets
├── MIGRATION.md # Guía de migración desde v2
├── INSTALL.md # Guía de instalación
├── README-v3.md # Este archivo
└── [Legacy - no usados en v3]
├── install.rdf
├── chrome.manifest
├── bootstrap.js
└── update.xml
```
## 🔄 Migración desde v2 (Legacy)
Si vienes de la versión 2.x (XUL/XPCOM), lee la [Guía de Migración](MIGRATION.md) que explica:
- ❌ Qué dejó de funcionar
- ✅ Cómo se reemplazó cada funcionalidad
- 🔧 Cambios en la arquitectura
- 📊 Tabla de compatibilidad de navegadores
### Resumen rápido de cambios
| Aspecto | v2 (Legacy) | v3 (WebExtensions) |
|---------|-------------|-------------------|
| **Manifest** | install.rdf | manifest.json |
| **Protocolo** | caa: | hash routing (#) |
| **APIs** | Components/XPCOM | WebExtensions APIs |
| **Base de datos** | mozIStorageService | sql.js |
| **Navegadores** | Firefox ≤56 | Firefox 57+, Chrome 80+ |
## 🛠️ Desarrollo
### Requisitos
- Node.js 16+ (opcional, para herramientas de build)
- Firefox Developer Edition o Chrome
- Editor de código (VS Code recomendado)
### Setup
```bash
# Instalar dependencias de desarrollo
npm install
# Lint
npm run lint
# Testing local
npm run start:firefox # Abre Firefox con la extensión
# o
npm run start:chrome # Abre Chrome con la extensión
```
### Testing manual
1. Hacer cambios en el código
2. En `about:debugging` (Firefox) o `chrome://extensions/` (Chrome), click en "Recargar"
3. Abrir la extensión y verificar cambios
4. Revisar console (`F12`) para errores
### Build para distribución
```bash
# Firefox (.xpi)
npm run build:firefox
# Output: dist/ca-archive-3.0.0.zip (renombrar a .xpi)
# Chrome (.zip)
npm run build:chrome
# Output: dist/ca-archive-3.0-chrome.zip
```
## 🐛 Debugging
### Abrir consola de la extensión
**Firefox:**
1. `about:debugging` > Este Firefox
2. Click en "Inspeccionar" en la extensión
**Chrome:**
1. `chrome://extensions/`
2. Click en "Inspeccionar vistas: background page"
### Logs útiles
```javascript
// En background.js
console.log("Background script:", message);
// En content scripts
console.log("Content script:", message);
// Errores de base de datos
// Revisar consola de la página ca-archive.html
```
## 📝 Notas técnicas
### Base de datos SQLite
- **Tamaño:** ~50MB (comprimido: ~15MB)
- **Engine:** sql.js (SQLite compilado a WebAssembly)
- **Storage:** `browser.storage.local` (primera carga)
- **Límite:** Chrome 10MB (usa `unlimitedStorage` permission)
### Permisos requeridos
- `storage` - Cachear base de datos
- `unlimitedStorage` - DB grande (>10MB)
- `tabs` - Abrir tabs del catálogo
- `webRequest` + `webRequestBlocking` - Modificar headers de descarga
- `<all_urls>` - Interceptar requests a web.archive.org
### Políticas de seguridad
```json
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
```
- `'unsafe-eval'` - Requerido por sql.js (usa WebAssembly)
- Todos los scripts deben estar en el paquete (no CDN inline)
## 🤝 Contribuir
¡Las contribuciones son bienvenidas!
### Áreas de mejora
- [ ] Migrar a Manifest V3 (cuando Firefox tenga mejor soporte)
- [ ] Optimizar carga de base de datos (chunks, compresión)
- [ ] Modo oscuro (dark mode)
- [ ] Búsqueda con índices full-text
- [ ] Favoritos/bookmarks sincronizados
- [ ] Mejoras de UI/UX
### Proceso
1. Fork del repositorio
2. Crear branch: `git checkout -b feature/mi-mejora`
3. Commit: `git commit -am 'Añadir nueva característica'`
4. Push: `git push origin feature/mi-mejora`
5. Crear Pull Request
## 📄 Licencia
**Mozilla Public License 2.0 (MPL-2.0)**
- ✅ Uso comercial
- ✅ Modificación
- ✅ Distribución
- ✅ Uso privado
- ⚠️ Divulgar fuente si se modifica
Ver [LICENSE](LICENSE) para más detalles.
### Atribuciones
- Diseño original: Mozilla AMO (CC-BY-SA-3.0)
- Datos: Dominio público / respectivos autores
- sql.js: MIT License
- Logos y marcas: Propiedad de sus respectivos dueños
## 👥 Autores
- **Off JustOff** - Autor original - [JustOff](https://github.com/JustOff)
- Migración a WebExtensions v3.0 - 2026
## 🔗 Enlaces
- 🏠 [Sitio web](https://ca-archive.us.to/)
- 📦 [Releases](https://github.com/JustOff/ca-archive/releases)
- 🐛 [Issues](https://github.com/JustOff/ca-archive/issues)
- 💬 [Discusiones](https://github.com/JustOff/ca-archive/discussions)
- 📖 [Documentación](https://github.com/JustOff/ca-archive/wiki)
## ⭐ Agradecimientos
- Mozilla y la comunidad AMO
- Wayback Machine (Internet Archive)
- Zetamex Network (hosting)
- Todos los desarrolladores de extensiones clásicas
---
**¿Te gusta el proyecto?** Dale una ⭐ en GitHub!
**¿Encontraste un bug?** [Repórtalo aquí](https://github.com/JustOff/ca-archive/issues)
**¿Tienes preguntas?** [Pregunta en Discusiones](https://github.com/JustOff/ca-archive/discussions)

544
TROUBLESHOOTING.md Archivo normal
Ver fichero

@@ -0,0 +1,544 @@
# Guía de Solución de Problemas (Troubleshooting)
Esta guía te ayudará a resolver problemas comunes al usar Classic Add-ons Archive v3.0.
## 📋 Índice
1. [Problemas de Instalación](#problemas-de-instalación)
2. [Problemas con la Base de Datos](#problemas-con-la-base-de-datos)
3. [Problemas de Navegación](#problemas-de-navegación)
4. [Problemas de Rendimiento](#problemas-de-rendimiento)
5. [Errores en la Consola](#errores-en-la-consola)
6. [Incompatibilidades](#incompatibilidades)
7. [Obtener Ayuda](#obtener-ayuda)
---
## Problemas de Instalación
### El icono de la extensión no aparece
**Síntomas:**
- No veo el icono en la barra de herramientas
**Soluciones:**
1. **Recargar la extensión:**
- Firefox: `about:debugging` > Click en "Recargar"
- Chrome: `chrome://extensions/` > Click en el icono de recargar ↻
2. **Verificar que está habilitada:**
- Firefox: `about:addons` > Extensiones > Buscar "Classic Add-ons Archive"
- Chrome: `chrome://extensions/` > Buscar la extensión y verificar que el toggle está activado
3. **Verificar manifest.json:**
```bash
cat manifest.json | grep -A 5 browser_action
```
Debe existir la sección `browser_action` con iconos válidos.
### Error al cargar la extensión
**Error:** "There was an error during installation"
**Causas comunes:**
- `manifest.json` inválido
- Archivos requeridos faltantes
- Permisos incorrectos
**Soluciones:**
1. **Validar el manifest:**
```bash
# Instalar web-ext si no lo tienes
npm install -g web-ext
# Validar
web-ext lint
```
2. **Verificar archivos requeridos:**
```bash
ls -la manifest.json background.js
ls -la content/ca-archive.html content/ca-archive.js content/db-webext.js
ls -la skin/logo.png skin/button.png
```
3. **Verificar sintaxis JSON:**
- Usar herramienta online: https://jsonlint.com/
- O con python:
```bash
python3 -m json.tool manifest.json > /dev/null
```
### Firefox: "This add-on could not be installed because it appears to be corrupt"
**Solución:**
1. Reconstruir el paquete:
```bash
./build.sh
```
2. Verificar integridad:
```bash
unzip -t dist/ca-archive-3.0.0.xpi
```
3. Si persiste, cargar sin empaquetar:
- `about:debugging` > Cargar complemento temporal
- Seleccionar `manifest.json` directamente
---
## Problemas con la Base de Datos
### "Loading database for the first time" se queda cargando
**Síntomas:**
- Mensaje de carga de DB sin terminar
- Página en blanco
**Causas:**
- Base de datos muy grande
- Falta archivo SQLite
- Error de red (si carga desde CDN)
**Soluciones:**
1. **Verificar que existe el archivo DB:**
```bash
ls -lh content/db/*.sqlite
```
2. **Verificar tamaño de la DB:**
```bash
du -h content/db/*.sqlite
```
Si es >50MB, puede tardar en el primer acceso.
3. **Revisar la consola del navegador** (`F12` > Console):
- Buscar errores relacionados con fetch o sql.js
4. **Limpiar storage y reintentar:**
```javascript
// En la consola del navegador
browser.storage.local.clear()
// o en Chrome:
chrome.storage.local.clear()
```
5. **Verificar permisos del archivo:**
```bash
chmod 644 content/db/*.sqlite
```
### Error: "Could not load SQL engine"
**Causa:** sql.js no se pudo cargar
**Soluciones:**
1. **Verificar conexión a internet** (si usa CDN)
2. **Descargar sql.js localmente:**
```bash
cd content/
wget https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js
wget https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.wasm
```
3. **Actualizar rutas en `db-webext.js`:**
```javascript
// Cambiar:
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
// Por:
locateFile: file => browser.runtime.getURL(`content/${file}`)
```
4. **Agregar archivos a `web_accessible_resources` en manifest.json:**
```json
"web_accessible_resources": [
"content/*.wasm",
"content/sql-wasm.js"
]
```
### Error: "Database has just been updated, not ready or corrupted!"
**Causa:** Archivo SQLite corrupto o inaccesible
**Soluciones:**
1. **Verificar integridad de la DB:**
```bash
sqlite3 content/db/ca-archive-*.sqlite "PRAGMA integrity_check;"
```
Debe retornar "ok"
2. **Re-descargar la base de datos:**
- Si tienes repositorio Git: `git checkout content/db/*.sqlite`
- O descargar manualmente desde releases
3. **Permisos de lectura:**
```bash
chmod 644 content/db/*.sqlite
```
---
## Problemas de Navegación
### Los enlaces no funcionan
**Síntomas:**
- Click en categoría/addon no hace nada
- URL no cambia
- Página en blanco
**Soluciones:**
1. **Verificar hash routing:**
- La URL debe ser: `moz-extension://[id]/content/ca-archive.html#list`
- No debe ser: `caa:list` (eso es legacy)
2. **Revisar consola JavaScript** (`F12`):
- Buscar errores en `ca-archive.js`
3. **Verificar que los módulos se cargan:**
```javascript
// En la consola del navegador
console.log(typeof List, typeof Addon, typeof DB);
// Debe mostrar los tipos de objeto/function
```
4. **Limpiar caché del navegador:**
- Firefox: `Ctrl+Shift+Del` > Marcar "Caché"
- Chrome: `Ctrl+Shift+Del` > "Cached images and files"
### Búsqueda no funciona
**Síntomas:**
- Campo de búsqueda no responde
- No muestra resultados
**Soluciones:**
1. **Verificar que la DB está cargada:**
- Debe haber completado la carga inicial
- Revisar que no hay mensajes de error en consola
2. **Verificar el formulario:**
```html
<form id="search" action="#list">
```
Debe tener `action="#list"` (no `caa:list`)
3. **Probar búsqueda directa:**
- Navegar manualmente a: `#list?q=tu-búsqueda`
---
## Problemas de Rendimiento
### La extensión es lenta
**Síntomas:**
- Navegación lenta entre páginas
- La UI se congela
- Alta uso de CPU/RAM
**Causas:**
- Base de datos muy grande en memoria
- Consultas SQL no optimizadas
- Demasiados addons en listado
**Soluciones:**
1. **Verificar uso de memoria:**
- Firefox: `about:performance`
- Chrome: `chrome://process-internals/`
2. **Reducir resultados por página:**
- Editar módulos que hacen consultas (list.js, addon.js)
- Agregar límite LIMIT a queries SQL
3. **Considerar IndexedDB en lugar de sql.js:**
- sql.js carga toda la DB en RAM
- IndexedDB maneja datos en disco de forma nativa
4. **Cerrar otras extensiones:**
- Conflictos con otras extensiones pueden causar lentitud
### Carga inicial muy lenta (>30 segundos)
**Causa:** Base de datos grande descargándose por primera vez
**Soluciones:**
1. **Paciencia:** Primera carga es normal que tarde
- DB de ~50MB puede tardar 30-60s en redes lentas
2. **Verificar progreso:**
- Abrir Network tab (`F12` > Network)
- Buscar descarga del archivo .sqlite
3. **Pre-cargar la DB:**
- Copiar manualmente a storage:
```javascript
// Demasiado complejo, mejor esperar carga natural
```
4. **Usar conexión más rápida** para primera instalación
---
## Errores en la Consola
### "ReferenceError: DB is not defined"
**Causa:** `db-webext.js` no se cargó antes de `ca-archive.js`
**Solución:**
Verificar orden en `ca-archive.html`:
```html
<!-- CORRECTO: -->
<script src="db-webext.js"></script>
<script src="ca-archive.js"></script>
<!-- INCORRECTO: -->
<script src="ca-archive.js"></script>
<script src="db-webext.js"></script>
```
### "Failed to load script: versions.js"
**Causa:** Módulo no existe o ruta incorrecta
**Soluciones:**
1. **Verificar que existe:**
```bash
ls -la content/versions.js
```
2. **Verificar permisos:**
```bash
chmod 644 content/*.js
```
3. **Verificar en web_accessible_resources** (manifest.json):
```json
"web_accessible_resources": [
"content/*.js"
]
```
### "Unsafe attempt to load URL"
**Causa:** Violación de Content Security Policy
**Solución:**
Actualizar CSP en manifest.json:
```json
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
```
**Nota:** `'unsafe-eval'` es necesario para sql.js (WebAssembly).
### "TypeError: Cannot read property 'openDB' of undefined"
**Causa:** Variable `DB` no está definida globalmente
**Solución:**
En `db-webext.js`, verificar al final:
```javascript
if (typeof window !== "undefined") {
window.DB = DB;
}
```
---
## Incompatibilidades
### No funciona en Firefox 56 o inferior
**Explicación:**
- v3.0 requiere Firefox 57+ (WebExtensions puras)
- Firefox 56 y anteriores usan sistema legacy
**Solución:**
- Usar la versión 2.x para Firefox ≤56
- O actualizar Firefox a versión moderna
### No funciona en Pale Moon / Basilisk
**Explicación:**
- Estos navegadores no soportan WebExtensions completas
- Usan sistema legacy de Firefox
**Solución:**
- Usar la versión 2.x legacy
- No hay planes de portar v3 a estos navegadores
### Problemas en Chrome con storage
**Error:** "QUOTA_BYTES quota exceeded"
**Causa:** Base de datos muy grande para storage.local de Chrome (límite 10MB)
**Solución:**
1. **Ya incluido:** Permiso `unlimitedStorage` en manifest.json
2. **Verificar que está presente:**
```json
"permissions": [
"unlimitedStorage"
]
```
3. **Si sigue fallando:** Considerar:
- Comprimir la DB
- Usar chunks para storage
- Usar IndexedDB nativa
---
## Obtener Ayuda
Si ninguna solución funciona:
### 1. Recopilar información
**Información del sistema:**
- Navegador y versión: `about:support` (Firefox) o `chrome://version/` (Chrome)
- Versión de la extensión: Ver manifest.json
- Sistema operativo
**Logs:**
```javascript
// En consola del navegador (F12)
console.log("Extension ID:", browser.runtime.id);
console.log("Extension URL:", browser.runtime.getURL(""));
// En consola de background script
// (about:debugging > Inspeccionar)
// Copiar todos los console.log relevantes
```
**Errores:**
- Captura de pantalla de errores en consola
- Texto completo de mensajes de error
### 2. Verificar problemas conocidos
- GitHub Issues: https://github.com/JustOff/ca-archive/issues
- Buscar error específico
### 3. Reportar bug
Si no existe issue similar, crear uno nuevo:
**Plantilla:**
```markdown
## Descripción del problema
[Describe qué está fallando]
## Pasos para reproducir
1. [Primer paso]
2. [Segundo paso]
3. [...]
## Comportamiento esperado
[Qué debería pasar]
## Comportamiento actual
[Qué está pasando]
## Entorno
- Navegador: [Firefox 100 / Chrome 105]
- SO: [Linux / Windows / macOS]
- Versión extensión: [3.0.0]
## Logs
```
[Pegar logs de consola aquí]
```
## Capturas
[Adjuntar screenshots si aplica]
```
### 4. Contacto
- GitHub Issues: https://github.com/JustOff/ca-archive/issues
- Discusiones: https://github.com/JustOff/ca-archive/discussions
- Email: [Especificar si aplica]
---
## Debugging Avanzado
### Habilitar logging detallado
En `background.js`, agregar al inicio:
```javascript
const DEBUG = true;
function log(...args) {
if (DEBUG) console.log("[CA Archive]", ...args);
}
```
Luego usar `log()` en lugar de `console.log()`.
### Inspeccionar storage
```javascript
// Firefox
browser.storage.local.get(null).then(console.log);
// Chrome
chrome.storage.local.get(null, console.log);
```
### Ejecutar queries SQL manualmente
```javascript
// En consola de ca-archive.html
if (DB.db) {
const results = DB.execute("SELECT COUNT(*) FROM addons");
console.log(results);
}
```
### Profiling de rendimiento
1. Abrir DevTools (`F12`)
2. Ir a tab "Performance" / "Rendimiento"
3. Click en "Record" / "Grabar"
4. Realizar acción lenta
5. Stop y analizar flamegraph
---
## Recursos Adicionales
- [Documentación Mozilla WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
- [Chrome Extensions API](https://developer.chrome.com/docs/extensions/reference/)
- [sql.js Documentation](https://sql.js.org/)
- [Debugging WebExtensions (Firefox)](https://extensionworkshop.com/documentation/develop/debugging/)
- [Debugging Chrome Extensions](https://developer.chrome.com/docs/extensions/mv2/tut_debugging/)
---
**¿Solucionaste tu problema?** ¡Considera contribuir esta solución a la documentación!
**¿Problema diferente?** Abre un issue en GitHub: https://github.com/JustOff/ca-archive/issues

149
background.js Archivo normal
Ver fichero

@@ -0,0 +1,149 @@
"use strict";
// Compatibilidad con Chrome y Firefox
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
// Configuración por defecto
const DEFAULT_SETTINGS = {
url: "about",
firstRun: true
};
// Inicializar storage con valores por defecto
browserAPI.runtime.onInstalled.addListener(async (details) => {
if (details.reason === "install") {
await browserAPI.storage.local.set(DEFAULT_SETTINGS);
// Abrir la página "about" en la primera instalación
openArchivePage("about");
} else if (details.reason === "update") {
console.log("Classic Add-ons Archive updated to version", browserAPI.runtime.getManifest().version);
}
});
// Manejar clic en el botón de la barra de herramientas
browserAPI.browserAction.onClicked.addListener(() => {
openArchivePage();
});
// Función para abrir la página del archivo
async function openArchivePage(page) {
try {
// Obtener la URL guardada o usar la página especificada
const settings = await browserAPI.storage.local.get("url");
const targetPage = page || settings.url || "about";
// Construir la URL de la extensión
const extensionUrl = browserAPI.runtime.getURL("content/ca-archive.html");
const fullUrl = `${extensionUrl}#${targetPage}`;
// Verificar si ya hay una pestaña abierta con el archivo
const tabs = await browserAPI.tabs.query({});
let foundTab = null;
for (let tab of tabs) {
if (tab.url && tab.url.startsWith(extensionUrl)) {
foundTab = tab;
break;
}
}
if (foundTab) {
// Si existe, activar la pestaña
await browserAPI.tabs.update(foundTab.id, { active: true });
await browserAPI.windows.update(foundTab.windowId, { focused: true });
// Actualizar el hash si es necesario
if (page) {
await browserAPI.tabs.update(foundTab.id, { url: fullUrl });
}
} else {
// Si no existe, crear nueva pestaña
await browserAPI.tabs.create({ url: fullUrl });
}
// Si abrimos "about" en primera ejecución, cambiar a página principal para siguientes veces
if (targetPage === "about") {
await browserAPI.storage.local.set({ url: "list", firstRun: false });
}
} catch (error) {
console.error("Error opening archive page:", error);
}
}
// Interceptar peticiones para modificar headers cuando sea necesario
browserAPI.webRequest.onHeadersReceived.addListener(
(details) => {
// Modificar headers para archivos de addons.mozilla.org archivados
const headers = details.responseHeaders || [];
// Si es una descarga desde nuestro storage, manejar Content-Disposition
if (details.url.includes("origin=caa")) {
const newHeaders = headers.filter(h => h.name.toLowerCase() !== "content-disposition");
if (details.url.includes("action=install")) {
// No forzar descarga para instalación
return { responseHeaders: newHeaders };
} else if (details.url.includes("action=download")) {
// Forzar descarga
newHeaders.push({
name: "Content-Disposition",
value: "attachment"
});
return { responseHeaders: newHeaders };
}
}
return {};
},
{
urls: ["<all_urls>"]
},
["blocking", "responseHeaders"]
);
// Bloquear ciertas peticiones de web.archive.org
browserAPI.webRequest.onBeforeRequest.addListener(
(details) => {
if (details.url.includes("web.archive.org")) {
// Bloquear scripts y recursos específicos de AMO que no queremos cargar
if (/\/web\/.+?\/(addons\.mozilla\.org\/.+?\/more|addons\.cdn\.mozilla\.net\/.+?\/loading-more\.gif|addons-amo\.cdn\.mozilla\.net\/amo-.+?\.js)/.test(details.url)) {
return { cancel: true };
}
}
return {};
},
{
urls: ["*://web.archive.org/*"]
},
["blocking"]
);
// Manejar comandos de teclado (si se configuran)
if (browserAPI.commands && browserAPI.commands.onCommand) {
browserAPI.commands.onCommand.addListener((command) => {
if (command === "open-archive") {
openArchivePage();
}
});
}
// Escuchar mensajes desde content scripts
browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "openPage") {
openArchivePage(message.page);
sendResponse({ success: true });
} else if (message.action === "getSettings") {
browserAPI.storage.local.get(null).then(settings => {
sendResponse({ settings });
});
return true; // Mantener el canal abierto para respuesta asíncrona
} else if (message.action === "saveSettings") {
browserAPI.storage.local.set(message.settings).then(() => {
sendResponse({ success: true });
});
return true;
}
return false;
});
console.log("Classic Add-ons Archive background script loaded");

192
build.sh Archivo ejecutable
Ver fichero

@@ -0,0 +1,192 @@
#!/bin/bash
# Script de construcción para Classic Add-ons Archive v3.0
# Genera paquetes .xpi (Firefox) y .zip (Chrome)
set -e # Salir si hay errores
echo "=================================================="
echo " Classic Add-ons Archive - Build Script v3.0"
echo "=================================================="
echo ""
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuración
VERSION="3.0.0"
DIST_DIR="dist"
FIREFOX_PACKAGE="ca-archive-${VERSION}.xpi"
CHROME_PACKAGE="ca-archive-${VERSION}-chrome.zip"
# Archivos a incluir (relativos al directorio raíz)
INCLUDE_FILES=(
"manifest.json"
"background.js"
"content/"
"skin/"
)
# Archivos a excluir (patrones de exclusión)
EXCLUDE_PATTERNS=(
"*.git*"
"*~"
"*.swp"
".DS_Store"
"Thumbs.db"
"*.tmp"
"*.log"
"*.md" # Excluir documentación (opcional: comentar para incluir)
"install.rdf" # Legacy
"chrome.manifest" # Legacy
"bootstrap.js" # Legacy
"update.xml" # Legacy
"content/db.js" # Legacy (usamos db-webext.js)
"package.json"
"package-lock.json"
"node_modules/"
"dist/"
)
# Función para imprimir con color
print_status() {
echo -e "${GREEN}[✓]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[!]${NC} $1"
}
print_error() {
echo -e "${RED}[✗]${NC} $1"
}
# Verificar que estamos en el directorio correcto
if [ ! -f "manifest.json" ]; then
print_error "No se encuentra manifest.json"
print_error "Ejecuta este script desde el directorio raíz del proyecto"
exit 1
fi
print_status "Directorio de proyecto detectado"
# Crear directorio de distribución
if [ -d "$DIST_DIR" ]; then
print_warning "Limpiando directorio de distribución existente..."
rm -rf "$DIST_DIR"
fi
mkdir -p "$DIST_DIR"
print_status "Directorio de distribución creado: $DIST_DIR/"
# Función para construir el comando de exclusión para zip
build_exclude_args() {
local args=""
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
args="$args -x '$pattern'"
done
echo "$args"
}
# Construir paquete para Firefox (.xpi)
echo ""
echo "Construyendo paquete para Firefox..."
print_status "Empaquetando archivos..."
# Crear archivo zip temporal
TMP_ZIP="$DIST_DIR/temp.zip"
# Comando de zip con exclusiones
zip -r "$TMP_ZIP" "${INCLUDE_FILES[@]}" \
-x "${EXCLUDE_PATTERNS[@]}" \
> /dev/null 2>&1
# Renombrar a .xpi
mv "$TMP_ZIP" "$DIST_DIR/$FIREFOX_PACKAGE"
if [ -f "$DIST_DIR/$FIREFOX_PACKAGE" ]; then
FIREFOX_SIZE=$(du -h "$DIST_DIR/$FIREFOX_PACKAGE" | cut -f1)
print_status "Paquete Firefox creado: $FIREFOX_PACKAGE ($FIREFOX_SIZE)"
else
print_error "Error al crear paquete Firefox"
exit 1
fi
# Construir paquete para Chrome (.zip)
echo ""
echo "Construyendo paquete para Chrome..."
print_status "Empaquetando archivos..."
zip -r "$DIST_DIR/$CHROME_PACKAGE" "${INCLUDE_FILES[@]}" \
-x "${EXCLUDE_PATTERNS[@]}" \
> /dev/null 2>&1
if [ -f "$DIST_DIR/$CHROME_PACKAGE" ]; then
CHROME_SIZE=$(du -h "$DIST_DIR/$CHROME_PACKAGE" | cut -f1)
print_status "Paquete Chrome creado: $CHROME_PACKAGE ($CHROME_SIZE)"
else
print_error "Error al crear paquete Chrome"
exit 1
fi
# Verificar integridad de los paquetes
echo ""
echo "Verificando paquetes..."
# Verificar Firefox
if unzip -t "$DIST_DIR/$FIREFOX_PACKAGE" > /dev/null 2>&1; then
print_status "Paquete Firefox verificado (integridad OK)"
else
print_error "Paquete Firefox corrupto"
exit 1
fi
# Verificar Chrome
if unzip -t "$DIST_DIR/$CHROME_PACKAGE" > /dev/null 2>&1; then
print_status "Paquete Chrome verificado (integridad OK)"
else
print_error "Paquete Chrome corrupto"
exit 1
fi
# Listar contenido (opcional)
if [ "$1" = "--list" ] || [ "$1" = "-l" ]; then
echo ""
echo "Contenido del paquete Firefox:"
unzip -l "$DIST_DIR/$FIREFOX_PACKAGE"
fi
# Resumen final
echo ""
echo "=================================================="
echo " ✨ Build completado exitosamente"
echo "=================================================="
echo ""
echo "Paquetes generados en: $DIST_DIR/"
echo ""
echo " Firefox: $FIREFOX_PACKAGE ($FIREFOX_SIZE)"
echo " Chrome: $CHROME_PACKAGE ($CHROME_SIZE)"
echo ""
echo "Próximos pasos:"
echo ""
echo " Firefox:"
echo " 1. Ir a about:debugging#/runtime/this-firefox"
echo " 2. Click en 'Cargar complemento temporal'"
echo " 3. Seleccionar: $DIST_DIR/$FIREFOX_PACKAGE"
echo ""
echo " Chrome:"
echo " 1. Ir a chrome://extensions/"
echo " 2. Activar 'Modo de desarrollador'"
echo " 3. Click en 'Cargar extensión sin empaquetar'"
echo " 4. Extraer y seleccionar la carpeta de $CHROME_PACKAGE"
echo ""
echo " Para publicar:"
echo " - Firefox AMO: https://addons.mozilla.org/developers/"
echo " - Chrome Web Store: https://chrome.google.com/webstore/devconsole"
echo ""
echo "=================================================="
exit 0

Ver fichero

@@ -4,8 +4,8 @@
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="shortcut icon" type="image/x-icon" href="chrome://ca-archive/skin/logo.png"> <link rel="shortcut icon" type="image/x-icon" href="../skin/logo.png">
<link rel="stylesheet" href="chrome://ca-archive/skin/ca-archive.css"> <link rel="stylesheet" href="../skin/ca-archive.css">
<title>Classic Add-ons Archive</title> <title>Classic Add-ons Archive</title>
</head> </head>
@@ -16,58 +16,58 @@
<div id="page" class="c"> <div id="page" class="c">
<div class="amo-header"> <div class="amo-header">
<div class="header-search" role="search"> <div class="header-search" role="search">
<form id="search" action="caa:list"> <form id="search" action="#list">
<input id="search-q" name="q" autocomplete="off" title="" class="text placeholder" placeholder="search for add-ons" type="text"> <input id="search-q" name="q" autocomplete="off" title="" class="text placeholder" placeholder="search for add-ons" type="text">
<button class="search-button" type="submit" title="Search" src="chrome://ca-archive/skin/btn-search.png"></button> <button class="search-button" type="submit" title="Search" src="../skin/btn-search.png"></button>
</form> </form>
</div> </div>
<div id="masthead"> <div id="masthead">
<h1 class="site-title"> <h1 class="site-title">
<a href="caa:"> <a href="#">
<img alt="Firefox" src="chrome://ca-archive/skin/logo.png">Classic Add-ons Archive <font size="1">(2004-2018)</font></a> <img alt="Firefox" src="../skin/logo.png">Classic Add-ons Archive <font size="1">(2004-2018)</font></a>
</h1> </h1>
<nav id="site-nav" class="menu-nav c"> <nav id="site-nav" class="menu-nav c">
<ul> <ul>
<li id="extensions"> <li id="extensions">
<a href="caa:list">Categories</a> <a href="#list">Categories</a>
<ul class="two-col"> <ul class="two-col">
<li><a href="caa:list/alerts-updates">Alerts &amp; Updates</a></li> <li><a href="#list/alerts-updates">Alerts &amp; Updates</a></li>
<li><a href="caa:list/appearance">Appearance</a></li> <li><a href="#list/appearance">Appearance</a></li>
<li><a href="caa:list/bookmarks">Bookmarks</a></li> <li><a href="#list/bookmarks">Bookmarks</a></li>
<li><a href="caa:list/download-management">Download Management</a></li> <li><a href="#list/download-management">Download Management</a></li>
<li><a href="caa:list/feeds-news-blogging">Feeds, News &amp; Blogging</a></li> <li><a href="#list/feeds-news-blogging">Feeds, News &amp; Blogging</a></li>
<li><a href="caa:list/games-entertainment">Games &amp; Entertainment</a></li> <li><a href="#list/games-entertainment">Games &amp; Entertainment</a></li>
<li><a href="caa:list/language-support">Language Support</a></li> <li><a href="#list/language-support">Language Support</a></li>
<li><a href="caa:list/photos-music-videos">Photos, Music &amp; Videos</a></li> <li><a href="#list/photos-music-videos">Photos, Music &amp; Videos</a></li>
<li><a href="caa:list/privacy-security">Privacy &amp; Security</a></li> <li><a href="#list/privacy-security">Privacy &amp; Security</a></li>
<li><a href="caa:list/search-tools">Search Tools</a></li> <li><a href="#list/search-tools">Search Tools</a></li>
<li><a href="caa:list/shopping">Shopping</a></li> <li><a href="#list/shopping">Shopping</a></li>
<li><a href="caa:list/social-communication">Social &amp; Communication</a></li> <li><a href="#list/social-communication">Social &amp; Communication</a></li>
<li><a href="caa:list/tabs">Tabs</a></li> <li><a href="#list/tabs">Tabs</a></li>
<li><a href="caa:list/web-development">Web Development</a></li> <li><a href="#list/web-development">Web Development</a></li>
<li><a href="caa:list/other">Other</a></li> <li><a href="#list/other">Other</a></li>
</ul> </ul>
</li> </li>
<li id="listby"> <li id="listby">
<a href="caa:list">List by</a> <a href="#list">List by</a>
<ul> <ul>
<li><a href="caa:list?sort=users">Most Users</a></li> <li><a href="#list?sort=users">Most Users</a></li>
<li><a href="caa:list?sort=rating">Top Rated</a></li> <li><a href="#list?sort=rating">Top Rated</a></li>
<li><a href="caa:list?sort=reviews">Most Reviews</a></li> <li><a href="#list?sort=reviews">Most Reviews</a></li>
<li><a href="caa:list?sort=created">Newest</a></li> <li><a href="#list?sort=created">Newest</a></li>
<li><a href="caa:list?sort=updated">Recently Updated</a></li> <li><a href="#list?sort=updated">Recently Updated</a></li>
<li><a href="caa:list?sort=name">Name </a></li> <li><a href="#list?sort=name">Name </a></li>
</ul> </ul>
</li> </li>
<li id="more"> <li id="more">
<a href="caa:">More…</a> <a href="#">More…</a>
<ul> <ul>
<li><a target="_blank" href="https://addons.mozilla.org/en-US/firefox/">Live Firefox Add-ons Site</a></li> <li><a target="_blank" href="https://addons.mozilla.org/en-US/firefox/">Live Firefox Add-ons Site</a></li>
<li><a target="_blank" href="http://web.archive.org/web/*/https://addons.mozilla.org/en-US/firefox/">Wayback Machine</a></li> <li><a target="_blank" href="http://web.archive.org/web/*/https://addons.mozilla.org/en-US/firefox/">Wayback Machine</a></li>
<li><a href="caa:about">About</a></li> <li><a href="#about">About</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -83,7 +83,8 @@
</div> </div>
<script src="chrome://ca-archive/content/ca-archive.js"></script> <script src="db-webext.js"></script>
<script src="ca-archive.js"></script>
</body> </body>
</html> </html>

Ver fichero

@@ -1,51 +1,107 @@
"use strict"; "use strict";
(function () {
var Cu = Components.utils;
let dbname = "ca-archive-19030501.sqlite"; // Compatibilidad con Chrome y Firefox
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
Cu.import("chrome://ca-archive/content/db.js"); (async function () {
if (!DB.openDB(dbname, document)) { const dbname = "ca-archive-19030501.sqlite";
// Cargar el módulo de base de datos
// NOTA: Los scripts deben cargarse en el HTML antes de este script
// o usar import dinámico
// Inicializar la base de datos (ahora es asíncrono)
const dbOpened = await DB.openDB(dbname, document);
if (!dbOpened) {
console.error("Failed to open database");
return; return;
} }
/* /*
caa:addon/{slug|id}/versions[?page=[#ver]] Rutas soportadas (usando hash):
caa:addon/{slug|id}/{eula|privacy|license/lid} #addon/{slug|id}/versions[?page=[#ver]]
caa:addon/{slug|id} #addon/{slug|id}/{eula|privacy|license/lid}
caa:list[/category][?{tag|q}=][?sort=][?page=] #addon/{slug|id}
caa:[search-query] #list[/category][?{tag|q}=][?sort=][?page=]
*/ #about
#{search-query}
*/
// Función para manejar la navegación
async function handleNavigation() {
try { try {
let params, url = decodeURI(document.location); // Obtener la URL del hash
if ((params = /^caa:addon\/(.+?)\/versions\/?(\?page=(\d+)(#.+)?)?$/.exec(url)) !== null) { let hash = window.location.hash.substring(1); // Remover el '#'
Cu.import("chrome://ca-archive/content/versions.js"); if (!hash) {
hash = "list"; // Por defecto mostrar listado
}
let params;
const url = decodeURI(hash);
if ((params = /^addon\/(.+?)\/versions\/?(\?page=(\d+)(#.+)?)?$/.exec(url)) !== null) {
// Cargar dinámicamente el módulo de versiones si no está cargado
if (typeof Versions === "undefined") {
await loadScript("versions.js");
}
Versions.showPage(document, DB.db, params[1], params[3]); Versions.showPage(document, DB.db, params[1], params[3]);
} else if ((params = /^caa:addon\/(.+?)\/(eula|privacy|license\/(.+))$/.exec(url)) !== null) { } else if ((params = /^addon\/(.+?)\/(eula|privacy|license\/(.+))$/.exec(url)) !== null) {
Cu.import("chrome://ca-archive/content/epl.js"); if (typeof EPL === "undefined") {
await loadScript("epl.js");
}
EPL.showPage(document, DB.db, params[1], params[2], params[3]); EPL.showPage(document, DB.db, params[1], params[2], params[3]);
} else if ((params = /^caa:addon\/(.+?)\/?$/.exec(url)) !== null) { } else if ((params = /^addon\/(.+?)\/?$/.exec(url)) !== null) {
Cu.import("chrome://ca-archive/content/addon.js"); if (typeof Addon === "undefined") {
await loadScript("addon.js");
}
Addon.showPage(document, DB.db, params[1]); Addon.showPage(document, DB.db, params[1]);
} else if ((params = /^caa:list(\/([a-z-]+))?(\?tag=(.+?)|\?q=(.+?))?([\?|\&]sort=(\w+))?\&?(page=(\d+))?$/.exec(url)) !== null) { } else if ((params = /^list(\/([a-z-]+))?(\?tag=(.+?)|\?q=(.+?))?([\?|\&]sort=(\w+))?\&?(page=(\d+))?$/.exec(url)) !== null) {
Cu.import("chrome://ca-archive/content/list.js"); if (typeof List === "undefined") {
await loadScript("list.js");
}
List.showPage(document, DB.db, params[2], params[4], params[5], params[7], params[9]); List.showPage(document, DB.db, params[2], params[4], params[5], params[7], params[9]);
} else if (url == "caa:") { } else if (url === "" || url === "list") {
Cu.import("chrome://ca-archive/content/tcloud.js"); if (typeof TCloud === "undefined") {
await loadScript("tcloud.js");
}
TCloud.showPage(document, DB.db); TCloud.showPage(document, DB.db);
} else if (url == "caa:about") { } else if (url === "about") {
Cu.import("chrome://ca-archive/content/about.js"); if (typeof About === "undefined") {
await loadScript("about.js");
}
About.showPage(document, DB.db); About.showPage(document, DB.db);
} else if ((params = /^caa:(.+)$/.exec(url)) !== null) {
Cu.import("chrome://ca-archive/content/list.js");
List.showPage(document, DB.db, undefined, undefined, params[1], undefined, undefined);
} else { } else {
document.location = "caa:list"; // Búsqueda genérica
if (typeof List === "undefined") {
await loadScript("list.js");
}
List.showPage(document, DB.db, undefined, undefined, url, undefined, undefined);
} }
} catch (e) { } catch (e) {
Cu.reportError(e); console.error("Navigation error:", e);
} }
}
// Función auxiliar para cargar scripts dinámicamente
function loadScript(filename) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = filename;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load script: ${filename}`));
document.head.appendChild(script);
});
}
// Manejar el cambio de hash
window.addEventListener("hashchange", handleNavigation);
// Inicializar la navegación inicial
await handleNavigation();
// Cerrar la base de datos cuando se cierre la página
window.addEventListener("beforeunload", () => {
DB.closeDB(); DB.closeDB();
});
})(); })();

245
content/db-webext.js Archivo normal
Ver fichero

@@ -0,0 +1,245 @@
"use strict";
// Módulo de base de datos para WebExtensions
// Usa sql.js (SQLite compilado a JavaScript/WebAssembly)
const DB = {
db: null,
SQL: null,
/**
* Inicializa sql.js (debe ser llamado primero)
*/
async initSQL() {
if (this.SQL) return this.SQL;
// Cargar sql.js desde CDN o localmente
// Para producción, incluir sql.js localmente
try {
// Intentar cargar sql.js si no está ya cargado
if (typeof window.initSqlJs === "undefined") {
await this.loadScript("https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js");
}
this.SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
});
return this.SQL;
} catch (e) {
console.error("Failed to initialize sql.js:", e);
throw new Error("Could not load SQL engine");
}
},
/**
* Cargar script dinámicamente
*/
loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
},
/**
* Abre o crea la base de datos
* @param {string} dbname - Nombre del archivo de base de datos
* @param {Document} document - Documento HTML
* @returns {boolean} - true si se abrió correctamente
*/
async openDB(dbname, document) {
try {
await this.initSQL();
// Intentar cargar la base de datos desde el storage de la extensión
const dbData = await this.loadDBFromStorage(dbname);
if (dbData) {
// Cargar base de datos existente
this.db = new this.SQL.Database(new Uint8Array(dbData));
console.log("Database loaded from storage");
} else {
// Intentar descargar la base de datos empaquetada
await this.downloadAndStoreDB(dbname, document);
}
return true;
} catch (e) {
console.error("Error opening database:", e);
this.showMessage(document,
"Error loading database. Please check the console for details.",
"db-warning"
);
return false;
}
},
/**
* Carga la base de datos desde el storage local de la extensión
*/
async loadDBFromStorage(dbname) {
try {
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
const result = await browserAPI.storage.local.get(dbname);
return result[dbname];
} catch (e) {
console.warn("Could not load DB from storage:", e);
return null;
}
},
/**
* Descarga y almacena la base de datos
*/
async downloadAndStoreDB(dbname, document) {
this.showMessage(document,
"Loading database for the first time. Please wait...",
"db-warning ok"
);
try {
// Intentar cargar desde la extensión
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
const dbUrl = browserAPI.runtime.getURL(`content/db/${dbname}`);
const response = await fetch(dbUrl);
if (!response.ok) {
throw new Error(`Failed to fetch database: ${response.status}`);
}
const arrayBuffer = await response.arrayBuffer();
this.db = new this.SQL.Database(new Uint8Array(arrayBuffer));
// Guardar en storage para uso futuro
await this.saveDBToStorage(dbname, arrayBuffer);
this.showMessage(document,
"Database loaded successfully!",
"db-warning ok"
);
console.log("Database downloaded and stored");
} catch (e) {
console.error("Error downloading database:", e);
this.showMessage(document,
"Failed to load database. Please reinstall the extension.",
"db-warning bad"
);
throw e;
}
},
/**
* Guarda la base de datos en el storage local
*/
async saveDBToStorage(dbname, arrayBuffer) {
try {
const browserAPI = typeof browser !== "undefined" ? browser : chrome;
// Convertir ArrayBuffer a Array para storage
const uint8Array = new Uint8Array(arrayBuffer);
const array = Array.from(uint8Array);
// Chrome tiene límite de storage, considerar usar chunks para DBs grandes
// Por ahora, intentar guardar directamente
await browserAPI.storage.local.set({ [dbname]: array });
console.log("Database saved to storage");
} catch (e) {
console.warn("Could not save DB to storage (may be too large):", e);
// No es crítico si falla, se recargará la próxima vez
}
},
/**
* Muestra un mensaje en la página
*/
showMessage(document, msg, style) {
const div = document.createElement("div");
div.className = style;
div.appendChild(document.createTextNode(msg));
const page = document.getElementById("page");
if (page) {
page.appendChild(div);
}
},
/**
* Cierra la base de datos
*/
closeDB() {
if (this.db) {
this.db.close();
this.db = null;
}
},
/**
* Ejecuta una consulta SQL
*/
execute(sql, params = []) {
if (!this.db) {
throw new Error("Database not opened");
}
return this.db.exec(sql, params);
},
/**
* Ejecuta una consulta y retorna la primera fila
*/
executeRow(sql, params = []) {
const results = this.execute(sql, params);
if (results.length > 0 && results[0].values.length > 0) {
return results[0].values[0];
}
return null;
},
/**
* Prepara una statement (compatible con API antigua)
*/
createStatement(sql) {
if (!this.db) {
throw new Error("Database not opened");
}
// Crear un objeto que emule la API de mozIStorageStatement
const stmt = this.db.prepare(sql);
return {
params: {},
executeStep() {
return stmt.step();
},
reset() {
stmt.reset();
},
finalize() {
stmt.free();
},
row: new Proxy({}, {
get(target, prop) {
return stmt.get()[stmt.getColumnNames().indexOf(prop)];
}
}),
getString(index) {
return stmt.get()[index];
},
getInt32(index) {
return stmt.get()[index];
},
getDouble(index) {
return stmt.get()[index];
}
};
}
};
// Hacer disponible globalmente para compatibilidad
if (typeof window !== "undefined") {
window.DB = DB;
}

52
manifest.json Archivo normal
Ver fichero

@@ -0,0 +1,52 @@
{
"manifest_version": 2,
"name": "Classic Add-ons Archive",
"version": "3.0.0",
"description": "Catalog of classic Firefox add-ons created before WebExtensions apocalypse",
"author": "Off JustOff",
"homepage_url": "https://github.com/JustOff/ca-archive/",
"icons": {
"48": "skin/logo.png",
"96": "skin/logo.png"
},
"browser_action": {
"default_icon": {
"16": "skin/button.png",
"32": "skin/button.png"
},
"default_title": "Classic Add-ons Archive",
"default_popup": ""
},
"background": {
"scripts": ["background.js"]
},
"permissions": [
"storage",
"unlimitedStorage",
"tabs",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"web_accessible_resources": [
"content/ca-archive.html",
"content/*.js",
"skin/*.css",
"skin/*.png",
"skin/*.gif"
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_specific_settings": {
"gecko": {
"id": "ca-archive@Off.JustOff",
"strict_min_version": "57.0"
}
}
}

31
package.json Archivo normal
Ver fichero

@@ -0,0 +1,31 @@
{
"name": "ca-archive",
"version": "3.0.0",
"description": "Classic Add-ons Archive - WebExtensions Edition",
"author": "Off JustOff <Off.Just.Off@gmail.com>",
"license": "MPL-2.0",
"homepage": "https://github.com/JustOff/ca-archive",
"repository": {
"type": "git",
"url": "https://github.com/JustOff/ca-archive.git"
},
"scripts": {
"lint": "web-ext lint --source-dir=.",
"build:firefox": "web-ext build --source-dir=. --artifacts-dir=./dist --overwrite-dest",
"build:chrome": "npm run zip",
"zip": "zip -r dist/ca-archive-3.0-chrome.zip manifest.json background.js content/ skin/ -x '*.git*' -x '*~' -x '*.md' -x 'install.rdf' -x 'chrome.manifest' -x 'bootstrap.js' -x 'update.xml'",
"start:firefox": "web-ext run --source-dir=. --firefox-preview",
"start:chrome": "web-ext run --source-dir=. --target=chromium"
},
"devDependencies": {
"web-ext": "^7.9.0"
},
"keywords": [
"firefox",
"chrome",
"extensions",
"addons",
"archive",
"webextensions"
]
}