diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..791b4d9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,27 @@ +version: 2 + +updates: + # Enable version updates for npm + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "automated" + commit-message: + prefix: "chore(deps)" + include: "scope" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + labels: + - "github-actions" + - "dependencies" + commit-message: + prefix: "chore(ci)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cf7405c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI + +on: + push: + branches: [main, develop, feat/*] + pull_request: + branches: [main, develop] + +jobs: + lint-and-test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint + + - name: Run formatter check + run: npx prettier --check ./lib/**/*.ts + + - name: Run tests + run: npm run test:run + + - name: Build project + run: npm run build + + coverage: + runs-on: ubuntu-latest + needs: lint-and-test + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run tests with coverage + run: npm run test:coverage + + - name: Upload coverage reports + uses: codecov/codecov-action@v4 + if: github.event_name == 'push' + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..de5474a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + registry-url: "https://registry.npmjs.org" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm run test:run + + - name: Build project + run: npm run build + + - name: Publish to npm + run: npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index f1bfeea..ee936c8 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ dist-ssr .env bun.lockb -bun.lock \ No newline at end of file +bun.lock +# Test coverage +coverage/ +.nyc_output/ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..0a4b97d --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/PLAN_DE_MEJORA.md b/PLAN_DE_MEJORA.md new file mode 100644 index 0000000..2c56631 --- /dev/null +++ b/PLAN_DE_MEJORA.md @@ -0,0 +1,540 @@ +# Plan de Mejora - WebSerial Core + +## Análisis del Proyecto + +WebSerial Core es una biblioteca TypeScript que facilita la comunicación serial desde navegadores web, con soporte para Socket.io para comunicación remota. El proyecto está bien estructurado pero puede beneficiarse de varias mejoras en calidad, mantenibilidad y robustez. + +--- + +## 1. Testing y Cobertura de Código + +### Problemas Identificados +- ❌ No existe infraestructura de testing +- ❌ No hay tests unitarios ni de integración +- ❌ Sin cobertura de código medible + +### Mejoras Propuestas + +#### 1.1 Implementar Framework de Testing +- **Acción**: Integrar Vitest (compatible con Vite) +- **Beneficio**: Tests rápidos y nativamente compatibles con ESM +- **Prioridad**: 🔴 Alta + +**Dependencias a agregar**: +```json +{ + "vitest": "^2.0.0", + "@vitest/ui": "^2.0.0", + "@vitest/coverage-v8": "^2.0.0", + "happy-dom": "^14.0.0" +} +``` + +**Scripts recomendados**: +```json +{ + "test": "vitest", + "test:ui": "vitest --ui", + "test:coverage": "vitest --coverage", + "test:run": "vitest run" +} +``` + +#### 1.2 Áreas Críticas para Testing +- Tests unitarios para `Core.ts` (conversiones hex, manejo de buffers) +- Tests de `Dispatcher.ts` (sistema de eventos) +- Tests de `Devices.ts` (registro y gestión de dispositivos) +- Mocks para Web Serial API +- Tests de integración para flujos completos de conexión + +#### 1.3 Objetivo de Cobertura +- **Meta inicial**: 60-70% cobertura de código +- **Meta a mediano plazo**: 80%+ cobertura + +--- + +## 2. Documentación y Tipos + +### Problemas Identificados +- ⚠️ Falta documentación JSDoc en funciones públicas +- ⚠️ Algunos `any` types que deberían ser más específicos +- ⚠️ README extenso pero falta documentación de API completa + +### Mejoras Propuestas + +#### 2.1 JSDoc Completo +- **Acción**: Agregar JSDoc a todas las funciones y métodos públicos +- **Beneficio**: Mejor IntelliSense y documentación auto-generada +- **Prioridad**: 🟡 Media + +**Ejemplo**: +```typescript +/** + * Connects to a serial device + * @returns Promise that resolves to true if connection successful + * @throws {Error} If device is already connected + * @example + * ```typescript + * await device.connect(); + * ``` + */ +async connect(): Promise +``` + +#### 2.2 Fortalecer Sistema de Tipos +- Eliminar uso de `any` en: + - `Socket.ts`: `#socket: any` → tipar correctamente con Socket.io types + - `auto_response: any` en interfaces +- Crear tipos más estrictos para respuestas seriales +- **Prioridad**: 🟡 Media + +#### 2.3 Documentación API +- **Acción**: Considerar usar TypeDoc para generar documentación +- Crear sección de API Reference separada del README +- **Prioridad**: 🟢 Baja + +--- + +## 3. CI/CD y Automatización + +### Problemas Identificados +- ❌ No existen workflows de GitHub Actions +- ❌ Sin validación automática de PRs +- ❌ Sin publicación automatizada a npm + +### Mejoras Propuestas + +#### 3.1 GitHub Actions Workflows + +**3.1.1 Workflow de CI (`.github/workflows/ci.yml`)** +```yaml +name: CI +on: [push, pull_request] +jobs: + test: + - Ejecutar linter + - Ejecutar tests + - Verificar build + - Reportar cobertura +``` +**Prioridad**: 🔴 Alta + +**3.1.2 Workflow de Release (`.github/workflows/release.yml`)** +```yaml +name: Release +on: + release: + types: [published] +jobs: + publish: + - Build del proyecto + - Publicar a npm + - Crear tag git +``` +**Prioridad**: 🟡 Media + +**3.1.3 Workflow de Code Quality** +```yaml +name: Quality +- Prettier check +- TypeScript strict check +- Dependency audit +``` +**Prioridad**: 🟢 Baja + +#### 3.2 Herramientas Adicionales +- **Husky**: Git hooks para pre-commit +- **lint-staged**: Validar solo archivos modificados +- **commitlint**: Convenciones en mensajes de commit +- **Prioridad**: 🟡 Media + +--- + +## 4. Gestión de Errores y Logging + +### Problemas Identificados +- ⚠️ Manejo de errores inconsistente +- ⚠️ Falta de sistema de logging estructurado +- ⚠️ Errores capturados pero sin contexto suficiente + +### Mejoras Propuestas + +#### 4.1 Sistema de Errores Personalizado +```typescript +class SerialError extends Error { + constructor( + message: string, + public code: string, + public context?: Record + ) { + super(message); + this.name = 'SerialError'; + } +} +``` +**Prioridad**: 🟡 Media + +#### 4.2 Logging Estructurado +- Implementar niveles de log (debug, info, warn, error) +- Permitir configuración de nivel de log +- Integrar con `__debug__` existente +- **Prioridad**: 🟢 Baja + +#### 4.3 Error Recovery +- Estrategias de reintentos automáticos +- Mecanismos de fallback +- Documentar comportamiento en errores +- **Prioridad**: 🟡 Media + +--- + +## 5. Mejoras de Código + +### Problemas Identificados +- ⚠️ Archivo `Core.ts` muy extenso (1657 líneas) +- ⚠️ Algunas funciones con múltiples responsabilidades +- ⚠️ Uso mixto de private fields (`#`) y `__internal__` + +### Mejoras Propuestas + +#### 5.1 Refactorización de Core.ts +- **Acción**: Dividir en módulos más pequeños + - `CoreConnection.ts`: Lógica de conexión + - `CoreParser.ts`: Parseo de datos + - `CoreQueue.ts`: Gestión de cola + - `CoreUtils.ts`: Utilidades de conversión +- **Beneficio**: Mayor mantenibilidad y testabilidad +- **Prioridad**: 🟡 Media + +#### 5.2 Consistencia en Encapsulación +- **Opción A**: Usar solo private fields (`#field`) +- **Opción B**: Usar solo `__internal__` +- Elegir una estrategia y mantenerla +- **Prioridad**: 🟢 Baja + +#### 5.3 Reducir Complejidad Ciclomática +- Extraer métodos largos en funciones más pequeñas +- Aplicar principio de responsabilidad única +- **Prioridad**: 🟡 Media + +--- + +## 6. Performance y Optimización + +### Mejoras Propuestas + +#### 6.1 Memory Management +- Revisar limpieza de buffers (`SerialResponse.buffer`) +- Implementar límites de tamaño para colas +- Verificar que no hay memory leaks en eventos +- **Prioridad**: 🟡 Media + +#### 6.2 Bundle Size +- Analizar tamaño del bundle +- Considerar tree-shaking optimization +- Implementar code splitting si es necesario +- **Herramienta**: `rollup-plugin-visualizer` +- **Prioridad**: 🟢 Baja + +#### 6.3 Lazy Loading +- Socket.io como import dinámico (solo si se usa) +- Reducir bundle para casos sin socket +- **Prioridad**: 🟢 Baja + +--- + +## 7. Seguridad + +### Mejoras Propuestas + +#### 7.1 Dependencias +- **Acción**: Configurar Dependabot +- Auditorías regulares con `npm audit` +- Mantener dependencias actualizadas +- **Prioridad**: 🔴 Alta + +#### 7.2 Validación de Entrada +- Validar filtros de puerto serial +- Sanitizar datos antes de escribir a puerto +- Validar configuraciones de Socket.io +- **Prioridad**: 🟡 Media + +#### 7.3 Seguridad de Socket +- Validar URIs en Socket.ts (ya implementado ✓) +- Considerar autenticación en conexiones socket +- Documentar mejores prácticas de seguridad +- **Prioridad**: 🟡 Media + +--- + +## 8. Compatibilidad y Soporte + +### Mejoras Propuestas + +#### 8.1 Browser Support Matrix +- Documentar navegadores soportados +- Añadir tabla de compatibilidad Web Serial API +- Advertencias para navegadores no soportados +- **Prioridad**: 🟡 Media + +#### 8.2 Polyfills y Fallbacks +- Detectar disponibilidad de Web Serial API +- Proveer mensajes de error útiles +- Guías de configuración por navegador +- **Prioridad**: 🟢 Baja + +#### 8.3 Versiones de Node.js +- Especificar versión mínima en `package.json` +```json +{ + "engines": { + "node": ">=18.0.0" + } +} +``` +- **Prioridad**: 🟢 Baja + +--- + +## 9. Developer Experience + +### Mejoras Propuestas + +#### 9.1 Ejemplos y Playground +- Crear carpeta `/examples` con casos de uso +- Playground interactivo (usando Vite dev server) +- Templates para dispositivos comunes (Arduino, ESP32, etc.) +- **Prioridad**: 🟡 Media + +#### 9.2 Mensajes de Error Mejorados +- Errores más descriptivos y accionables +- Links a documentación en errores +- Sugerencias de solución +- **Prioridad**: 🟡 Media + +#### 9.3 TypeScript Strict Mode +```json +{ + "compilerOptions": { + "strict": true, // ✓ Ya habilitado + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true + } +} +``` +- **Prioridad**: 🟢 Baja + +--- + +## 10. Monitoreo y Observabilidad + +### Mejoras Propuestas + +#### 10.1 Telemetría Opcional +- Sistema opt-in de métricas de uso +- Reportes de errores anónimos +- Analytics de performance +- **Prioridad**: 🟢 Baja + +#### 10.2 Debug Tools +- Mejorar sistema `__debug__` existente +- Panel de debugging en DevTools +- Logs estructurados exportables +- **Prioridad**: 🟢 Baja + +--- + +## 11. Gestión de Versiones + +### Mejoras Propuestas + +#### 11.1 Semantic Versioning +- Seguir estrictamente SemVer +- CHANGELOG automatizado (conventional-changelog) +- Release notes detallados +- **Prioridad**: 🟡 Media + +#### 11.2 Deprecation Strategy +- Marcar funciones obsoletas claramente +- Periodo de transición documentado +- Migration guides entre versiones +- **Prioridad**: 🟡 Media + +--- + +## 12. Build y Distribución + +### Mejoras Propuestas + +#### 12.1 Múltiples Formatos de Salida +- ✓ ESM (ya existe) +- ✓ UMD (ya existe) +- Considerar CJS separado si es necesario +- **Prioridad**: 🟢 Baja + +#### 12.2 Source Maps +- Incluir source maps en distribución +- Facilitar debugging en producción +```javascript +// vite.config.js +build: { + sourcemap: true +} +``` +- **Prioridad**: 🟡 Media + +#### 12.3 Tree Shaking +- Verificar que exports son tree-shakeable +- Documentar imports selectivos +```typescript +// Bueno +import { Core } from 'webserial-core'; + +// También posible +import { Core, Devices } from 'webserial-core'; +``` +- **Prioridad**: 🟢 Baja + +--- + +## Roadmap de Implementación + +### Fase 1 - Fundamentos (1-2 semanas) +**Prioridad: Alta** 🔴 + +1. ✅ Configurar Vitest y estructura de tests +2. ✅ Implementar GitHub Actions (CI básico) +3. ✅ Configurar Dependabot +4. ✅ Escribir primeros tests unitarios (Core utils) + +### Fase 2 - Calidad de Código (2-3 semanas) +**Prioridad: Media** 🟡 + +5. ✅ Agregar JSDoc a APIs públicas +6. ✅ Fortalecer tipos (eliminar `any`) +7. ✅ Implementar sistema de errores mejorado +8. ✅ Refactorizar Core.ts en módulos +9. ✅ Configurar Husky y lint-staged + +### Fase 3 - Experiencia de Usuario (1-2 semanas) +**Prioridad: Media** 🟡 + +10. ✅ Crear ejemplos prácticos +11. ✅ Mejorar mensajes de error +12. ✅ Documentar compatibilidad de navegadores +13. ✅ Implementar CHANGELOG automatizado + +### Fase 4 - Optimización (1-2 semanas) +**Prioridad: Baja** 🟢 + +14. ✅ Análisis de bundle size +15. ✅ Optimizaciones de performance +16. ✅ Implementar lazy loading de Socket.io +17. ✅ Agregar source maps + +### Fase 5 - Avanzado (Opcional) +**Prioridad: Baja** 🟢 + +18. ✅ TypeDoc para documentación +19. ✅ Sistema de telemetría +20. ✅ Debug tools avanzados + +--- + +## Métricas de Éxito + +### Calidad de Código +- ✅ Cobertura de tests: >70% +- ✅ 0 vulnerabilidades críticas en dependencias +- ✅ Todos los tipos estrictos (sin `any` innecesarios) +- ✅ Complejidad ciclomática <10 por función + +### Automatización +- ✅ CI/CD completamente automatizado +- ✅ 100% de PRs pasan checks automáticos +- ✅ Releases automatizados + +### Developer Experience +- ✅ Tiempo de onboarding <30 minutos +- ✅ Ejemplos funcionales para casos comunes +- ✅ Documentación completa y actualizada + +### Performance +- ✅ Bundle size <50KB (gzipped) +- ✅ 0 memory leaks detectados +- ✅ Tiempo de conexión <2s (promedio) + +--- + +## Mejores Prácticas Generales + +### 📋 Código + +1. **Consistencia**: Mantener estilo de código uniforme +2. **SOLID**: Aplicar principios de diseño orientado a objetos +3. **DRY**: Evitar duplicación de código +4. **KISS**: Mantener soluciones simples +5. **Comentarios**: Solo cuando el código no es auto-explicativo + +### 🔒 Seguridad + +1. **Dependencias**: Actualizar regularmente +2. **Validación**: Siempre validar entradas externas +3. **Secrets**: Nunca commitear credenciales +4. **Auditorías**: Ejecutar `npm audit` antes de releases + +### 📚 Documentación + +1. **README**: Mantener actualizado con ejemplos +2. **CHANGELOG**: Documentar todos los cambios +3. **JSDoc**: APIs públicas bien documentadas +4. **Examples**: Casos de uso reales + +### 🧪 Testing + +1. **Cobertura**: Mínimo 70% en código crítico +2. **Unit Tests**: Para lógica de negocio +3. **Integration Tests**: Para flujos completos +4. **E2E Tests**: Para casos críticos de usuario + +### 🚀 Releases + +1. **SemVer**: Seguir versionado semántico +2. **Breaking Changes**: Documentar claramente +3. **Migration Guides**: Proveer para cambios mayores +4. **Release Notes**: Detalladas y útiles + +### 🔄 Git Workflow + +1. **Branches**: `main` (stable), `develop` (development), `feat/*`, `fix/*` +2. **Commits**: Conventional Commits format +3. **PRs**: Revisar antes de merge +4. **Tags**: Etiquetar releases + +--- + +## Conclusión + +Este plan de mejora está diseñado para elevar la calidad, mantenibilidad y profesionalismo del proyecto WebSerial Core. La implementación por fases permite progresar de manera ordenada, priorizando mejoras de alto impacto. + +### Beneficios Esperados + +- 🎯 **Mayor Confiabilidad**: Tests y CI/CD reducen bugs +- 📈 **Mejor Mantenibilidad**: Código modular y bien documentado +- 🚀 **Experiencia Mejorada**: Desarrolladores más productivos +- 🔒 **Mayor Seguridad**: Auditorías y validaciones constantes +- 📊 **Calidad Medible**: Métricas claras de éxito + +### Próximos Pasos Recomendados + +1. Revisar y priorizar este plan con el equipo +2. Crear issues en GitHub para cada tarea +3. Asignar recursos y timelines +4. Comenzar con Fase 1 inmediatamente +5. Revisar progreso semanalmente + +--- + +**Documento creado**: Noviembre 2025 +**Versión del proyecto analizado**: 1.1.3 +**Rama**: feat/better diff --git a/README.md b/README.md index 60d9530..7f88554 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # WebSerial Core -And easy way to connect to a serial port from a web page. +An easy way to connect to a serial port from a web page. + +[![CI](https://github.com/danidoble/webserial-core/actions/workflows/ci.yml/badge.svg)](https://github.com/danidoble/webserial-core/actions/workflows/ci.yml) +[![npm version](https://badge.fury.io/js/webserial-core.svg)](https://www.npmjs.com/package/webserial-core) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) > [!NOTE] > Since version 1.0.7 default response is an instance of Uint8Array. @@ -11,6 +15,17 @@ And easy way to connect to a serial port from a web page. > `this.getResponseAsString()` > Only choose one of them. +## Features + +- 🔌 Easy serial port connection from web browsers +- 🎯 TypeScript support with full type definitions +- 🧪 Comprehensive test suite with Vitest +- 📦 Multiple module formats (ESM, UMD) +- 🔍 Source maps for debugging +- 🌐 Socket.io integration for remote connections +- ⚡ Event-driven architecture +- 🛡️ Custom error handling with `SerialError` + ## Installation ```bash diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 0000000..08e727c --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1,23 @@ +module.exports = { + extends: ["@commitlint/config-conventional"], + rules: { + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "build", + "ci", + "chore", + "revert", + ], + ], + "subject-case": [2, "never", ["upper-case"]], + }, +}; diff --git a/dist/types/Devices.d.ts b/dist/types/Devices.d.ts index 87878c9..a1f5356 100644 --- a/dist/types/Devices.d.ts +++ b/dist/types/Devices.d.ts @@ -6,14 +6,46 @@ interface IDevice { interface IDevices { [key: string]: IDevice; } +/** + * Manages and tracks all serial devices in the application + * Provides a centralized registry for device instances + * @extends Dispatcher + */ export declare class Devices extends Dispatcher { static instance: Devices; static devices: IDevices; constructor(); static $dispatchChange(device?: Core | null): void; static typeError(type: string): void; + /** + * Registers a new device type in the registry + * @param type - The type name of the device (e.g., 'arduino', 'esp32') + * @internal + */ static registerType(type: string): void; + /** + * Adds a device to the registry + * @param device - The Core device instance to add + * @returns The index of the device in its type registry + * @throws {Error} If device with the same ID already exists + * @example + * ```typescript + * const arduino = new Arduino(); + * Devices.add(arduino); + * ``` + */ static add(device: Core): number; + /** + * Gets a specific device by type and UUID + * @param type - The device type + * @param id - The device UUID + * @returns The device instance + * @throws {Error} If the device type is not supported + * @example + * ```typescript + * const device = Devices.get('arduino', 'uuid-123'); + * ``` + */ static get(type: string, id: string): Core; static getAll(type?: string | null): IDevice | IDevices; static getList(): Core[]; diff --git a/dist/types/Devices.d.ts.map b/dist/types/Devices.d.ts.map index e493717..4070d54 100644 --- a/dist/types/Devices.d.ts.map +++ b/dist/types/Devices.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Devices.d.ts","sourceRoot":"","sources":["../../lib/Devices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,UAAU,OAAO;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qBAAa,OAAQ,SAAQ,UAAU;IACrC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAM;;WAYhB,eAAe,CAAC,MAAM,GAAE,IAAI,GAAG,IAAW,GAAG,IAAI;WAOjD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;WAO7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;WAMhC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;WAoBzB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;WAUnC,MAAM,CAAC,IAAI,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,GAAG,QAAQ;WAOtD,OAAO,IAAI,IAAI,EAAE;WAWjB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;WAO7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,GAAE,MAAU,GAAG,IAAI,GAAG,IAAI;WAOzD,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;WAWhC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;WAWjC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;WAUnC,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;WAUtC,eAAe,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;WAKlC,kBAAkB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;CAI1D"} \ No newline at end of file +{"version":3,"file":"Devices.d.ts","sourceRoot":"","sources":["../../lib/Devices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,UAAU,OAAO;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,OAAQ,SAAQ,UAAU;IACrC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAM;;WAYhB,eAAe,CAAC,MAAM,GAAE,IAAI,GAAG,IAAW,GAAG,IAAI;WAOjD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO3C;;;;OAIG;WACW,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM9C;;;;;;;;;;OAUG;WACW,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IAoBvC;;;;;;;;;;OAUG;WACW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;WAUnC,MAAM,CAAC,IAAI,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,GAAG,QAAQ;WAOtD,OAAO,IAAI,IAAI,EAAE;WAWjB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;WAO7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,GAAE,MAAU,GAAG,IAAI,GAAG,IAAI;WAOzD,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;WAWhC,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;WAWjC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;WAUnC,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;WAUtC,eAAe,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;WAKlC,kBAAkB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;CAI1D"} \ No newline at end of file diff --git a/dist/types/Dispatcher.d.ts b/dist/types/Dispatcher.d.ts index 3ca67a5..2597511 100644 --- a/dist/types/Dispatcher.d.ts +++ b/dist/types/Dispatcher.d.ts @@ -23,12 +23,75 @@ export declare class Dispatcher extends EventTarget implements IDispatcher { key: string; callback: EventListenerOrEventListenerObject; }[]; + /** + * Dispatches an event with the specified type and data + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @example + * ```typescript + * dispatcher.dispatch('connected', { port: 'COM3' }); + * ``` + */ dispatch(type: string, data?: DataType): void; + /** + * Dispatches an event asynchronously after a specified delay + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @param ms - Delay in milliseconds (default: 100) + * @example + * ```typescript + * dispatcher.dispatchAsync('timeout', { reason: 'no response' }, 500); + * ``` + */ dispatchAsync(type: string, data?: null, ms?: number): void; + /** + * Registers an event listener for the specified event type + * @param type - The event type to listen to + * @param callback - The callback function to execute when the event is triggered + * @example + * ```typescript + * dispatcher.on('connected', (event) => { + * console.log('Device connected', event.detail); + * }); + * ``` + */ on(type: string, callback: EventListenerOrEventListenerObject): void; + /** + * Removes an event listener for the specified event type + * @param type - The event type to stop listening to + * @param callback - The callback function to remove + * @example + * ```typescript + * const handler = (event) => console.log(event.detail); + * dispatcher.on('data', handler); + * dispatcher.off('data', handler); + * ``` + */ off(type: string, callback: EventListenerOrEventListenerObject): void; + /** + * Registers an available listener type for tracking + * @param type - The event type to register + * @internal + */ serialRegisterAvailableListener(type: string): void; + /** + * Gets the list of all available listeners and their state + * @returns Array of listener objects with type and listening status + * @example + * ```typescript + * const listeners = dispatcher.availableListeners; + * console.log(listeners); // [{ type: 'connected', listening: true }, ...] + * ``` + */ get availableListeners(): AvailableListeners; + /** + * Removes all event listeners except internal ones (like queue listeners) + * Resets all listener states to false + * @example + * ```typescript + * dispatcher.removeAllListeners(); + * ``` + */ removeAllListeners(): void; } export {}; diff --git a/dist/types/Dispatcher.d.ts.map b/dist/types/Dispatcher.d.ts.map index 2505527..6288c81 100644 --- a/dist/types/Dispatcher.d.ts.map +++ b/dist/types/Dispatcher.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Dispatcher.d.ts","sourceRoot":"","sources":["../../lib/Dispatcher.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,kBAAkB,GAAG,iBAAiB,EAAE,CAAC;AAE9C,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;AAE1D,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE9C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAEhD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAEjD,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpD,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAEvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,UAAW,SAAQ,WAAY,YAAW,WAAW;IAChE,aAAa,EAAE,SAAS,CAEtB;IACF,SAAS,EAAE,OAAO,CAAS;IAE3B,sBAAsB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,kCAAkC,CAAA;KAAE,EAAE,CAAM;IAEtF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,QAAe;IAQ5C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,SAAM;IAQjD,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC;IAS7D,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC;IAQ9D,+BAA+B,CAAC,IAAI,EAAE,MAAM;IAMnD,IAAI,kBAAkB,IAAI,kBAAkB,CAQ3C;IAEM,kBAAkB,IAAI,IAAI;CAalC"} \ No newline at end of file +{"version":3,"file":"Dispatcher.d.ts","sourceRoot":"","sources":["../../lib/Dispatcher.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAC9D,KAAK,kBAAkB,GAAG,iBAAiB,EAAE,CAAC;AAE9C,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;AAE1D,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE9C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAEhD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAEjD,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpD,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAEvB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,UAAW,SAAQ,WAAY,YAAW,WAAW;IAChE,aAAa,EAAE,SAAS,CAEtB;IACF,SAAS,EAAE,OAAO,CAAS;IAE3B,sBAAsB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,kCAAkC,CAAA;KAAE,EAAE,CAAM;IAE7F;;;;;;;;OAQG;IACI,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,QAAe;IAQnD;;;;;;;;;OASG;IACI,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,SAAM;IAQxD;;;;;;;;;;OAUG;IACI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC;IASpE;;;;;;;;;;OAUG;IACI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kCAAkC;IAQrE;;;;OAIG;IACI,+BAA+B,CAAC,IAAI,EAAE,MAAM;IAMnD;;;;;;;;OAQG;IACH,IAAI,kBAAkB,IAAI,kBAAkB,CAQ3C;IAED;;;;;;;OAOG;IACI,kBAAkB,IAAI,IAAI;CAalC"} \ No newline at end of file diff --git a/dist/types/SerialError.d.ts b/dist/types/SerialError.d.ts new file mode 100644 index 0000000..3fafa8c --- /dev/null +++ b/dist/types/SerialError.d.ts @@ -0,0 +1,61 @@ +/** + * Custom error codes for serial communication errors + */ +export declare enum SerialErrorCode { + CONNECTION_FAILED = "CONNECTION_FAILED", + DISCONNECTION_FAILED = "DISCONNECTION_FAILED", + WRITE_FAILED = "WRITE_FAILED", + READ_FAILED = "READ_FAILED", + TIMEOUT = "TIMEOUT", + PORT_NOT_FOUND = "PORT_NOT_FOUND", + PERMISSION_DENIED = "PERMISSION_DENIED", + DEVICE_NOT_SUPPORTED = "DEVICE_NOT_SUPPORTED", + INVALID_CONFIGURATION = "INVALID_CONFIGURATION", + SOCKET_ERROR = "SOCKET_ERROR", + UNKNOWN_ERROR = "UNKNOWN_ERROR" +} +/** + * Custom error class for WebSerial operations + * Provides structured error information with codes and context + * @extends Error + */ +export declare class SerialError extends Error { + /** + * Error code identifying the type of error + */ + readonly code: SerialErrorCode; + /** + * Additional context about the error + */ + readonly context?: Record; + /** + * Timestamp when the error occurred + */ + readonly timestamp: Date; + /** + * Creates a new SerialError + * @param message - Human-readable error message + * @param code - Error code from SerialErrorCode enum + * @param context - Additional context information + * @example + * ```typescript + * throw new SerialError( + * 'Failed to connect to device', + * SerialErrorCode.CONNECTION_FAILED, + * { port: 'COM3', baudRate: 9600 } + * ); + * ``` + */ + constructor(message: string, code?: SerialErrorCode, context?: Record); + /** + * Returns a JSON representation of the error + * @returns Serialized error object + */ + toJSON(): Record; + /** + * Returns a formatted string representation of the error + * @returns Formatted error string + */ + toString(): string; +} +//# sourceMappingURL=SerialError.d.ts.map \ No newline at end of file diff --git a/dist/types/SerialError.d.ts.map b/dist/types/SerialError.d.ts.map new file mode 100644 index 0000000..f11a8f4 --- /dev/null +++ b/dist/types/SerialError.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SerialError.d.ts","sourceRoot":"","sources":["../../lib/SerialError.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,eAAe;IACzB,iBAAiB,sBAAsB;IACvC,oBAAoB,yBAAyB;IAC7C,YAAY,iBAAiB;IAC7B,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,cAAc,mBAAmB;IACjC,iBAAiB,sBAAsB;IACvC,oBAAoB,yBAAyB;IAC7C,qBAAqB,0BAA0B;IAC/C,YAAY,iBAAiB;IAC7B,aAAa,kBAAkB;CAChC;AAED;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;OAEG;IACH,SAAgB,IAAI,EAAE,eAAe,CAAC;IAEtC;;OAEG;IACH,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAElD;;OAEG;IACH,SAAgB,SAAS,EAAE,IAAI,CAAC;IAEhC;;;;;;;;;;;;;OAaG;gBAED,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,eAA+C,EACrD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAcnC;;;OAGG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAWjC;;;OAGG;IACH,QAAQ,IAAI,MAAM;CAInB"} \ No newline at end of file diff --git a/dist/types/Socket.d.ts b/dist/types/Socket.d.ts index c604338..6b78629 100644 --- a/dist/types/Socket.d.ts +++ b/dist/types/Socket.d.ts @@ -1,18 +1,24 @@ import { ManagerOptions, SocketOptions } from "socket.io-client"; +interface SocketResponseData { + name: string; + uuid: string; + deviceNumber: number; + [key: string]: unknown; +} declare class MySocket { #private; + constructor(); set uri(uri: string); get uri(): string; set options(options: Partial); get options(): Partial; - constructor(); disconnect(): void; prepare(): void; connectDevice(config: object): void; disconnectDevice(config: object): void; disconnectAllDevices(): void; write(data: object): void; - onResponse(data: any): void; + onResponse(data: SocketResponseData): void; } export declare const Socket: MySocket; export {}; diff --git a/dist/types/Socket.d.ts.map b/dist/types/Socket.d.ts.map index 8dd9d16..d1c532a 100644 --- a/dist/types/Socket.d.ts.map +++ b/dist/types/Socket.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Socket.d.ts","sourceRoot":"","sources":["../../lib/Socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAQrE,cAAM,QAAQ;;IAUZ,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,EAOlB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,EAK3D;IAED,IAAI,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAErD;;IAMD,UAAU;IAUV,OAAO;IASP,aAAa,CAAC,MAAM,EAAE,MAAM;IAI5B,gBAAgB,CAAC,MAAM,EAAE,MAAM;IAI/B,oBAAoB;IAIpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,UAAU,CAAC,IAAI,EAAE,GAAG;CAUrB;AAED,eAAO,MAAM,MAAM,UAAiB,CAAC"} \ No newline at end of file +{"version":3,"file":"Socket.d.ts","sourceRoot":"","sources":["../../lib/Socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,cAAc,EAAE,aAAa,EAA4B,MAAM,kBAAkB,CAAC;AAI/F,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAMD,cAAM,QAAQ;;;IAgBZ,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,EAOlB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,EAK3D;IAED,IAAI,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC,CAErD;IAED,UAAU;IAUV,OAAO;IASP,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOnC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOtC,oBAAoB,IAAI,IAAI;IAO5B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOzB,UAAU,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;CAU3C;AAED,eAAO,MAAM,MAAM,UAAiB,CAAC"} \ No newline at end of file diff --git a/dist/types/main.d.ts b/dist/types/main.d.ts index ce75b78..1307c29 100644 --- a/dist/types/main.d.ts +++ b/dist/types/main.d.ts @@ -11,4 +11,5 @@ export { Core } from "./Core"; export { Devices } from "./Devices"; export { Dispatcher } from "./Dispatcher"; export { Socket } from "./Socket"; +export { SerialError, SerialErrorCode } from "./SerialError"; //# sourceMappingURL=main.d.ts.map \ No newline at end of file diff --git a/dist/types/main.d.ts.map b/dist/types/main.d.ts.map index 0c0dddd..5422a8b 100644 --- a/dist/types/main.d.ts.map +++ b/dist/types/main.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../lib/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"} \ No newline at end of file +{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../lib/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"} \ No newline at end of file diff --git a/dist/webserial-core.js b/dist/webserial-core.js index d58548d..8ebce6e 100644 --- a/dist/webserial-core.js +++ b/dist/webserial-core.js @@ -1,4 +1,4 @@ -import { io as b } from "socket.io-client"; +import { io as w } from "socket.io-client"; class g extends CustomEvent { constructor(e, t) { super(e, t); @@ -10,31 +10,94 @@ class m extends EventTarget { }; __debug__ = !1; __listenersCallbacks__ = []; + /** + * Dispatches an event with the specified type and data + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @example + * ```typescript + * dispatcher.dispatch('connected', { port: 'COM3' }); + * ``` + */ dispatch(e, t = null) { - const i = new g(e, { detail: t }); - this.dispatchEvent(i), this.__debug__ && this.dispatchEvent(new g("debug", { detail: { type: e, data: t } })); - } - dispatchAsync(e, t = null, i = 100) { - const n = this; + const n = new g(e, { detail: t }); + this.dispatchEvent(n), this.__debug__ && this.dispatchEvent(new g("debug", { detail: { type: e, data: t } })); + } + /** + * Dispatches an event asynchronously after a specified delay + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @param ms - Delay in milliseconds (default: 100) + * @example + * ```typescript + * dispatcher.dispatchAsync('timeout', { reason: 'no response' }, 500); + * ``` + */ + dispatchAsync(e, t = null, n = 100) { + const i = this; setTimeout(() => { - n.dispatch(e, t); - }, i); - } + i.dispatch(e, t); + }, n); + } + /** + * Registers an event listener for the specified event type + * @param type - The event type to listen to + * @param callback - The callback function to execute when the event is triggered + * @example + * ```typescript + * dispatcher.on('connected', (event) => { + * console.log('Device connected', event.detail); + * }); + * ``` + */ on(e, t) { typeof this.__listeners__[e] < "u" && !this.__listeners__[e] && (this.__listeners__[e] = !0), this.__listenersCallbacks__.push({ key: e, callback: t }), this.addEventListener(e, t); } + /** + * Removes an event listener for the specified event type + * @param type - The event type to stop listening to + * @param callback - The callback function to remove + * @example + * ```typescript + * const handler = (event) => console.log(event.detail); + * dispatcher.on('data', handler); + * dispatcher.off('data', handler); + * ``` + */ off(e, t) { - this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((i) => !(i.key === e && i.callback === t)), this.removeEventListener(e, t); + this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((n) => !(n.key === e && n.callback === t)), this.removeEventListener(e, t); } + /** + * Registers an available listener type for tracking + * @param type - The event type to register + * @internal + */ serialRegisterAvailableListener(e) { this.__listeners__[e] || (this.__listeners__[e] = !1); } + /** + * Gets the list of all available listeners and their state + * @returns Array of listener objects with type and listening status + * @example + * ```typescript + * const listeners = dispatcher.availableListeners; + * console.log(listeners); // [{ type: 'connected', listening: true }, ...] + * ``` + */ get availableListeners() { return Object.keys(this.__listeners__).sort().map((t) => ({ type: t, listening: this.__listeners__[t] })); } + /** + * Removes all event listeners except internal ones (like queue listeners) + * Resets all listener states to false + * @example + * ```typescript + * dispatcher.removeAllListeners(); + * ``` + */ removeAllListeners() { for (const e of this.__listenersCallbacks__) ["internal:queue"].includes(e.key) || (this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((t) => !(t.key === e.key && t.callback === e.callback)), this.removeEventListener(e.key, e.callback)); @@ -57,17 +120,44 @@ class s extends m { const t = new Error(); throw t.message = `Type ${e} is not supported`, t.name = "DeviceTypeError", t; } + /** + * Registers a new device type in the registry + * @param type - The type name of the device (e.g., 'arduino', 'esp32') + * @internal + */ static registerType(e) { typeof s.devices[e] > "u" && (s.devices = { ...s.devices, [e]: {} }); } + /** + * Adds a device to the registry + * @param device - The Core device instance to add + * @returns The index of the device in its type registry + * @throws {Error} If device with the same ID already exists + * @example + * ```typescript + * const arduino = new Arduino(); + * Devices.add(arduino); + * ``` + */ static add(e) { const t = e.typeDevice; typeof s.devices[t] > "u" && s.registerType(t); - const i = e.uuid; - if (typeof s.devices[t] > "u" && s.typeError(t), s.devices[t][i]) - throw new Error(`Device with id ${i} already exists`); - return s.devices[t][i] = e, s.$dispatchChange(e), Object.keys(s.devices[t]).indexOf(i); - } + const n = e.uuid; + if (typeof s.devices[t] > "u" && s.typeError(t), s.devices[t][n]) + throw new Error(`Device with id ${n} already exists`); + return s.devices[t][n] = e, s.$dispatchChange(e), Object.keys(s.devices[t]).indexOf(n); + } + /** + * Gets a specific device by type and UUID + * @param type - The device type + * @param id - The device UUID + * @returns The device instance + * @throws {Error} If the device type is not supported + * @example + * ```typescript + * const device = Devices.get('arduino', 'uuid-123'); + * ``` + */ static get(e, t) { return typeof s.devices[e] > "u" && s.registerType(e), typeof s.devices[e] > "u" && s.typeError(e), s.devices[e][t]; } @@ -78,10 +168,10 @@ class s extends m { return Object.values(s.devices).map((t) => Object.values(t)).flat(); } static getByNumber(e, t) { - return typeof s.devices[e] > "u" && s.typeError(e), Object.values(s.devices[e]).find((n) => n.deviceNumber === t) ?? null; + return typeof s.devices[e] > "u" && s.typeError(e), Object.values(s.devices[e]).find((i) => i.deviceNumber === t) ?? null; } static getCustom(e, t = 1) { - return typeof s.devices[e] > "u" && s.typeError(e), Object.values(s.devices[e]).find((n) => n.deviceNumber === t) ?? null; + return typeof s.devices[e] > "u" && s.typeError(e), Object.values(s.devices[e]).find((i) => i.deviceNumber === t) ?? null; } static async connectToAll() { const e = s.getList(); @@ -117,19 +207,24 @@ class s extends m { } } s.instance || (s.instance = new s()); -function y(c = 100) { +function y(a = 100) { return new Promise( - (e) => setTimeout(() => e(), c) + (e) => setTimeout(() => e(), a) ); } -class w { +class C { #t = "http://localhost:3000"; - #i = { + #n = { transports: ["websocket"] }; - #e; + #e = null; #r = !1; - #a = {}; + #a; + constructor() { + this.#a = { + onResponse: this.onResponse.bind(this) + }; + } set uri(e) { const t = new URL(e); if (!["http:", "https:", "ws:", "wss:"].includes(t.protocol)) @@ -142,30 +237,35 @@ class w { set options(e) { if (typeof e != "object") throw new Error("Options must be an object"); - this.#i = e; + this.#n = e; } get options() { - return this.#i; - } - constructor() { - this.#a.onResponse = this.onResponse.bind(this); + return this.#n; } disconnect() { this.#e && (this.#e.off("response", this.#a.onResponse), this.#e.disconnect(), this.#e = null), this.#r = !1; } prepare() { - this.#r || (this.#e = b(this.#t, this.#i), this.#r = !0, this.#e.on("response", this.#a.onResponse)); + this.#r || (this.#e = w(this.#t, this.#n), this.#r = !0, this.#e.on("response", this.#a.onResponse)); } connectDevice(e) { + if (!this.#e) + throw new Error("Socket not connected. Call prepare() first."); this.#e.emit("connectDevice", { config: e }); } disconnectDevice(e) { + if (!this.#e) + throw new Error("Socket not connected. Call prepare() first."); this.#e.emit("disconnectDevice", { config: e }); } disconnectAllDevices() { + if (!this.#e) + throw new Error("Socket not connected. Call prepare() first."); this.#e.emit("disconnectAll"); } write(e) { + if (!this.#e) + throw new Error("Socket not connected. Call prepare() first."); this.#e.emit("cmd", e); } onResponse(e) { @@ -173,7 +273,7 @@ class w { t || (t = s.getByNumber(e.name, e.deviceNumber)), t && t.socketResponse(e); } } -const u = new w(), p = { +const u = new C(), p = { baudRate: 9600, dataBits: 8, stopBits: 1, @@ -259,9 +359,9 @@ class v extends m { constructor({ filters: e = null, config_port: t = p, - no_device: i = 1, - device_listen_on_channel: n = 1, - bypassSerialBytesConnection: a = !1, + no_device: n = 1, + device_listen_on_channel: i = 1, + bypassSerialBytesConnection: o = !1, socket: r = !1 } = { filters: null, @@ -273,7 +373,7 @@ class v extends m { }) { if (super(), !("serial" in navigator)) throw new Error("Web Serial not supported"); - e && (this.serialFilters = e), t && (this.serialConfigPort = t), a && (this.__internal__.bypassSerialBytesConnection = a), i && this.#w(i), n && ["number", "string"].includes(typeof n) && (this.listenOnChannel = n), this.__internal__.serial.socket = r, this.#g(), this.#y(); + e && (this.serialFilters = e), t && (this.serialConfigPort = t), o && (this.__internal__.bypassSerialBytesConnection = o), n && this.#w(n), i && ["number", "string"].includes(typeof i) && (this.listenOnChannel = i), this.__internal__.serial.socket = r, this.#g(), this.#y(); } set listenOnChannel(e) { if (typeof e == "string" && (e = parseInt(e)), isNaN(e) || e < 1 || e > 255) @@ -307,14 +407,14 @@ class v extends m { this.__internal__.serial.useRTSCTS = e; } get isConnected() { - const e = this.__internal__.serial.connected, t = this.#i(this.__internal__.serial.port); + const e = this.__internal__.serial.connected, t = this.#n(this.__internal__.serial.port); return e && !t && this.#e({ error: "Port is closed, not readable or writable." }), this.__internal__.serial.connected = t, this.__internal__.serial.connected; } get isConnecting() { return this.__internal__.serial.connecting; } get isDisconnected() { - const e = this.__internal__.serial.connected, t = this.#i(this.__internal__.serial.port); + const e = this.__internal__.serial.connected, t = this.#n(this.__internal__.serial.port); return !e && t && (this.dispatch("serial:connected"), this.#s(!1), s.$dispatchChange(this)), this.__internal__.serial.connected = t, !this.__internal__.serial.connected; } get deviceNumber() { @@ -488,7 +588,7 @@ class v extends m { } }; } - #i(e) { + #n(e) { return this.useSocket ? this.__internal__.serial.connected : !!(e && e.readable && e.writable); } async timeout(e, t) { @@ -510,18 +610,18 @@ class v extends m { socketResponse(e) { const t = this.__internal__.serial.connected; if (e.type === "disconnect" || e.type === "error" && e.data === "DISCONNECTED" ? this.__internal__.serial.connected = !1 : e.type === "success" && (this.__internal__.serial.connected = !0), s.$dispatchChange(this), !t && this.__internal__.serial.connected && (this.dispatch("serial:connected"), this.#s(!1)), e.type === "success") - this.#n(new Uint8Array(e.data)); + this.#i(new Uint8Array(e.data)); else if (e.type === "error") { - const i = new Error("The port is closed or is not readable/writable"); - this.serialErrors(i); + const n = new Error("The port is closed or is not readable/writable"); + this.serialErrors(n); } else e.type === "timeout" && this.timeout(e.data.bytes ?? [], this.lastAction || "unknown"); this.__internal__.serial.last_action = null; } async connect() { return this.isConnected ? !0 : (this.__internal__.serial.aux_connecting = "idle", new Promise((e, t) => { this.#t || (this.#t = this.#r.bind(this)), this.on("internal:connecting", this.#t); - const i = setInterval(() => { - this.__internal__.serial.aux_connecting === "finished" ? (clearInterval(i), this.__internal__.serial.aux_connecting = "idle", this.#t !== null && this.off("internal:connecting", this.#t), this.isConnected ? e(!0) : t(`${this.typeDevice} device ${this.deviceNumber} not connected`)) : this.__internal__.serial.aux_connecting === "connecting" && (this.__internal__.serial.aux_connecting = "idle", this.dispatch("internal:connecting", { active: !0 }), this.dispatch("serial:connecting", { active: !0 })); + const n = setInterval(() => { + this.__internal__.serial.aux_connecting === "finished" ? (clearInterval(n), this.__internal__.serial.aux_connecting = "idle", this.#t !== null && this.off("internal:connecting", this.#t), this.isConnected ? e(!0) : t(`${this.typeDevice} device ${this.deviceNumber} not connected`)) : this.__internal__.serial.aux_connecting === "connecting" && (this.__internal__.serial.aux_connecting = "idle", this.dispatch("internal:connecting", { active: !0 }), this.dispatch("serial:connecting", { active: !0 })); }, 100); this.serialConnect(); })); @@ -532,7 +632,7 @@ class v extends m { u.disconnectDevice(this.configDeviceSocket); else { const e = this.__internal__.serial.reader, t = this.__internal__.serial.output_stream; - e && (await e.cancel().catch((n) => this.serialErrors(n)), await this.__internal__.serial.input_done), t && (await t.getWriter().close(), await this.__internal__.serial.output_done), this.__internal__.serial.connected && this.__internal__.serial && this.__internal__.serial.port && await this.__internal__.serial.port.close(); + e && (await e.cancel().catch((i) => this.serialErrors(i)), await this.__internal__.serial.input_done), t && (await t.getWriter().close(), await this.__internal__.serial.output_done), this.__internal__.serial.connected && this.__internal__.serial && this.__internal__.serial.port && await this.__internal__.serial.port.close(); } } catch (e) { this.serialErrors(e); @@ -554,39 +654,39 @@ class v extends m { const t = this.__internal__.serial.port; if (!t || t && (!t.readable || !t.writable)) throw this.#e({ error: "Port is closed, not readable or writable." }), new Error("The port is closed or is not readable/writable"); - const i = this.validateBytes(e); + const n = this.validateBytes(e); if (this.useRTSCTS && await this.#l(t, 5e3), t.writable === null) return; - const n = t.writable.getWriter(); - await n.write(i), n.releaseLock(); + const i = t.writable.getWriter(); + await i.write(n), i.releaseLock(); } async #l(e, t = 5e3) { - const i = Date.now(); + const n = Date.now(); for (; ; ) { - if (Date.now() - i > t) + if (Date.now() - n > t) throw new Error("Timeout waiting for clearToSend signal"); - const { clearToSend: n } = await e.getSignals(); - if (n) return; + const { clearToSend: i } = await e.getSignals(); + if (i) return; await y(100); } } - #n(e = new Uint8Array([]), t = !1) { + #i(e = new Uint8Array([]), t = !1) { if (e && e.length > 0) { - const i = this.__internal__.serial.connected; - if (this.__internal__.serial.connected = this.#i(this.__internal__.serial.port), s.$dispatchChange(this), !i && this.__internal__.serial.connected && (this.dispatch("serial:connected"), this.#s(!1)), this.__internal__.interval.reconnection && (clearInterval(this.__internal__.interval.reconnection), this.__internal__.interval.reconnection = 0), this.__internal__.timeout.until_response && (clearTimeout(this.__internal__.timeout.until_response), this.__internal__.timeout.until_response = 0), this.__internal__.serial.response.as === "hex") + const n = this.__internal__.serial.connected; + if (this.__internal__.serial.connected = this.#n(this.__internal__.serial.port), s.$dispatchChange(this), !n && this.__internal__.serial.connected && (this.dispatch("serial:connected"), this.#s(!1)), this.__internal__.interval.reconnection && (clearInterval(this.__internal__.interval.reconnection), this.__internal__.interval.reconnection = 0), this.__internal__.timeout.until_response && (clearTimeout(this.__internal__.timeout.until_response), this.__internal__.timeout.until_response = 0), this.__internal__.serial.response.as === "hex") t ? this.serialCorruptMessage(this.parseUint8ToHex(e)) : this.serialMessage(this.parseUint8ToHex(e)); else if (this.__internal__.serial.response.as === "uint8") t ? this.serialCorruptMessage(e) : this.serialMessage(e); else if (this.__internal__.serial.response.as === "string") { - const n = this.parseUint8ArrayToString(e); + const i = this.parseUint8ArrayToString(e); if (this.__internal__.serial.response.limiter !== null) { - const a = n.split(this.__internal__.serial.response.limiter); - for (const r in a) - a[r] && (t ? this.serialCorruptMessage(a[r]) : this.serialMessage(a[r])); + const o = i.split(this.__internal__.serial.response.limiter); + for (const r in o) + o[r] && (t ? this.serialCorruptMessage(o[r]) : this.serialMessage(o[r])); } else - t ? this.serialCorruptMessage(n) : this.serialMessage(n); + t ? this.serialCorruptMessage(i) : this.serialMessage(i); } else { - const n = this.stringToArrayBuffer(this.parseUint8ArrayToString(e)); - t ? this.serialCorruptMessage(n) : this.serialMessage(n); + const i = this.stringToArrayBuffer(this.parseUint8ArrayToString(e)); + t ? this.serialCorruptMessage(i) : this.serialMessage(i); } } if (this.__internal__.serial.queue.length === 0) { @@ -609,16 +709,16 @@ class v extends m { } async #_() { const e = this.serialFilters, t = await navigator.serial.getPorts({ filters: e }); - return e.length === 0 ? t : t.filter((n) => { - const a = n.getInfo(); - return e.some((r) => a.usbProductId === r.usbProductId && a.usbVendorId === r.usbVendorId); - }).filter((n) => !this.#i(n)); + return e.length === 0 ? t : t.filter((i) => { + const o = i.getInfo(); + return e.some((r) => o.usbProductId === r.usbProductId && o.usbVendorId === r.usbVendorId); + }).filter((i) => !this.#n(i)); } async serialPortsSaved(e) { const t = this.serialFilters; if (this.__internal__.aux_port_connector < e.length) { - const i = this.__internal__.aux_port_connector; - this.__internal__.serial.port = e[i]; + const n = this.__internal__.aux_port_connector; + this.__internal__.serial.port = e[n]; } else this.__internal__.aux_port_connector = 0, this.__internal__.serial.port = await navigator.serial.requestPort({ filters: t @@ -675,13 +775,13 @@ class v extends m { } #c(e) { if (e) { - const t = this.__internal__.serial.response.buffer, i = new Uint8Array(t.length + e.byteLength); - i.set(t, 0), i.set(new Uint8Array(e), t.length), this.__internal__.serial.response.buffer = i; + const t = this.__internal__.serial.response.buffer, n = new Uint8Array(t.length + e.byteLength); + n.set(t, 0), n.set(new Uint8Array(e), t.length), this.__internal__.serial.response.buffer = n; } } async #h() { this.__internal__.serial.time_until_send_bytes && (clearTimeout(this.__internal__.serial.time_until_send_bytes), this.__internal__.serial.time_until_send_bytes = 0), this.__internal__.serial.time_until_send_bytes = setTimeout(() => { - this.__internal__.serial.response.buffer && this.#n(this.__internal__.serial.response.buffer), this.__internal__.serial.response.buffer = new Uint8Array(0); + this.__internal__.serial.response.buffer && this.#i(this.__internal__.serial.response.buffer), this.__internal__.serial.response.buffer = new Uint8Array(0); }, this.__internal__.serial.free_timeout_ms || 50); } async #u() { @@ -689,11 +789,11 @@ class v extends m { let t = this.__internal__.serial.response.buffer; if (this.__internal__.serial.time_until_send_bytes && (clearTimeout(this.__internal__.serial.time_until_send_bytes), this.__internal__.serial.time_until_send_bytes = 0), !(e === null || !t || t.length === 0)) { for (; t.length >= e; ) { - const i = t.slice(0, e); - this.#n(i), t = t.slice(e); + const n = t.slice(0, e); + this.#i(n), t = t.slice(e); } this.__internal__.serial.response.buffer = t, t.length > 0 && (this.__internal__.serial.time_until_send_bytes = setTimeout(() => { - this.#n(this.__internal__.serial.response.buffer, !0); + this.#i(this.__internal__.serial.response.buffer, !0); }, this.__internal__.serial.free_timeout_ms || 50)); } } @@ -701,54 +801,54 @@ class v extends m { const { limiter: e, prefixLimiter: t = !1, - sufixLimiter: i = !0 + sufixLimiter: n = !0 } = this.__internal__.serial.response; if (!e) throw new Error("No limiter defined for delimited serial response"); - const n = this.__internal__.serial.response.buffer; - if (!e || !n || n.length === 0) return; + const i = this.__internal__.serial.response.buffer; + if (!e || !i || i.length === 0) return; this.__internal__.serial.time_until_send_bytes && (clearTimeout(this.__internal__.serial.time_until_send_bytes), this.__internal__.serial.time_until_send_bytes = 0); - let r = new TextDecoder().decode(n); + let r = new TextDecoder().decode(i); const h = []; if (typeof e == "string") { - let o; - if (t && i) - o = new RegExp(`${e}([^${e}]+)${e}`, "g"); + let l; + if (t && n) + l = new RegExp(`${e}([^${e}]+)${e}`, "g"); else if (t) - o = new RegExp(`${e}([^${e}]*)`, "g"); - else if (i) - o = new RegExp(`([^${e}]+)${e}`, "g"); + l = new RegExp(`${e}([^${e}]*)`, "g"); + else if (n) + l = new RegExp(`([^${e}]+)${e}`, "g"); else return; - let _, l = 0; - for (; (_ = o.exec(r)) !== null; ) - h.push(new TextEncoder().encode(_[1])), l = o.lastIndex; - r = r.slice(l); + let c, _ = 0; + for (; (c = l.exec(r)) !== null; ) + h.push(new TextEncoder().encode(c[1])), _ = l.lastIndex; + r = r.slice(_); } else if (e instanceof RegExp) { - let o, _ = 0; - if (t && i) { - const l = new RegExp(`${e.source}(.*?)${e.source}`, "g"); - for (; (o = l.exec(r)) !== null; ) - h.push(new TextEncoder().encode(o[1])), _ = l.lastIndex; - } else if (i) - for (; (o = e.exec(r)) !== null; ) { - const l = o.index, d = r.slice(_, l); - h.push(new TextEncoder().encode(d)), _ = e.lastIndex; + let l, c = 0; + if (t && n) { + const _ = new RegExp(`${e.source}(.*?)${e.source}`, "g"); + for (; (l = _.exec(r)) !== null; ) + h.push(new TextEncoder().encode(l[1])), c = _.lastIndex; + } else if (n) + for (; (l = e.exec(r)) !== null; ) { + const _ = l.index, d = r.slice(c, _); + h.push(new TextEncoder().encode(d)), c = e.lastIndex; } else if (t) { - const l = r.split(e); - l.shift(); - for (const d of l) + const _ = r.split(e); + _.shift(); + for (const d of _) h.push(new TextEncoder().encode(d)); r = ""; } - r = r.slice(_); + r = r.slice(c); } - for (const o of h) - this.#n(o); + for (const l of h) + this.#i(l); const f = new TextEncoder().encode(r); this.__internal__.serial.response.buffer = f, f.length > 0 && (this.__internal__.serial.time_until_send_bytes = setTimeout(() => { - this.#n(this.__internal__.serial.response.buffer, !0), this.__internal__.serial.response.buffer = new Uint8Array(0); + this.#i(this.__internal__.serial.response.buffer, !0), this.__internal__.serial.response.buffer = new Uint8Array(0); }, this.__internal__.serial.free_timeout_ms ?? 50)); } async #p() { @@ -758,12 +858,12 @@ class v extends m { this.__internal__.serial.reader = t; try { for (; this.__internal__.serial.keep_reading; ) { - const { value: i, done: n } = await t.read(); - if (n) break; - this.#c(i), this.__internal__.serial.response.delimited ? await this.#d() : this.__internal__.serial.response.length === null ? await this.#h() : await this.#u(); + const { value: n, done: i } = await t.read(); + if (i) break; + this.#c(n), this.__internal__.serial.response.delimited ? await this.#d() : this.__internal__.serial.response.length === null ? await this.#h() : await this.#u(); } - } catch (i) { - this.serialErrors(i); + } catch (n) { + this.serialErrors(n); } finally { t.releaseLock(), this.__internal__.serial.keep_reading = !0, this.__internal__.serial.port && await this.__internal__.serial.port.close(); } @@ -785,26 +885,26 @@ class v extends m { if (e.length > 0) await this.serialPortsSaved(e); else { - const n = this.serialFilters; + const i = this.serialFilters; this.__internal__.serial.port = await navigator.serial.requestPort({ - filters: n + filters: i }); } const t = this.__internal__.serial.port; if (!t) throw new Error("No port selected by the user"); await t.open(this.serialConfigPort); - const i = this; - t.onconnect = (n) => { - i.dispatch("serial:connected", n), i.#s(!1), s.$dispatchChange(this), i.__internal__.serial.queue.length > 0 ? i.dispatch("internal:queue", {}) : i.__internal__.serial.running_queue = !1; + const n = this; + t.onconnect = (i) => { + n.dispatch("serial:connected", i), n.#s(!1), s.$dispatchChange(this), n.__internal__.serial.queue.length > 0 ? n.dispatch("internal:queue", {}) : n.__internal__.serial.running_queue = !1; }, t.ondisconnect = async () => { - await i.disconnect(); + await n.disconnect(); }, await y(this.__internal__.serial.delay_first_connection), this.__internal__.timeout.until_response = setTimeout(async () => { - await i.timeout(i.__internal__.serial.bytes_connection ?? [], "connection:start"); + await n.timeout(n.__internal__.serial.bytes_connection ?? [], "connection:start"); }, this.__internal__.time.response_connection), this.__internal__.serial.last_action = "connect", await this.#o(this.__internal__.serial.bytes_connection ?? []), this.dispatch("serial:sent", { action: "connect", bytes: this.__internal__.serial.bytes_connection - }), this.__internal__.auto_response && this.#n(this.__internal__.serial.auto_response), await this.#p(); + }), this.__internal__.auto_response && this.#i(this.__internal__.serial.auto_response), await this.#p(); } } catch (e) { this.#s(!1), this.serialErrors(e); @@ -827,8 +927,8 @@ class v extends m { } add0x(e) { const t = []; - return e.forEach((i, n) => { - t[n] = "0x" + i; + return e.forEach((n, i) => { + t[i] = "0x" + n; }), t; } bytesToHex(e) { @@ -869,7 +969,7 @@ class v extends m { }); } async #b() { - if (!this.#i(this.__internal__.serial.port)) { + if (!this.#n(this.__internal__.serial.port)) { this.#e({ error: "Port is closed, not readable or writable." }), await this.serialConnect(); return; } @@ -887,16 +987,16 @@ class v extends m { action: e.action, bytes: e.bytes }), this.__internal__.auto_response) { - let n = new Uint8Array(0); + let i = new Uint8Array(0); try { - n = this.validateBytes(this.__internal__.serial.auto_response); - } catch (a) { - this.serialErrors(a); + i = this.validateBytes(this.__internal__.serial.auto_response); + } catch (o) { + this.serialErrors(o); } - this.#n(n); + this.#i(i); } - const i = [...this.__internal__.serial.queue]; - this.__internal__.serial.queue = i.splice(1), this.__internal__.serial.queue.length > 0 && (this.__internal__.serial.running_queue = !0); + const n = [...this.__internal__.serial.queue]; + this.__internal__.serial.queue = n.splice(1), this.__internal__.serial.queue.length > 0 && (this.__internal__.serial.running_queue = !0); } validateBytes(e) { let t = new Uint8Array(0); @@ -913,13 +1013,13 @@ class v extends m { return t; } async appendToQueue(e, t) { - const i = this.validateBytes(e); + const n = this.validateBytes(e); if (["connect", "connection:start"].includes(t)) { if (this.__internal__.serial.connected) return; await this.serialConnect(); return; } - this.__internal__.serial.queue.push({ bytes: i, action: t }), this.dispatch("internal:queue", {}); + this.__internal__.serial.queue.push({ bytes: n, action: t }), this.dispatch("internal:queue", {}); } #w(e = 1) { this.__internal__.device_number = e, !this.__internal__.bypassSerialBytesConnection && (this.__internal__.serial.bytes_connection = this.serialSetConnectionConstant(e)); @@ -947,8 +1047,8 @@ class v extends m { } sumHex(e) { let t = 0; - return e.forEach((i) => { - t += parseInt(i, 16); + return e.forEach((n) => { + t += parseInt(n, 16); }), t.toString(16); } toString() { @@ -982,13 +1082,13 @@ class v extends m { } parseStringToTextEncoder(e = "", t = ` `) { - const i = new TextEncoder(); - return e += t, i.encode(e); + const n = new TextEncoder(); + return e += t, n.encode(e); } parseStringToBytes(e = "", t = ` `) { - const i = this.parseStringToTextEncoder(e, t); - return Array.from(i).map((n) => n.toString(16)); + const n = this.parseStringToTextEncoder(e, t); + return Array.from(n).map((i) => i.toString(16)); } parseUint8ToHex(e) { return Array.from(e).map((t) => t.toString(16).padStart(2, "0").toLowerCase()); @@ -998,29 +1098,29 @@ class v extends m { } stringArrayToUint8Array(e) { const t = []; - return typeof e == "string" ? this.parseStringToTextEncoder(e).buffer : (e.forEach((i) => { - const n = i.replace("0x", ""); - t.push(parseInt(n, 16)); + return typeof e == "string" ? this.parseStringToTextEncoder(e).buffer : (e.forEach((n) => { + const i = n.replace("0x", ""); + t.push(parseInt(i, 16)); }), new Uint8Array(t)); } parseUint8ArrayToString(e) { let t = new Uint8Array(0); e instanceof Uint8Array ? t = e : t = this.stringArrayToUint8Array(e), e = this.parseUint8ToHex(t); - const i = e.map((n) => parseInt(n, 16)); - return this.__internal__.serial.response.replacer ? String.fromCharCode(...i).replace(this.__internal__.serial.response.replacer, "") : String.fromCharCode(...i); + const n = e.map((i) => parseInt(i, 16)); + return this.__internal__.serial.response.replacer ? String.fromCharCode(...n).replace(this.__internal__.serial.response.replacer, "") : String.fromCharCode(...n); } hexToAscii(e) { const t = e.toString(); - let i = ""; - for (let n = 0; n < t.length; n += 2) - i += String.fromCharCode(parseInt(t.substring(n, 2), 16)); - return i; + let n = ""; + for (let i = 0; i < t.length; i += 2) + n += String.fromCharCode(parseInt(t.substring(i, 2), 16)); + return n; } asciiToHex(e) { const t = []; - for (let i = 0, n = e.length; i < n; i++) { - const a = Number(e.charCodeAt(i)).toString(16); - t.push(a); + for (let n = 0, i = e.length; n < i; n++) { + const o = Number(e.charCodeAt(n)).toString(16); + t.push(o); } return t.join(""); } @@ -1028,9 +1128,66 @@ class v extends m { return this.isConnected; } } +var E = /* @__PURE__ */ ((a) => (a.CONNECTION_FAILED = "CONNECTION_FAILED", a.DISCONNECTION_FAILED = "DISCONNECTION_FAILED", a.WRITE_FAILED = "WRITE_FAILED", a.READ_FAILED = "READ_FAILED", a.TIMEOUT = "TIMEOUT", a.PORT_NOT_FOUND = "PORT_NOT_FOUND", a.PERMISSION_DENIED = "PERMISSION_DENIED", a.DEVICE_NOT_SUPPORTED = "DEVICE_NOT_SUPPORTED", a.INVALID_CONFIGURATION = "INVALID_CONFIGURATION", a.SOCKET_ERROR = "SOCKET_ERROR", a.UNKNOWN_ERROR = "UNKNOWN_ERROR", a))(E || {}); +class b extends Error { + /** + * Error code identifying the type of error + */ + code; + /** + * Additional context about the error + */ + context; + /** + * Timestamp when the error occurred + */ + timestamp; + /** + * Creates a new SerialError + * @param message - Human-readable error message + * @param code - Error code from SerialErrorCode enum + * @param context - Additional context information + * @example + * ```typescript + * throw new SerialError( + * 'Failed to connect to device', + * SerialErrorCode.CONNECTION_FAILED, + * { port: 'COM3', baudRate: 9600 } + * ); + * ``` + */ + constructor(e, t = "UNKNOWN_ERROR", n) { + super(e), this.name = "SerialError", this.code = t, this.context = n, this.timestamp = /* @__PURE__ */ new Date(), Error.captureStackTrace && Error.captureStackTrace(this, b); + } + /** + * Returns a JSON representation of the error + * @returns Serialized error object + */ + toJSON() { + return { + name: this.name, + message: this.message, + code: this.code, + context: this.context, + timestamp: this.timestamp.toISOString(), + stack: this.stack + }; + } + /** + * Returns a formatted string representation of the error + * @returns Formatted error string + */ + toString() { + const e = this.context ? ` | Context: ${JSON.stringify(this.context)}` : ""; + return `${this.name} [${this.code}]: ${this.message}${e}`; + } +} export { v as Core, s as Devices, m as Dispatcher, + b as SerialError, + E as SerialErrorCode, u as Socket }; +//# sourceMappingURL=webserial-core.js.map diff --git a/dist/webserial-core.js.map b/dist/webserial-core.js.map new file mode 100644 index 0000000..09d0c01 --- /dev/null +++ b/dist/webserial-core.js.map @@ -0,0 +1 @@ +{"version":3,"file":"webserial-core.js","sources":["../lib/SerialEvent.ts","../lib/Dispatcher.ts","../lib/Devices.ts","../lib/utils.ts","../lib/Socket.ts","../lib/Core.ts","../lib/SerialError.ts"],"sourcesContent":["export class SerialEvent extends CustomEvent implements CustomEvent {\n constructor(type: string, options: CustomEventInit) {\n super(type, options);\n }\n}\n","import { SerialEvent } from \"./SerialEvent\";\n\ntype AvailableListener = { type: string; listening: boolean };\ntype AvailableListeners = AvailableListener[];\n\ntype DataType = string | number | boolean | object | null;\n\ninterface IDispatcher {\n dispatch(type: string, data?: DataType): void;\n\n dispatchAsync(type: string, data?: DataType, ms?: number): void;\n\n on(type: string, callback: EventListener): void;\n\n off(type: string, callback: EventListener): void;\n\n serialRegisterAvailableListener(type: string): void;\n\n availableListeners: AvailableListeners;\n}\n\ninterface Listeners {\n [key: string]: boolean;\n\n debug: boolean;\n}\n\nexport class Dispatcher extends EventTarget implements IDispatcher {\n __listeners__: Listeners = {\n debug: false,\n };\n __debug__: boolean = false;\n\n __listenersCallbacks__: { key: string; callback: EventListenerOrEventListenerObject }[] = [];\n\n /**\n * Dispatches an event with the specified type and data\n * @param type - The event type to dispatch\n * @param data - Optional data to attach to the event\n * @example\n * ```typescript\n * dispatcher.dispatch('connected', { port: 'COM3' });\n * ```\n */\n public dispatch(type: string, data: DataType = null) {\n const event = new SerialEvent(type, { detail: data });\n this.dispatchEvent(event);\n if (this.__debug__) {\n this.dispatchEvent(new SerialEvent(\"debug\", { detail: { type, data } }));\n }\n }\n\n /**\n * Dispatches an event asynchronously after a specified delay\n * @param type - The event type to dispatch\n * @param data - Optional data to attach to the event\n * @param ms - Delay in milliseconds (default: 100)\n * @example\n * ```typescript\n * dispatcher.dispatchAsync('timeout', { reason: 'no response' }, 500);\n * ```\n */\n public dispatchAsync(type: string, data = null, ms = 100) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1 = this;\n setTimeout(() => {\n this1.dispatch(type, data);\n }, ms);\n }\n\n /**\n * Registers an event listener for the specified event type\n * @param type - The event type to listen to\n * @param callback - The callback function to execute when the event is triggered\n * @example\n * ```typescript\n * dispatcher.on('connected', (event) => {\n * console.log('Device connected', event.detail);\n * });\n * ```\n */\n public on(type: string, callback: EventListenerOrEventListenerObject) {\n if (typeof this.__listeners__[type] !== \"undefined\" && !this.__listeners__[type]) {\n this.__listeners__[type] = true;\n }\n\n this.__listenersCallbacks__.push({ key: type, callback });\n this.addEventListener(type, callback);\n }\n\n /**\n * Removes an event listener for the specified event type\n * @param type - The event type to stop listening to\n * @param callback - The callback function to remove\n * @example\n * ```typescript\n * const handler = (event) => console.log(event.detail);\n * dispatcher.on('data', handler);\n * dispatcher.off('data', handler);\n * ```\n */\n public off(type: string, callback: EventListenerOrEventListenerObject) {\n this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((listener) => {\n return !(listener.key === type && listener.callback === callback);\n });\n\n this.removeEventListener(type, callback);\n }\n\n /**\n * Registers an available listener type for tracking\n * @param type - The event type to register\n * @internal\n */\n public serialRegisterAvailableListener(type: string) {\n if (this.__listeners__[type]) return;\n\n this.__listeners__[type] = false;\n }\n\n /**\n * Gets the list of all available listeners and their state\n * @returns Array of listener objects with type and listening status\n * @example\n * ```typescript\n * const listeners = dispatcher.availableListeners;\n * console.log(listeners); // [{ type: 'connected', listening: true }, ...]\n * ```\n */\n get availableListeners(): AvailableListeners {\n const keys = Object.keys(this.__listeners__).sort();\n return keys.map((type): AvailableListener => {\n return {\n type,\n listening: this.__listeners__[type],\n };\n });\n }\n\n /**\n * Removes all event listeners except internal ones (like queue listeners)\n * Resets all listener states to false\n * @example\n * ```typescript\n * dispatcher.removeAllListeners();\n * ```\n */\n public removeAllListeners(): void {\n for (const listener of this.__listenersCallbacks__) {\n if ([\"internal:queue\"].includes(listener.key)) continue; // Skip queue listener\n\n this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((l) => {\n return !(l.key === listener.key && l.callback === listener.callback);\n });\n this.removeEventListener(listener.key, listener.callback);\n }\n for (const key of Object.keys(this.__listeners__)) {\n this.__listeners__[key] = false;\n }\n }\n}\n","import { Core } from \"./Core\";\nimport { Dispatcher } from \"./Dispatcher\";\n\ninterface IDevice {\n [key: string]: Core;\n}\n\ninterface IDevices {\n [key: string]: IDevice;\n}\n\n/**\n * Manages and tracks all serial devices in the application\n * Provides a centralized registry for device instances\n * @extends Dispatcher\n */\nexport class Devices extends Dispatcher {\n static instance: Devices;\n static devices: IDevices = {};\n\n constructor() {\n super();\n\n const availableListeners: string[] = [\"change\"];\n\n availableListeners.forEach((event: string): void => {\n this.serialRegisterAvailableListener(event);\n });\n }\n\n public static $dispatchChange(device: Core | null = null): void {\n if (device) {\n device.$checkAndDispatchConnection();\n }\n Devices.instance.dispatch(\"change\", { devices: Devices.devices, dispatcher: device });\n }\n\n public static typeError(type: string): void {\n const error = new Error();\n error.message = `Type ${type} is not supported`;\n error.name = \"DeviceTypeError\";\n throw error;\n }\n\n /**\n * Registers a new device type in the registry\n * @param type - The type name of the device (e.g., 'arduino', 'esp32')\n * @internal\n */\n public static registerType(type: string): void {\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.devices = { ...Devices.devices, [type]: {} };\n }\n }\n\n /**\n * Adds a device to the registry\n * @param device - The Core device instance to add\n * @returns The index of the device in its type registry\n * @throws {Error} If device with the same ID already exists\n * @example\n * ```typescript\n * const arduino = new Arduino();\n * Devices.add(arduino);\n * ```\n */\n public static add(device: Core): number {\n const type = device.typeDevice;\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.registerType(type);\n }\n\n const id: string = device.uuid;\n\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n if (Devices.devices[type][id]) {\n throw new Error(`Device with id ${id} already exists`);\n }\n\n Devices.devices[type][id] = device;\n\n Devices.$dispatchChange(device);\n return Object.keys(Devices.devices[type]).indexOf(id);\n }\n\n /**\n * Gets a specific device by type and UUID\n * @param type - The device type\n * @param id - The device UUID\n * @returns The device instance\n * @throws {Error} If the device type is not supported\n * @example\n * ```typescript\n * const device = Devices.get('arduino', 'uuid-123');\n * ```\n */\n public static get(type: string, id: string): Core {\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.registerType(type);\n }\n\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n return Devices.devices[type][id];\n }\n\n public static getAll(type: string | null = null): IDevice | IDevices {\n if (type === null) return Devices.devices;\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n return Devices.devices[type];\n }\n\n public static getList(): Core[] {\n // get all devices in list mode no matter the type\n // by some reason the array is empty so we need to use Object.values and map\n const devices: IDevice[] = Object.values(Devices.devices);\n return devices\n .map((device: IDevice): Core[] => {\n return Object.values(device);\n })\n .flat();\n }\n\n public static getByNumber(type: string, device_number: number): Core | null {\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n const devices = Object.values(Devices.devices[type]);\n return devices.find((device) => device.deviceNumber === device_number) ?? null;\n }\n\n public static getCustom(type: string, device_number: number = 1): Core | null {\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n const devices = Object.values(Devices.devices[type]);\n return devices.find((device) => device.deviceNumber === device_number) ?? null;\n }\n\n public static async connectToAll(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (device.isConnected) continue;\n await device.connect().catch(console.warn);\n }\n\n return Promise.resolve(Devices.areAllConnected());\n }\n\n public static async disconnectAll(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (device.isDisconnected) continue;\n await device.disconnect().catch(console.warn);\n }\n\n return Promise.resolve(Devices.areAllDisconnected());\n }\n\n public static async areAllConnected(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (!device.isConnected) return Promise.resolve(false);\n }\n\n return Promise.resolve(true);\n }\n\n public static async areAllDisconnected(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (!device.isDisconnected) return Promise.resolve(false);\n }\n\n return Promise.resolve(true);\n }\n\n public static async getAllConnected(): Promise {\n const devices: Core[] = Devices.getList();\n return Promise.resolve(devices.filter((device: Core): boolean => device.isConnected));\n }\n\n public static async getAllDisconnected(): Promise {\n const devices: Core[] = Devices.getList();\n return Promise.resolve(devices.filter((device: Core): boolean => device.isDisconnected));\n }\n}\n\nif (!Devices.instance) {\n Devices.instance = new Devices();\n}\n","type empty = void | PromiseLike;\n\nexport function wait(ms: number = 100): Promise {\n return new Promise(\n (resolve: (value: empty) => void): ReturnType => setTimeout((): void => resolve(), ms),\n );\n}\n\n/*\n * @deprecated This function is deprecated and will be removed in future versions.\n */\nexport function supportWebSerial(): boolean {\n return \"serial\" in navigator;\n}\n","import { io, ManagerOptions, SocketOptions, Socket as SocketIOClient } from \"socket.io-client\";\nimport { Devices } from \"./Devices\";\nimport { Core } from \"./Core\";\n\ninterface SocketResponseData {\n name: string;\n uuid: string;\n deviceNumber: number;\n [key: string]: unknown;\n}\n\ntype BoundedFunction = {\n onResponse: (data: SocketResponseData) => void;\n};\n\nclass MySocket {\n #uri: string = \"http://localhost:3000\";\n #options: Partial = {\n transports: [\"websocket\"],\n };\n #socket: SocketIOClient | null = null;\n #connected: boolean = false;\n\n #boundedFun: BoundedFunction;\n\n constructor() {\n this.#boundedFun = {\n onResponse: this.onResponse.bind(this),\n };\n }\n\n set uri(uri: string) {\n const url = new URL(uri);\n\n if (![\"http:\", \"https:\", \"ws:\", \"wss:\"].includes(url.protocol)) {\n throw new Error(\"URI must start with http://, https://, ws://, or wss://\");\n }\n this.#uri = uri;\n }\n\n get uri(): string {\n return this.#uri;\n }\n\n set options(options: Partial) {\n if (typeof options !== \"object\") {\n throw new Error(\"Options must be an object\");\n }\n this.#options = options;\n }\n\n get options(): Partial {\n return this.#options;\n }\n\n disconnect() {\n if (this.#socket) {\n this.#socket.off(\"response\", this.#boundedFun.onResponse);\n\n this.#socket.disconnect();\n this.#socket = null;\n }\n this.#connected = false;\n }\n\n prepare() {\n if (this.#connected) return;\n\n this.#socket = io(this.#uri, this.#options);\n this.#connected = true;\n\n this.#socket.on(\"response\", this.#boundedFun.onResponse);\n }\n\n connectDevice(config: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"connectDevice\", { config });\n }\n\n disconnectDevice(config: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"disconnectDevice\", { config });\n }\n\n disconnectAllDevices(): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"disconnectAll\");\n }\n\n write(data: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"cmd\", data);\n }\n\n onResponse(data: SocketResponseData): void {\n let device: Core | null = Devices.get(data.name, data.uuid);\n if (!device) {\n device = Devices.getByNumber(data.name, data.deviceNumber);\n }\n if (!device) {\n return;\n }\n device.socketResponse(data);\n }\n}\n\nexport const Socket = new MySocket();\n","import { Dispatcher } from \"./Dispatcher\";\nimport { Devices } from \"./Devices\";\nimport { wait } from \"./utils\";\nimport { Socket } from \"./Socket\";\n\ninterface LastError {\n message: string | null;\n action: string | null;\n code: string | Uint8Array | Array | Array | null | number;\n no_code: number;\n}\n\ninterface DeviceData {\n type: string;\n id: string;\n listen_on_port: number | null;\n}\n\ntype SerialResponseAs = \"hex\" | \"uint8\" | \"string\" | \"arraybuffer\";\n\ninterface SerialResponse {\n length: number | null;\n buffer: Uint8Array;\n as: SerialResponseAs;\n replacer: RegExp | string;\n limiter: null | string | RegExp;\n prefixLimiter: boolean; // If true, the limiter is at the beginning of the message\n sufixLimiter: boolean; // If true, the limiter is at the end of the message\n delimited: boolean;\n}\n\ninterface QueueData {\n bytes: string | Uint8Array | Array | Array;\n action: string;\n}\n\ntype ParserSocketPort = {\n name: \"byte-length\" | \"inter-byte-timeout\";\n length?: number; // Length of each byte in the response, only for byte-length\n interval?: number; // Interval in milliseconds for inter-byte-timeout\n};\n\ntype PortInfo = {\n path: string | null;\n vendorId: number | string | null;\n productId: number | string | null;\n parser: ParserSocketPort;\n};\n\ntype SerialData = {\n socket: boolean;\n portInfo: PortInfo;\n aux_connecting: string;\n connecting: boolean;\n connected: boolean;\n port: SerialPort | null;\n last_action: string | null;\n response: SerialResponse;\n reader: ReadableStreamDefaultReader | null;\n input_done: Promise | null;\n output_done: Promise | null;\n input_stream: ReadableStream | null;\n output_stream: WritableStream | null;\n keep_reading: boolean;\n time_until_send_bytes: number | undefined | ReturnType;\n delay_first_connection: number;\n bytes_connection: string | Uint8Array | string[] | number[] | null;\n filters: SerialPortFilter[];\n config_port: SerialOptions;\n queue: QueueData[];\n running_queue: boolean;\n auto_response: any;\n free_timeout_ms: number;\n useRTSCTS: boolean;\n};\n\ninterface TimeResponse {\n response_connection: number;\n response_engines: number;\n response_general: number;\n}\n\ninterface Timeout {\n until_response: number | ReturnType;\n}\n\ninterface InternalIntervals {\n reconnection: number;\n}\n\nexport type Internal = {\n bypassSerialBytesConnection: boolean;\n auto_response: boolean;\n device_number: number;\n aux_port_connector: number;\n last_error: LastError;\n serial: SerialData;\n device: DeviceData;\n time: TimeResponse;\n timeout: Timeout;\n interval: InternalIntervals;\n};\n\ninterface CoreConstructorParams {\n filters?: SerialPortFilter[] | null;\n config_port?: SerialOptions;\n no_device?: number;\n device_listen_on_channel?: number | string;\n bypassSerialBytesConnection?: boolean;\n socket?: boolean;\n}\n\nconst defaultConfigPort: SerialOptions = {\n baudRate: 9600,\n dataBits: 8,\n stopBits: 1,\n parity: \"none\",\n bufferSize: 32768,\n flowControl: \"none\",\n};\n\ninterface CustomCode {\n code: string | Uint8Array | Array | Array;\n}\n\ninterface ICore {\n lastAction: string | null;\n\n set listenOnChannel(channel: string | number);\n\n set serialFilters(filters: SerialPortFilter[]);\n\n get serialFilters(): SerialPortFilter[];\n\n set serialConfigPort(config_port: SerialOptions);\n\n get serialConfigPort(): SerialOptions;\n\n get isConnected(): boolean;\n\n get isConnecting(): boolean;\n\n get isDisconnected(): boolean;\n\n get useRTSCTS(): boolean;\n\n set useRTSCTS(value: boolean);\n\n get deviceNumber(): number;\n\n get uuid(): string;\n\n get typeDevice(): string;\n\n get queue(): QueueData[];\n\n get timeoutBeforeResponseBytes(): number;\n\n set timeoutBeforeResponseBytes(value: number);\n\n get fixedBytesMessage(): number | null;\n\n set fixedBytesMessage(length: number | null);\n\n get responseDelimited(): boolean;\n\n set responseDelimited(value: boolean);\n\n get responsePrefixLimited(): boolean;\n\n set responsePrefixLimited(value: boolean);\n\n get responseSufixLimited(): boolean;\n\n set responseSufixLimited(value: boolean);\n\n get responseLimiter(): string | RegExp | null;\n\n set responseLimiter(limiter: string | RegExp | null);\n\n get bypassSerialBytesConnection(): boolean;\n\n set bypassSerialBytesConnection(value: boolean);\n\n timeout(bytes: string[], event: string): Promise;\n\n disconnect(detail?: null): Promise;\n\n connect(): Promise;\n\n serialDisconnect(): Promise;\n\n serialPortsSaved(ports: SerialPort[]): Promise;\n\n serialErrors(error: unknown | Error | DOMException): void;\n\n serialConnect(): Promise;\n\n serialForget(): Promise;\n\n decToHex(dec: number | string): string;\n\n hexToDec(hex: string): number;\n\n hexMaker(val?: string, min?: number): string;\n\n add0x(bytes: string[]): string[];\n\n bytesToHex(bytes: string[]): string[];\n\n appendToQueue(arr: string[], action: string): Promise;\n\n serialSetConnectionConstant(listen_on_port?: number): string | Uint8Array | string[] | number[] | null;\n\n serialMessage(code: string[]): void;\n\n serialCorruptMessage(data: Uint8Array | number[] | string[] | never | null | string | ArrayBuffer): void;\n\n clearSerialQueue(): void;\n\n sumHex(arr: string[]): string;\n\n softReload(): void;\n\n sendConnect(): Promise;\n\n sendCustomCode(customCode: CustomCode): Promise;\n\n stringToArrayHex(string: string): string[];\n\n stringToArrayBuffer(string: string, end: string): ArrayBufferLike;\n\n parseStringToBytes(string: string, end: string): string[];\n\n parseUint8ToHex(array: Uint8Array): string[];\n\n parseHexToUint8(array: string[]): Uint8Array;\n\n stringArrayToUint8Array(strings: string[]): Uint8Array;\n\n parseUint8ArrayToString(array: string[]): string;\n\n parseStringToTextEncoder(string: string, end: string): Uint8Array;\n\n hexToAscii(hex: string | number): string;\n\n asciiToHex(asciiString: string): string;\n\n getResponseAsArrayBuffer(): void;\n\n getResponseAsArrayHex(): void;\n\n getResponseAsUint8Array(): void;\n\n getResponseAsString(): void;\n}\n\nexport class Core extends Dispatcher implements ICore {\n protected __internal__: Internal = {\n bypassSerialBytesConnection: false,\n auto_response: false,\n device_number: 1,\n aux_port_connector: 0,\n last_error: {\n message: null,\n action: null,\n code: null,\n no_code: 0,\n },\n serial: {\n socket: false,\n portInfo: {\n path: null,\n vendorId: null,\n productId: null,\n parser: {\n name: \"inter-byte-timeout\",\n interval: 50,\n },\n },\n aux_connecting: \"idle\",\n connecting: false,\n connected: false,\n port: null,\n last_action: null,\n response: {\n length: null,\n buffer: new Uint8Array([]),\n as: \"uint8\",\n replacer: /[\\n\\r]+/g,\n limiter: null,\n prefixLimiter: false,\n sufixLimiter: true,\n delimited: false,\n },\n reader: null,\n input_done: null,\n output_done: null,\n input_stream: null,\n output_stream: null,\n keep_reading: true,\n time_until_send_bytes: undefined,\n delay_first_connection: 200,\n bytes_connection: null,\n filters: [],\n config_port: defaultConfigPort,\n queue: [],\n running_queue: false,\n auto_response: null,\n free_timeout_ms: 50, // In previous versions 400 was used\n useRTSCTS: false, // Use RTS/CTS flow control\n },\n device: {\n type: \"unknown\",\n id: window.crypto.randomUUID(),\n listen_on_port: null,\n },\n time: {\n response_connection: 500,\n response_engines: 2e3,\n response_general: 2e3,\n },\n timeout: {\n until_response: 0,\n },\n interval: {\n reconnection: 0,\n },\n };\n\n #boundFinishConnecting: EventListenerOrEventListenerObject | null = null;\n\n constructor(\n {\n filters = null,\n config_port = defaultConfigPort,\n no_device = 1,\n device_listen_on_channel = 1,\n bypassSerialBytesConnection = false,\n socket = false,\n }: CoreConstructorParams = {\n filters: null,\n config_port: defaultConfigPort,\n no_device: 1,\n device_listen_on_channel: 1,\n bypassSerialBytesConnection: false,\n socket: false,\n },\n ) {\n super();\n\n if (!(\"serial\" in navigator)) {\n throw new Error(\"Web Serial not supported\");\n }\n\n if (filters) {\n this.serialFilters = filters;\n }\n\n if (config_port) {\n this.serialConfigPort = config_port;\n }\n\n if (bypassSerialBytesConnection) {\n this.__internal__.bypassSerialBytesConnection = bypassSerialBytesConnection;\n }\n\n if (no_device) {\n this.#serialSetBytesConnection(no_device);\n }\n\n if (device_listen_on_channel && [\"number\", \"string\"].includes(typeof device_listen_on_channel)) {\n this.listenOnChannel = device_listen_on_channel;\n }\n\n this.__internal__.serial.socket = socket;\n\n this.#registerDefaultListeners();\n this.#internalEvents();\n }\n\n set listenOnChannel(channel: string | number) {\n if (typeof channel === \"string\") {\n channel = parseInt(channel);\n }\n if (isNaN(channel) || channel < 1 || channel > 255) {\n throw new Error(\"Invalid port number\");\n }\n this.__internal__.device.listen_on_port = channel;\n if (this.__internal__.bypassSerialBytesConnection) return;\n this.__internal__.serial.bytes_connection = this.serialSetConnectionConstant(channel);\n }\n\n get lastAction(): string | null {\n return this.__internal__.serial.last_action;\n }\n\n get listenOnChannel(): number {\n return this.__internal__.device.listen_on_port ?? 1;\n }\n\n set serialFilters(filters: SerialPortFilter[]) {\n if (this.isConnected) throw new Error(\"Cannot change serial filters while connected\");\n this.__internal__.serial.filters = filters;\n }\n\n get serialFilters(): SerialPortFilter[] {\n return this.__internal__.serial.filters;\n }\n\n set serialConfigPort(config_port: SerialOptions) {\n if (this.isConnected) throw new Error(\"Cannot change serial filters while connected\");\n this.__internal__.serial.config_port = config_port;\n }\n\n get serialConfigPort(): SerialOptions {\n return this.__internal__.serial.config_port;\n }\n\n get useRTSCTS(): boolean {\n return this.__internal__.serial.useRTSCTS;\n }\n\n set useRTSCTS(value: boolean) {\n this.__internal__.serial.useRTSCTS = value;\n }\n\n get isConnected(): boolean {\n const prevConnected = this.__internal__.serial.connected;\n const connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n if (prevConnected && !connected) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n }\n this.__internal__.serial.connected = connected;\n return this.__internal__.serial.connected;\n }\n\n get isConnecting(): boolean {\n return this.__internal__.serial.connecting;\n }\n\n get isDisconnected(): boolean {\n const prevConnected = this.__internal__.serial.connected;\n const connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n if (!prevConnected && connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n Devices.$dispatchChange(this);\n }\n this.__internal__.serial.connected = connected;\n return !this.__internal__.serial.connected;\n }\n\n get deviceNumber(): number {\n return this.__internal__.device_number;\n }\n\n get uuid(): string {\n return this.__internal__.device.id;\n }\n\n get typeDevice(): string {\n return this.__internal__.device.type;\n }\n\n get queue(): QueueData[] {\n return this.__internal__.serial.queue;\n }\n\n get responseDelimited(): boolean {\n return this.__internal__.serial.response.delimited;\n }\n\n set responseDelimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responseDelimited must be a boolean\");\n }\n this.__internal__.serial.response.delimited = value;\n }\n\n get responsePrefixLimited(): boolean {\n return this.__internal__.serial.response.prefixLimiter;\n }\n\n set responsePrefixLimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responsePrefixLimited must be a boolean\");\n }\n this.__internal__.serial.response.prefixLimiter = value;\n }\n\n get responseSufixLimited(): boolean {\n return this.__internal__.serial.response.sufixLimiter;\n }\n\n set responseSufixLimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responseSufixLimited must be a boolean\");\n }\n this.__internal__.serial.response.sufixLimiter = value;\n }\n\n get responseLimiter(): string | RegExp | null {\n return this.__internal__.serial.response.limiter;\n }\n\n set responseLimiter(limiter: string | RegExp | null) {\n if (typeof limiter !== \"string\" && !(limiter instanceof RegExp)) {\n throw new Error(\"responseLimiter must be a string or a RegExp\");\n }\n\n this.__internal__.serial.response.limiter = limiter;\n }\n\n get fixedBytesMessage(): number | null {\n return this.__internal__.serial.response.length;\n }\n\n set fixedBytesMessage(length: number | null) {\n if (length !== null && (typeof length !== \"number\" || length < 1)) {\n throw new Error(\"Invalid length for fixed bytes message\");\n }\n this.__internal__.serial.response.length = length;\n }\n\n get timeoutBeforeResponseBytes(): number {\n return this.__internal__.serial.free_timeout_ms || 50;\n }\n\n set timeoutBeforeResponseBytes(value: number) {\n if (value !== undefined && (typeof value !== \"number\" || value < 1)) {\n throw new Error(\"Invalid timeout for response bytes\");\n }\n this.__internal__.serial.free_timeout_ms = value ?? 50;\n }\n\n get bypassSerialBytesConnection(): boolean {\n return this.__internal__.bypassSerialBytesConnection;\n }\n\n set bypassSerialBytesConnection(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"bypassSerialBytesConnection must be a boolean\");\n }\n this.__internal__.bypassSerialBytesConnection = value;\n }\n\n get useSocket(): boolean {\n return this.__internal__.serial.socket;\n }\n\n get connectionBytes(): Uint8Array {\n const bytes = this.__internal__.serial.bytes_connection;\n\n if (bytes instanceof Uint8Array) {\n return bytes;\n }\n\n if (typeof bytes === \"string\") {\n return this.stringArrayToUint8Array(this.parseStringToBytes(bytes, \"\"));\n }\n\n if (Array.isArray(bytes) && typeof bytes[0] === \"string\") {\n return this.stringArrayToUint8Array(bytes as string[]);\n }\n\n if (Array.isArray(bytes) && typeof bytes[0] === \"number\") {\n return new Uint8Array(bytes as number[]);\n }\n\n return new Uint8Array([]);\n }\n\n set portPath(path: string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port path while connected\");\n if (typeof path !== \"string\" && path !== null) {\n throw new TypeError(\"vendorId must be string or null\");\n }\n this.__internal__.serial.portInfo.path = path;\n }\n\n get portPath(): string | null {\n return this.__internal__.serial.portInfo.path;\n }\n\n set portVendorId(vendorId: number | string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port vendorId while connected\");\n if (typeof vendorId! == \"number\" && typeof vendorId !== \"string\" && vendorId !== null) {\n throw new TypeError(\"vendorId must be a number, string or null\");\n }\n this.__internal__.serial.portInfo.vendorId = vendorId;\n }\n\n get portVendorId(): number | string | null {\n return this.__internal__.serial.portInfo.vendorId;\n }\n\n set portProductId(productId: number | string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port productId while connected\");\n if (typeof productId! == \"number\" && typeof productId !== \"string\" && productId !== null) {\n throw new TypeError(\"productId must be a number, string or null\");\n }\n this.__internal__.serial.portInfo.productId = productId;\n }\n\n get portProductId(): number | string | null {\n return this.__internal__.serial.portInfo.productId;\n }\n\n set socketPortParser(string: \"byte-length\" | \"inter-byte-timeout\") {\n if ([\"byte-length\", \"inter-byte-timeout\"].includes(string)) {\n throw new TypeError(\"socketPortParser must be a string, either 'byte-length' or 'inter-byte-timeout'\");\n }\n this.__internal__.serial.portInfo.parser.name = string;\n }\n\n get socketPortParser(): \"byte-length\" | \"inter-byte-timeout\" {\n return this.__internal__.serial.portInfo.parser.name;\n }\n\n set socketPortParserInterval(value: number) {\n if (typeof value !== \"number\" || value < 1) {\n throw new TypeError(\"Interval must be a positive number\");\n }\n\n this.__internal__.serial.portInfo.parser.interval = value;\n }\n\n get socketPortParserInterval(): number {\n return this.__internal__.serial.portInfo.parser.interval || 50;\n }\n\n set socketPortParserLength(value: number) {\n if (typeof value !== \"number\" || value < 1) {\n throw new TypeError(\"Length must be a positive number or null\");\n }\n this.__internal__.serial.portInfo.parser.length = value;\n }\n\n get socketPortParserLength(): number {\n return this.__internal__.serial.portInfo.parser.length || 14;\n }\n\n get parserForSocket() {\n if (this.socketPortParser === \"byte-length\") {\n return {\n name: this.socketPortParser,\n length: this.socketPortParserLength,\n };\n }\n return {\n name: this.socketPortParser,\n interval: this.socketPortParserInterval,\n };\n }\n\n get configDeviceSocket(): object {\n return {\n uuid: this.uuid,\n name: this.typeDevice,\n deviceNumber: this.deviceNumber,\n connectionBytes: Array.from(this.connectionBytes),\n config: {\n baudRate: this.__internal__.serial.config_port.baudRate,\n dataBits: this.__internal__.serial.config_port.dataBits,\n stopBits: this.__internal__.serial.config_port.stopBits,\n parity: this.__internal__.serial.config_port.parity,\n bufferSize: this.__internal__.serial.config_port.bufferSize,\n flowControl: this.__internal__.serial.config_port.flowControl,\n },\n info: {\n vendorId: this.portVendorId, // vendor ID or null for auto-detect\n productId: this.portProductId, // product ID or null for auto-detect\n portName: this.portPath, // COM3, /dev/ttyUSB0, etc. null for auto-detect\n },\n response: {\n automatic: this.__internal__.auto_response, // true to auto-respond to commands this only for devices that doesn't respond nothing\n autoResponse: this.__internal__.serial.auto_response, // null or data to respond automatically, ie. [0x02, 0x06, 0xdd, 0xdd, 0xf0, 0xcf, 0x03] for relay\n parser: this.parserForSocket,\n timeout: {\n general: this.__internal__.time.response_general,\n engines: this.__internal__.time.response_engines,\n connection: this.__internal__.time.response_connection,\n },\n },\n };\n }\n\n #checkIfPortIsOpen(port: SerialPort | null): boolean {\n if (this.useSocket) return this.__internal__.serial.connected;\n\n return !!(port && port.readable && port.writable);\n }\n\n public async timeout(bytes: string | Uint8Array | Array | Array, event: string): Promise {\n this.__internal__.last_error.message = \"Operation response timed out.\";\n this.__internal__.last_error.action = event;\n this.__internal__.last_error.code = bytes;\n if (this.__internal__.timeout.until_response) {\n clearTimeout(this.__internal__.timeout.until_response);\n this.__internal__.timeout.until_response = 0;\n }\n if (event === \"connect\") {\n this.__internal__.serial.connected = false;\n this.dispatch(\"serial:reconnect\", {});\n Devices.$dispatchChange(this);\n } else if (event === \"connection:start\") {\n await this.serialDisconnect();\n this.__internal__.serial.connected = false;\n this.__internal__.aux_port_connector += 1;\n Devices.$dispatchChange(this);\n await this.serialConnect();\n }\n\n if (this.__internal__.serial.queue.length > 0) {\n this.dispatch(\"internal:queue\", {});\n }\n\n this.dispatch(\"serial:timeout\", {\n ...this.__internal__.last_error,\n bytes,\n action: event,\n });\n }\n\n public async disconnect(detail = null): Promise {\n await this.serialDisconnect();\n this.#disconnected(detail);\n }\n\n #disconnected(detail: object | null = null): void {\n this.__internal__.serial.connected = false;\n this.__internal__.aux_port_connector = 0;\n this.dispatch(\"serial:disconnected\", detail);\n Devices.$dispatchChange(this);\n }\n\n #onFinishConnecting(event: any): void {\n this.__internal__.serial.aux_connecting = event.detail.active ? \"connecting\" : \"finished\";\n }\n\n socketResponse(data: any) {\n const auxPrevConnected: boolean = this.__internal__.serial.connected;\n\n if (data.type === \"disconnect\" || (data.type === \"error\" && data.data === \"DISCONNECTED\")) {\n this.__internal__.serial.connected = false;\n } else if (data.type === \"success\") {\n this.__internal__.serial.connected = true;\n }\n\n Devices.$dispatchChange(this);\n if (!auxPrevConnected && this.__internal__.serial.connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n }\n\n // console.log(data, this.lastAction);\n if (data.type === \"success\") {\n this.#serialGetResponse(new Uint8Array(data.data));\n } else if (data.type === \"error\") {\n const error = new Error(\"The port is closed or is not readable/writable\");\n this.serialErrors(error);\n } else if (data.type === \"timeout\") {\n this.timeout(data.data.bytes ?? [], this.lastAction || \"unknown\");\n }\n\n this.__internal__.serial.last_action = null;\n }\n\n public async connect(): Promise {\n if (this.isConnected) {\n return true;\n }\n\n this.__internal__.serial.aux_connecting = \"idle\";\n\n return new Promise((resolve: (value: boolean) => void, reject: (reason: string) => void): void => {\n if (!this.#boundFinishConnecting) {\n this.#boundFinishConnecting = this.#onFinishConnecting.bind(this);\n }\n\n this.on(\"internal:connecting\", this.#boundFinishConnecting);\n\n const interval: ReturnType = setInterval((): void => {\n if (this.__internal__.serial.aux_connecting === \"finished\") {\n clearInterval(interval);\n this.__internal__.serial.aux_connecting = \"idle\";\n if (null !== this.#boundFinishConnecting) {\n this.off(\"internal:connecting\", this.#boundFinishConnecting);\n }\n\n if (this.isConnected) {\n resolve(true);\n } else {\n reject(`${this.typeDevice} device ${this.deviceNumber} not connected`);\n }\n } else if (this.__internal__.serial.aux_connecting === \"connecting\") {\n this.__internal__.serial.aux_connecting = \"idle\";\n this.dispatch(\"internal:connecting\", { active: true });\n this.dispatch(\"serial:connecting\", { active: true });\n }\n }, 100);\n\n this.serialConnect();\n });\n }\n\n public async serialDisconnect(): Promise {\n try {\n if (this.useSocket) {\n Socket.disconnectDevice(this.configDeviceSocket);\n } else {\n const reader: ReadableStreamDefaultReader | null = this.__internal__.serial.reader;\n const output_stream: WritableStream | null = this.__internal__.serial.output_stream;\n if (reader) {\n const reader_promise: Promise = reader.cancel();\n await reader_promise.catch((err: unknown): void => this.serialErrors(err));\n await this.__internal__.serial.input_done;\n }\n\n if (output_stream) {\n await output_stream.getWriter().close();\n await this.__internal__.serial.output_done;\n }\n\n if (this.__internal__.serial.connected && this.__internal__.serial && this.__internal__.serial.port) {\n await this.__internal__.serial.port.close();\n }\n }\n } catch (err: unknown) {\n this.serialErrors(err);\n } finally {\n this.__internal__.serial.reader = null;\n this.__internal__.serial.input_done = null;\n\n this.__internal__.serial.output_stream = null;\n this.__internal__.serial.output_done = null;\n\n this.__internal__.serial.connected = false;\n this.__internal__.serial.port = null;\n Devices.$dispatchChange(this);\n }\n }\n\n async #serialSocketWrite(data: string | Uint8Array | Array | Array): Promise {\n if (this.isDisconnected) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n throw new Error(\"The port is closed or is not readable/writable\");\n }\n\n const bytes: Uint8Array = this.validateBytes(data);\n Socket.write({ config: this.configDeviceSocket, bytes: Array.from(bytes) });\n }\n\n async #serialWrite(data: string | Uint8Array | Array | Array): Promise {\n if (this.useSocket) {\n await this.#serialSocketWrite(data);\n return;\n }\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port || (port && (!port.readable || !port.writable))) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n throw new Error(\"The port is closed or is not readable/writable\");\n }\n const bytes: Uint8Array = this.validateBytes(data);\n\n if (this.useRTSCTS) {\n await this.#waitForCTS(port, 5000);\n }\n\n if (port.writable === null) return; // never happens, it's already checked, but to suppress TS error\n const writer: WritableStreamDefaultWriter = port.writable.getWriter();\n await writer.write(bytes);\n writer.releaseLock();\n }\n\n async #waitForCTS(port: SerialPort, timeoutMs: number = 5000): Promise {\n const start = Date.now();\n while (true) {\n if (Date.now() - start > timeoutMs) {\n throw new Error(\"Timeout waiting for clearToSend signal\");\n }\n\n const { clearToSend } = await port.getSignals();\n if (clearToSend) return;\n await wait(100);\n }\n }\n\n #serialGetResponse(code: Uint8Array = new Uint8Array([]), corrupt: boolean = false) {\n if (code && code.length > 0) {\n const auxPrevConnected: boolean = this.__internal__.serial.connected;\n this.__internal__.serial.connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n Devices.$dispatchChange(this);\n if (!auxPrevConnected && this.__internal__.serial.connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n }\n\n if (this.__internal__.interval.reconnection) {\n clearInterval(this.__internal__.interval.reconnection);\n this.__internal__.interval.reconnection = 0;\n }\n\n if (this.__internal__.timeout.until_response) {\n clearTimeout(this.__internal__.timeout.until_response);\n this.__internal__.timeout.until_response = 0;\n }\n\n if (this.__internal__.serial.response.as === \"hex\") {\n if (corrupt) {\n this.serialCorruptMessage(this.parseUint8ToHex(code));\n } else {\n this.serialMessage(this.parseUint8ToHex(code));\n }\n } else if (this.__internal__.serial.response.as === \"uint8\") {\n if (corrupt) {\n this.serialCorruptMessage(code);\n } else {\n this.serialMessage(code);\n }\n } else if (this.__internal__.serial.response.as === \"string\") {\n const str = this.parseUint8ArrayToString(code);\n if (this.__internal__.serial.response.limiter !== null) {\n const splited = str.split(this.__internal__.serial.response.limiter);\n for (const s in splited) {\n if (!splited[s]) continue;\n if (corrupt) {\n this.serialCorruptMessage(splited[s]);\n } else {\n this.serialMessage(splited[s]);\n }\n }\n } else {\n if (corrupt) {\n this.serialCorruptMessage(str);\n } else {\n this.serialMessage(str);\n }\n }\n } else {\n const arraybuffer: ArrayBuffer | ArrayBufferLike = this.stringToArrayBuffer(this.parseUint8ArrayToString(code));\n if (corrupt) {\n this.serialCorruptMessage(arraybuffer as ArrayBuffer);\n } else {\n this.serialMessage(arraybuffer as ArrayBuffer);\n }\n }\n }\n\n if (this.__internal__.serial.queue.length === 0) {\n this.__internal__.serial.running_queue = false;\n return;\n }\n this.dispatch(\"internal:queue\", {});\n }\n\n public getResponseAsArrayBuffer(): void {\n this.__internal__.serial.response.as = \"arraybuffer\";\n }\n\n public getResponseAsArrayHex(): void {\n this.__internal__.serial.response.as = \"hex\";\n }\n\n public getResponseAsUint8Array(): void {\n this.__internal__.serial.response.as = \"uint8\";\n }\n\n public getResponseAsString(): void {\n this.__internal__.serial.response.as = \"string\";\n }\n\n async #serialPortsFiltered(): Promise {\n const filters: SerialPortFilter[] = this.serialFilters;\n // @ts-expect-error getPorts can use parameters\n const ports: SerialPort[] = await navigator.serial.getPorts({ filters });\n if (filters.length === 0) return ports;\n\n const filteredPorts: SerialPort[] = ports.filter((port: SerialPort): boolean => {\n const info: SerialPortInfo = port.getInfo();\n return filters.some((filter: SerialPortFilter): boolean => {\n return info.usbProductId === filter.usbProductId && info.usbVendorId === filter.usbVendorId;\n });\n });\n\n // return only ports that are not open\n return filteredPorts.filter((port: SerialPort): boolean => !this.#checkIfPortIsOpen(port));\n }\n\n public async serialPortsSaved(ports: SerialPort[]): Promise {\n const filters: SerialPortFilter[] = this.serialFilters;\n if (this.__internal__.aux_port_connector < ports.length) {\n const aux = this.__internal__.aux_port_connector;\n this.__internal__.serial.port = ports[aux];\n } else {\n this.__internal__.aux_port_connector = 0;\n this.__internal__.serial.port = await navigator.serial.requestPort({\n filters,\n });\n }\n if (!this.__internal__.serial.port) {\n throw new Error(\"Select another port please\");\n }\n }\n\n public serialErrors(error: any): void {\n const err = error.toString().toLowerCase();\n switch (true) {\n case err.includes(\"must be handling a user gesture to show a permission request\"):\n case err.includes(\"the port is closed.\"):\n case err.includes(\"the port is closed or is not writable\"):\n case err.includes(\"the port is closed or is not readable\"):\n case err.includes(\"the port is closed or is not readable/writable\"):\n case err.includes(\"select another port please\"):\n case err.includes(\"no port selected by the user\"):\n case err.includes(\n \"this readable stream reader has been released and cannot be used to cancel its previous owner stream\",\n ):\n this.dispatch(\"serial:need-permission\", {});\n Devices.$dispatchChange(this);\n break;\n case err.includes(\"the port is already open.\"):\n case err.includes(\"failed to open serial port\"):\n this.serialDisconnect().then(async () => {\n this.__internal__.aux_port_connector += 1;\n await this.serialConnect();\n });\n break;\n case err.includes(\"cannot read properties of undefined (reading 'writable')\"):\n case err.includes(\"cannot read properties of null (reading 'writable')\"):\n case err.includes(\"cannot read property 'writable' of null\"):\n case err.includes(\"cannot read property 'writable' of undefined\"):\n this.serialDisconnect().then(async () => {\n await this.serialConnect();\n });\n break;\n case err.includes(\"'close' on 'serialport': a call to close() is already in progress.\"):\n // ... do something?\n break;\n case err.includes(\"failed to execute 'open' on 'serialport': a call to open() is already in progress.\"):\n // ... do something?\n break;\n case err.includes(\"the port is already closed.\"):\n // ... do something?\n break;\n case err.includes(\"the device has been lost\"):\n this.dispatch(\"serial:lost\", {});\n Devices.$dispatchChange(this);\n // dispatch event\n break;\n case err.includes(\"navigator.serial is undefined\"):\n this.dispatch(\"serial:unsupported\", {});\n // dispatch event\n break;\n default:\n // unhandled error\n console.error(error);\n break;\n }\n\n this.dispatch(\"serial:error\", error);\n }\n\n #appendBuffer(arraybuffer: Uint8Array | ArrayBuffer | null): void {\n if (arraybuffer) {\n const incoming: Uint8Array = this.__internal__.serial.response.buffer;\n const tmp: Uint8Array = new Uint8Array(incoming.length + arraybuffer.byteLength);\n tmp.set(incoming, 0);\n tmp.set(new Uint8Array(arraybuffer), incoming.length);\n this.__internal__.serial.response.buffer = tmp;\n }\n }\n\n async #freeSerialLoop(): Promise {\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n if (this.__internal__.serial.response.buffer) {\n this.#serialGetResponse(this.__internal__.serial.response.buffer);\n }\n\n this.__internal__.serial.response.buffer = new Uint8Array(0);\n }, this.__internal__.serial.free_timeout_ms || 50);\n }\n\n async #slicedSerialLoop(): Promise {\n const expectedLength = this.__internal__.serial.response.length;\n let buffer = this.__internal__.serial.response.buffer;\n\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n if (expectedLength === null || !buffer || buffer.length === 0) return;\n\n while (buffer.length >= expectedLength) {\n const message = buffer.slice(0, expectedLength);\n this.#serialGetResponse(message);\n\n buffer = buffer.slice(expectedLength);\n }\n this.__internal__.serial.response.buffer = buffer;\n\n if (buffer.length > 0) {\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n this.#serialGetResponse(this.__internal__.serial.response.buffer, true);\n }, this.__internal__.serial.free_timeout_ms || 50);\n }\n }\n\n async #delimitedSerialLoop(): Promise {\n const {\n limiter,\n prefixLimiter = false,\n sufixLimiter = true,\n }: {\n limiter: string | RegExp | null;\n prefixLimiter?: boolean;\n sufixLimiter?: boolean;\n } = this.__internal__.serial.response;\n\n if (!limiter) {\n throw new Error(\"No limiter defined for delimited serial response\");\n }\n\n const buffer = this.__internal__.serial.response.buffer;\n\n if (!limiter || !buffer || buffer.length === 0) return;\n\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n const decoder = new TextDecoder();\n let decoded = decoder.decode(buffer);\n const messages: Uint8Array[] = [];\n\n if (typeof limiter === \"string\") {\n let pattern: RegExp;\n if (prefixLimiter && sufixLimiter) {\n pattern = new RegExp(`${limiter}([^${limiter}]+)${limiter}`, \"g\");\n } else if (prefixLimiter) {\n pattern = new RegExp(`${limiter}([^${limiter}]*)`, \"g\");\n } else if (sufixLimiter) {\n pattern = new RegExp(`([^${limiter}]+)${limiter}`, \"g\");\n } else {\n return;\n }\n\n let match;\n let lastIndex = 0;\n while ((match = pattern.exec(decoded)) !== null) {\n messages.push(new TextEncoder().encode(match[1]));\n lastIndex = pattern.lastIndex;\n }\n\n decoded = decoded.slice(lastIndex);\n } else if (limiter instanceof RegExp) {\n let match;\n let lastIndex = 0;\n if (prefixLimiter && sufixLimiter) {\n const pattern = new RegExp(`${limiter.source}(.*?)${limiter.source}`, \"g\");\n while ((match = pattern.exec(decoded)) !== null) {\n messages.push(new TextEncoder().encode(match[1]));\n lastIndex = pattern.lastIndex;\n }\n } else if (sufixLimiter) {\n while ((match = limiter.exec(decoded)) !== null) {\n const end = match.index;\n const chunk = decoded.slice(lastIndex, end);\n messages.push(new TextEncoder().encode(chunk));\n lastIndex = limiter.lastIndex;\n }\n } else if (prefixLimiter) {\n const parts = decoded.split(limiter);\n parts.shift();\n for (const part of parts) {\n messages.push(new TextEncoder().encode(part));\n }\n decoded = \"\";\n }\n\n decoded = decoded.slice(lastIndex);\n }\n\n for (const msg of messages) {\n this.#serialGetResponse(msg);\n }\n\n const leftoverBytes = new TextEncoder().encode(decoded);\n this.__internal__.serial.response.buffer = leftoverBytes;\n\n if (leftoverBytes.length > 0) {\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n this.#serialGetResponse(this.__internal__.serial.response.buffer, true);\n this.__internal__.serial.response.buffer = new Uint8Array(0);\n }, this.__internal__.serial.free_timeout_ms ?? 50);\n }\n }\n\n async #readSerialLoop(): Promise {\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port || !port.readable) throw new Error(\"Port is not readable\");\n\n const reader: ReadableStreamDefaultReader = port.readable.getReader();\n this.__internal__.serial.reader = reader;\n\n try {\n while (this.__internal__.serial.keep_reading) {\n const { value, done } = await reader.read();\n if (done) break;\n\n this.#appendBuffer(value);\n\n if (this.__internal__.serial.response.delimited) {\n await this.#delimitedSerialLoop();\n } else if (this.__internal__.serial.response.length === null) {\n await this.#freeSerialLoop();\n } else {\n await this.#slicedSerialLoop();\n }\n }\n } catch (err: unknown) {\n this.serialErrors(err);\n } finally {\n reader.releaseLock();\n this.__internal__.serial.keep_reading = true;\n\n if (this.__internal__.serial.port) {\n await this.__internal__.serial.port.close();\n }\n }\n }\n\n #connectingChange(value: boolean): void {\n if (value === this.__internal__.serial.connecting) return;\n\n this.__internal__.serial.connecting = value;\n this.dispatch(\"serial:connecting\", { active: value });\n this.dispatch(\"internal:connecting\", { active: value });\n }\n\n public async serialConnect(): Promise {\n try {\n this.#connectingChange(true);\n\n if (this.useSocket) {\n Socket.prepare();\n this.__internal__.serial.last_action = \"connect\";\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this.timeout(this.__internal__.serial.bytes_connection ?? [], \"connection:start\");\n }, this.__internal__.time.response_connection);\n Socket.connectDevice(this.configDeviceSocket);\n this.dispatch(\"serial:sent\", {\n action: \"connect\",\n bytes: this.__internal__.serial.bytes_connection,\n });\n } else {\n const ports: SerialPort[] = await this.#serialPortsFiltered();\n if (ports.length > 0) {\n await this.serialPortsSaved(ports);\n } else {\n const filters: SerialPortFilter[] = this.serialFilters;\n this.__internal__.serial.port = await navigator.serial.requestPort({\n filters,\n });\n }\n\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port) {\n throw new Error(\"No port selected by the user\");\n }\n await port.open(this.serialConfigPort);\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n port.onconnect = (event: Event): void => {\n // console.log(event);\n this1.dispatch(\"serial:connected\", event);\n this1.#connectingChange(false);\n Devices.$dispatchChange(this);\n if (this1.__internal__.serial.queue.length > 0) {\n this1.dispatch(\"internal:queue\", {});\n } else {\n this1.__internal__.serial.running_queue = false;\n }\n };\n port.ondisconnect = async (): Promise => {\n await this1.disconnect();\n };\n\n await wait(this.__internal__.serial.delay_first_connection);\n\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this1.timeout(this1.__internal__.serial.bytes_connection ?? [], \"connection:start\");\n }, this.__internal__.time.response_connection);\n\n this.__internal__.serial.last_action = \"connect\";\n await this.#serialWrite(this.__internal__.serial.bytes_connection ?? []);\n\n this.dispatch(\"serial:sent\", {\n action: \"connect\",\n bytes: this.__internal__.serial.bytes_connection,\n });\n\n if (this.__internal__.auto_response) {\n this.#serialGetResponse(this.__internal__.serial.auto_response);\n }\n await this.#readSerialLoop();\n }\n } catch (e: unknown) {\n this.#connectingChange(false);\n this.serialErrors(e);\n }\n }\n\n async #forget(): Promise {\n if (typeof window === \"undefined\") return false;\n\n if (\"serial\" in navigator && \"forget\" in SerialPort.prototype && this.__internal__.serial.port) {\n await this.__internal__.serial.port.forget();\n return true;\n }\n return false;\n }\n\n public async serialForget(): Promise {\n return await this.#forget();\n }\n\n public decToHex(dec: number | string): string {\n if (typeof dec === \"string\") {\n dec = parseInt(dec, 10);\n }\n return dec.toString(16);\n }\n\n public hexToDec(hex: string): number {\n return parseInt(hex, 16);\n }\n\n public hexMaker(val = \"00\", min = 2): string {\n return val.toString().padStart(min, \"0\").toLowerCase();\n }\n\n public add0x(bytes: string[]): string[] {\n const new_bytes: string[] = [];\n bytes.forEach((value: string, index: number): void => {\n new_bytes[index] = \"0x\" + value;\n });\n return new_bytes;\n }\n\n public bytesToHex(bytes: string[]): string[] {\n return this.add0x(Array.from(bytes, (byte: string): string => this.hexMaker(byte)));\n }\n\n #registerDefaultListeners(): void {\n const availableListeners: string[] = [\n \"serial:connected\",\n \"serial:connecting\",\n \"serial:reconnect\",\n \"serial:timeout\",\n \"serial:disconnected\",\n \"serial:sent\",\n \"serial:soft-reload\",\n \"serial:message\",\n \"serial:corrupt-message\",\n \"unknown\",\n \"serial:need-permission\",\n \"serial:lost\",\n \"serial:unsupported\",\n \"serial:error\",\n \"debug\",\n ];\n\n availableListeners.forEach((event: string): void => {\n this.serialRegisterAvailableListener(event);\n });\n }\n\n #internalEvents(): void {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n this.on(\"internal:queue\", async (): Promise => {\n await this1.#runQueue();\n });\n\n this.#browserEvents();\n }\n\n #browserEvents(): void {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n navigator.serial.addEventListener(\"connect\", async (): Promise => {\n if (!this1.isDisconnected) return;\n await this1.serialConnect().catch((): void => {});\n });\n }\n\n async #runQueue(): Promise {\n if (!this.#checkIfPortIsOpen(this.__internal__.serial.port)) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n await this.serialConnect();\n return;\n }\n\n // check if something is waiting for a response, when response arrives, the queue will be processed\n if (this.__internal__.timeout.until_response) return;\n\n if (this.__internal__.serial.queue.length === 0) {\n this.__internal__.serial.running_queue = false;\n return;\n }\n this.__internal__.serial.running_queue = true;\n\n // first element in queue\n const first: QueueData = this.__internal__.serial.queue[0];\n let limit_response: number = this.__internal__.time.response_general;\n if (first.action === \"connect\") {\n limit_response = this.__internal__.time.response_connection;\n }\n\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this.timeout(first.bytes, first.action);\n }, limit_response);\n\n this.__internal__.serial.last_action = first.action ?? \"unknown\";\n await this.#serialWrite(first.bytes);\n\n this.dispatch(\"serial:sent\", {\n action: first.action,\n bytes: first.bytes,\n });\n\n if (this.__internal__.auto_response) {\n let bytes: Uint8Array = new Uint8Array(0);\n try {\n bytes = this.validateBytes(this.__internal__.serial.auto_response);\n } catch (e: unknown) {\n this.serialErrors(e);\n }\n\n this.#serialGetResponse(bytes);\n }\n const copy_queue: QueueData[] = [...this.__internal__.serial.queue];\n this.__internal__.serial.queue = copy_queue.splice(1);\n\n if (this.__internal__.serial.queue.length > 0) {\n this.__internal__.serial.running_queue = true;\n //this.dispatch(\"internal:queue\", {});\n }\n }\n\n public validateBytes(data: string | Uint8Array | Array | Array): Uint8Array {\n let bytes: Uint8Array = new Uint8Array(0);\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (typeof data === \"string\") {\n bytes = this.parseStringToTextEncoder(data);\n } else if (Array.isArray(data) && typeof data[0] === \"string\") {\n bytes = this.stringArrayToUint8Array(data as string[]);\n } else if (Array.isArray(data) && typeof data[0] === \"number\") {\n bytes = new Uint8Array(data as unknown as number[]);\n } else {\n throw new Error(\"Invalid data type\");\n }\n return bytes;\n }\n\n public async appendToQueue(arr: string | Uint8Array | string[] | number[], action: string): Promise {\n const bytes: Uint8Array = this.validateBytes(arr);\n\n if ([\"connect\", \"connection:start\"].includes(action)) {\n if (this.__internal__.serial.connected) return;\n\n // ignore queue because the connection is not established, so first message is connection\n // queue will never send a message before connection is established\n\n await this.serialConnect();\n return;\n }\n\n this.__internal__.serial.queue.push({ bytes, action });\n this.dispatch(\"internal:queue\", {});\n }\n\n #serialSetBytesConnection(no_device = 1): void {\n this.__internal__.device_number = no_device;\n if (this.__internal__.bypassSerialBytesConnection) return;\n this.__internal__.serial.bytes_connection = this.serialSetConnectionConstant(no_device);\n }\n\n public serialSetConnectionConstant(listen_on_port = 1): string | Uint8Array | string[] | number[] | null {\n if (this.__internal__.bypassSerialBytesConnection) return this.__internal__.serial.bytes_connection;\n\n // console.warn(\"wtf?\", this.bypassSerialBytesConnection);\n\n throw new Error(`Method not implemented 'serialSetConnectionConstant' to listen on channel ${listen_on_port}`);\n // ... implement in subclass\n // return [];\n }\n\n public serialMessage(code: string[] | Uint8Array | string | ArrayBuffer): void {\n // this.dispatch('serial:message', code);\n // ... implement in subclass\n console.log(code);\n this.dispatch(\"serial:message\", { code: code });\n throw new Error(\"Method not implemented 'serialMessage'\");\n }\n\n public serialCorruptMessage(code: Uint8Array | number[] | string[] | never | null | string | ArrayBuffer): void {\n // ... implement in subclass\n console.log(code);\n this.dispatch(\"serial:corrupt-message\", { code });\n throw new Error(\"Method not implemented 'serialCorruptMessage'\");\n }\n\n #clearLastError(): void {\n this.__internal__.last_error = {\n message: null,\n action: null,\n code: null,\n no_code: 0,\n };\n }\n\n public clearSerialQueue(): void {\n this.__internal__.serial.queue = [];\n }\n\n public sumHex(arr: string[]): string {\n let sum: number = 0;\n arr.forEach((value: string): void => {\n sum += parseInt(value, 16);\n });\n return sum.toString(16);\n }\n\n public toString(): string {\n return JSON.stringify({\n __class: this.typeDevice,\n device_number: this.deviceNumber,\n uuid: this.uuid,\n connected: this.isConnected,\n connection: this.__internal__.serial.bytes_connection,\n });\n }\n\n public softReload(): void {\n this.#clearLastError();\n this.dispatch(\"serial:soft-reload\", {});\n }\n\n public async sendConnect(): Promise {\n if (!this.__internal__.serial.bytes_connection) {\n throw new Error(\"No connection bytes defined\");\n }\n await this.appendToQueue(this.__internal__.serial.bytes_connection, \"connect\");\n }\n\n public async sendCustomCode({ code = [] }: CustomCode = { code: [] }): Promise {\n if (!code) {\n throw new Error(\"No data to send\");\n }\n\n if (this.__internal__.bypassSerialBytesConnection) {\n this.__internal__.serial.bytes_connection = this.validateBytes(code);\n }\n\n await this.appendToQueue(code, \"custom\");\n }\n\n public stringToArrayHex(string: string): string[] {\n return Array.from(string).map((char: string): string => char.charCodeAt(0).toString(16));\n }\n\n public stringToArrayBuffer(string: string, end: string = \"\\n\"): ArrayBufferLike {\n return this.parseStringToTextEncoder(string, end).buffer;\n }\n\n public parseStringToTextEncoder(string: string = \"\", end: string = \"\\n\"): Uint8Array {\n const encoder = new TextEncoder();\n string += end; // to finish the command\n return encoder.encode(string);\n }\n\n public parseStringToBytes(string: string = \"\", end: string = \"\\n\"): string[] {\n const encoded: Uint8Array = this.parseStringToTextEncoder(string, end);\n return Array.from(encoded).map((byte: number): string => byte.toString(16));\n }\n\n public parseUint8ToHex(array: Uint8Array): string[] {\n return Array.from(array).map((byte: number): string => byte.toString(16).padStart(2, \"0\").toLowerCase());\n }\n\n public parseHexToUint8(array: string[]): Uint8Array {\n return new Uint8Array(array.map((hexString: string): number => parseInt(hexString, 16)));\n }\n\n public stringArrayToUint8Array(strings: string[]): Uint8Array {\n const bytes: number[] = [];\n if (typeof strings === \"string\") {\n return this.parseStringToTextEncoder(strings).buffer as unknown as Uint8Array;\n }\n strings.forEach((str: string): void => {\n const hex = str.replace(\"0x\", \"\");\n bytes.push(parseInt(hex, 16));\n });\n\n return new Uint8Array(bytes);\n }\n\n public parseUint8ArrayToString(array: Uint8Array | string[]): string {\n let arrayUint8: Uint8Array = new Uint8Array(0);\n if (array instanceof Uint8Array) {\n arrayUint8 = array;\n } else {\n arrayUint8 = this.stringArrayToUint8Array(array as string[]);\n }\n\n array = this.parseUint8ToHex(arrayUint8);\n const byteArray: number[] = array.map((hexString: string): number => parseInt(hexString, 16));\n if (this.__internal__.serial.response.replacer) {\n return String.fromCharCode(...byteArray).replace(this.__internal__.serial.response.replacer, \"\");\n }\n return String.fromCharCode(...byteArray);\n }\n\n public hexToAscii(hex: string | number): string {\n const hexString: string = hex.toString();\n let asciiString: string = \"\";\n for (let i: number = 0; i < hexString.length; i += 2) {\n asciiString += String.fromCharCode(parseInt(hexString.substring(i, 2), 16));\n }\n return asciiString;\n }\n\n public asciiToHex(asciiString: string): string {\n const hexArray: string[] = [];\n for (let i: number = 0, length: number = asciiString.length; i < length; i++) {\n const hex: string = Number(asciiString.charCodeAt(i)).toString(16);\n hexArray.push(hex);\n }\n return hexArray.join(\"\");\n }\n\n public $checkAndDispatchConnection(): boolean {\n return this.isConnected;\n }\n}\n","/**\n * Custom error codes for serial communication errors\n */\nexport enum SerialErrorCode {\n CONNECTION_FAILED = \"CONNECTION_FAILED\",\n DISCONNECTION_FAILED = \"DISCONNECTION_FAILED\",\n WRITE_FAILED = \"WRITE_FAILED\",\n READ_FAILED = \"READ_FAILED\",\n TIMEOUT = \"TIMEOUT\",\n PORT_NOT_FOUND = \"PORT_NOT_FOUND\",\n PERMISSION_DENIED = \"PERMISSION_DENIED\",\n DEVICE_NOT_SUPPORTED = \"DEVICE_NOT_SUPPORTED\",\n INVALID_CONFIGURATION = \"INVALID_CONFIGURATION\",\n SOCKET_ERROR = \"SOCKET_ERROR\",\n UNKNOWN_ERROR = \"UNKNOWN_ERROR\",\n}\n\n/**\n * Custom error class for WebSerial operations\n * Provides structured error information with codes and context\n * @extends Error\n */\nexport class SerialError extends Error {\n /**\n * Error code identifying the type of error\n */\n public readonly code: SerialErrorCode;\n\n /**\n * Additional context about the error\n */\n public readonly context?: Record;\n\n /**\n * Timestamp when the error occurred\n */\n public readonly timestamp: Date;\n\n /**\n * Creates a new SerialError\n * @param message - Human-readable error message\n * @param code - Error code from SerialErrorCode enum\n * @param context - Additional context information\n * @example\n * ```typescript\n * throw new SerialError(\n * 'Failed to connect to device',\n * SerialErrorCode.CONNECTION_FAILED,\n * { port: 'COM3', baudRate: 9600 }\n * );\n * ```\n */\n constructor(\n message: string,\n code: SerialErrorCode = SerialErrorCode.UNKNOWN_ERROR,\n context?: Record,\n ) {\n super(message);\n this.name = \"SerialError\";\n this.code = code;\n this.context = context;\n this.timestamp = new Date();\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SerialError);\n }\n }\n\n /**\n * Returns a JSON representation of the error\n * @returns Serialized error object\n */\n toJSON(): Record {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n context: this.context,\n timestamp: this.timestamp.toISOString(),\n stack: this.stack,\n };\n }\n\n /**\n * Returns a formatted string representation of the error\n * @returns Formatted error string\n */\n toString(): string {\n const contextStr = this.context ? ` | Context: ${JSON.stringify(this.context)}` : \"\";\n return `${this.name} [${this.code}]: ${this.message}${contextStr}`;\n }\n}\n"],"names":["SerialEvent","type","options","Dispatcher","data","event","ms","this1","callback","listener","l","key","Devices","device","error","id","device_number","devices","wait","resolve","MySocket","#uri","#options","#socket","#connected","#boundedFun","uri","url","io","config","Socket","defaultConfigPort","Core","#boundFinishConnecting","filters","config_port","no_device","device_listen_on_channel","bypassSerialBytesConnection","socket","#serialSetBytesConnection","#registerDefaultListeners","#internalEvents","channel","value","prevConnected","connected","#checkIfPortIsOpen","#disconnected","#connectingChange","limiter","length","bytes","path","vendorId","productId","string","port","detail","#onFinishConnecting","auxPrevConnected","#serialGetResponse","reject","interval","reader","output_stream","err","#serialSocketWrite","#serialWrite","#waitForCTS","writer","timeoutMs","start","clearToSend","code","corrupt","str","splited","s","arraybuffer","#serialPortsFiltered","ports","info","filter","aux","#appendBuffer","incoming","tmp","#freeSerialLoop","#slicedSerialLoop","expectedLength","buffer","message","#delimitedSerialLoop","prefixLimiter","sufixLimiter","decoded","messages","pattern","match","lastIndex","end","chunk","parts","part","msg","leftoverBytes","#readSerialLoop","done","#forget","dec","hex","val","min","new_bytes","index","byte","#runQueue","#browserEvents","first","limit_response","e","copy_queue","arr","action","listen_on_port","#clearLastError","sum","char","encoder","encoded","array","hexString","strings","arrayUint8","byteArray","asciiString","hexArray","i","SerialErrorCode","SerialError","context","contextStr"],"mappings":"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"} \ No newline at end of file diff --git a/dist/webserial-core.umd.cjs b/dist/webserial-core.umd.cjs index 2601604..8a6c2ca 100644 --- a/dist/webserial-core.umd.cjs +++ b/dist/webserial-core.umd.cjs @@ -1,4 +1,5 @@ -(function(l,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("socket.io-client")):typeof define=="function"&&define.amd?define(["exports","socket.io-client"],h):(l=typeof globalThis<"u"?globalThis:l||self,h(l.WebSerialCore={},l.io))})(this,function(l,h){"use strict";class m extends CustomEvent{constructor(e,t){super(e,t)}}class f extends EventTarget{__listeners__={debug:!1};__debug__=!1;__listenersCallbacks__=[];dispatch(e,t=null){const i=new m(e,{detail:t});this.dispatchEvent(i),this.__debug__&&this.dispatchEvent(new m("debug",{detail:{type:e,data:t}}))}dispatchAsync(e,t=null,i=100){const s=this;setTimeout(()=>{s.dispatch(e,t)},i)}on(e,t){typeof this.__listeners__[e]<"u"&&!this.__listeners__[e]&&(this.__listeners__[e]=!0),this.__listenersCallbacks__.push({key:e,callback:t}),this.addEventListener(e,t)}off(e,t){this.__listenersCallbacks__=this.__listenersCallbacks__.filter(i=>!(i.key===e&&i.callback===t)),this.removeEventListener(e,t)}serialRegisterAvailableListener(e){this.__listeners__[e]||(this.__listeners__[e]=!1)}get availableListeners(){return Object.keys(this.__listeners__).sort().map(t=>({type:t,listening:this.__listeners__[t]}))}removeAllListeners(){for(const e of this.__listenersCallbacks__)["internal:queue"].includes(e.key)||(this.__listenersCallbacks__=this.__listenersCallbacks__.filter(t=>!(t.key===e.key&&t.callback===e.callback)),this.removeEventListener(e.key,e.callback));for(const e of Object.keys(this.__listeners__))this.__listeners__[e]=!1}}class n extends f{static instance;static devices={};constructor(){super(),["change"].forEach(t=>{this.serialRegisterAvailableListener(t)})}static $dispatchChange(e=null){e&&e.$checkAndDispatchConnection(),n.instance.dispatch("change",{devices:n.devices,dispatcher:e})}static typeError(e){const t=new Error;throw t.message=`Type ${e} is not supported`,t.name="DeviceTypeError",t}static registerType(e){typeof n.devices[e]>"u"&&(n.devices={...n.devices,[e]:{}})}static add(e){const t=e.typeDevice;typeof n.devices[t]>"u"&&n.registerType(t);const i=e.uuid;if(typeof n.devices[t]>"u"&&n.typeError(t),n.devices[t][i])throw new Error(`Device with id ${i} already exists`);return n.devices[t][i]=e,n.$dispatchChange(e),Object.keys(n.devices[t]).indexOf(i)}static get(e,t){return typeof n.devices[e]>"u"&&n.registerType(e),typeof n.devices[e]>"u"&&n.typeError(e),n.devices[e][t]}static getAll(e=null){return e===null?n.devices:(typeof n.devices[e]>"u"&&n.typeError(e),n.devices[e])}static getList(){return Object.values(n.devices).map(t=>Object.values(t)).flat()}static getByNumber(e,t){return typeof n.devices[e]>"u"&&n.typeError(e),Object.values(n.devices[e]).find(s=>s.deviceNumber===t)??null}static getCustom(e,t=1){return typeof n.devices[e]>"u"&&n.typeError(e),Object.values(n.devices[e]).find(s=>s.deviceNumber===t)??null}static async connectToAll(){const e=n.getList();for(const t of e)t.isConnected||await t.connect().catch(console.warn);return Promise.resolve(n.areAllConnected())}static async disconnectAll(){const e=n.getList();for(const t of e)t.isDisconnected||await t.disconnect().catch(console.warn);return Promise.resolve(n.areAllDisconnected())}static async areAllConnected(){const e=n.getList();for(const t of e)if(!t.isConnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async areAllDisconnected(){const e=n.getList();for(const t of e)if(!t.isDisconnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async getAllConnected(){const e=n.getList();return Promise.resolve(e.filter(t=>t.isConnected))}static async getAllDisconnected(){const e=n.getList();return Promise.resolve(e.filter(t=>t.isDisconnected))}}n.instance||(n.instance=new n);function b(d=100){return new Promise(e=>setTimeout(()=>e(),d))}class C{#t="http://localhost:3000";#i={transports:["websocket"]};#e;#r=!1;#a={};set uri(e){const t=new URL(e);if(!["http:","https:","ws:","wss:"].includes(t.protocol))throw new Error("URI must start with http://, https://, ws://, or wss://");this.#t=e}get uri(){return this.#t}set options(e){if(typeof e!="object")throw new Error("Options must be an object");this.#i=e}get options(){return this.#i}constructor(){this.#a.onResponse=this.onResponse.bind(this)}disconnect(){this.#e&&(this.#e.off("response",this.#a.onResponse),this.#e.disconnect(),this.#e=null),this.#r=!1}prepare(){this.#r||(this.#e=h.io(this.#t,this.#i),this.#r=!0,this.#e.on("response",this.#a.onResponse))}connectDevice(e){this.#e.emit("connectDevice",{config:e})}disconnectDevice(e){this.#e.emit("disconnectDevice",{config:e})}disconnectAllDevices(){this.#e.emit("disconnectAll")}write(e){this.#e.emit("cmd",e)}onResponse(e){let t=n.get(e.name,e.uuid);t||(t=n.getByNumber(e.name,e.deviceNumber)),t&&t.socketResponse(e)}}const u=new C,g={baudRate:9600,dataBits:8,stopBits:1,parity:"none",bufferSize:32768,flowControl:"none"};class v extends f{__internal__={bypassSerialBytesConnection:!1,auto_response:!1,device_number:1,aux_port_connector:0,last_error:{message:null,action:null,code:null,no_code:0},serial:{socket:!1,portInfo:{path:null,vendorId:null,productId:null,parser:{name:"inter-byte-timeout",interval:50}},aux_connecting:"idle",connecting:!1,connected:!1,port:null,last_action:null,response:{length:null,buffer:new Uint8Array([]),as:"uint8",replacer:/[\n\r]+/g,limiter:null,prefixLimiter:!1,sufixLimiter:!0,delimited:!1},reader:null,input_done:null,output_done:null,input_stream:null,output_stream:null,keep_reading:!0,time_until_send_bytes:void 0,delay_first_connection:200,bytes_connection:null,filters:[],config_port:g,queue:[],running_queue:!1,auto_response:null,free_timeout_ms:50,useRTSCTS:!1},device:{type:"unknown",id:window.crypto.randomUUID(),listen_on_port:null},time:{response_connection:500,response_engines:2e3,response_general:2e3},timeout:{until_response:0},interval:{reconnection:0}};#t=null;constructor({filters:e=null,config_port:t=g,no_device:i=1,device_listen_on_channel:s=1,bypassSerialBytesConnection:a=!1,socket:r=!1}={filters:null,config_port:g,no_device:1,device_listen_on_channel:1,bypassSerialBytesConnection:!1,socket:!1}){if(super(),!("serial"in navigator))throw new Error("Web Serial not supported");e&&(this.serialFilters=e),t&&(this.serialConfigPort=t),a&&(this.__internal__.bypassSerialBytesConnection=a),i&&this.#w(i),s&&["number","string"].includes(typeof s)&&(this.listenOnChannel=s),this.__internal__.serial.socket=r,this.#g(),this.#y()}set listenOnChannel(e){if(typeof e=="string"&&(e=parseInt(e)),isNaN(e)||e<1||e>255)throw new Error("Invalid port number");this.__internal__.device.listen_on_port=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}get lastAction(){return this.__internal__.serial.last_action}get listenOnChannel(){return this.__internal__.device.listen_on_port??1}set serialFilters(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.filters=e}get serialFilters(){return this.__internal__.serial.filters}set serialConfigPort(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.config_port=e}get serialConfigPort(){return this.__internal__.serial.config_port}get useRTSCTS(){return this.__internal__.serial.useRTSCTS}set useRTSCTS(e){this.__internal__.serial.useRTSCTS=e}get isConnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return e&&!t&&this.#e({error:"Port is closed, not readable or writable."}),this.__internal__.serial.connected=t,this.__internal__.serial.connected}get isConnecting(){return this.__internal__.serial.connecting}get isDisconnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return!e&&t&&(this.dispatch("serial:connected"),this.#s(!1),n.$dispatchChange(this)),this.__internal__.serial.connected=t,!this.__internal__.serial.connected}get deviceNumber(){return this.__internal__.device_number}get uuid(){return this.__internal__.device.id}get typeDevice(){return this.__internal__.device.type}get queue(){return this.__internal__.serial.queue}get responseDelimited(){return this.__internal__.serial.response.delimited}set responseDelimited(e){if(typeof e!="boolean")throw new Error("responseDelimited must be a boolean");this.__internal__.serial.response.delimited=e}get responsePrefixLimited(){return this.__internal__.serial.response.prefixLimiter}set responsePrefixLimited(e){if(typeof e!="boolean")throw new Error("responsePrefixLimited must be a boolean");this.__internal__.serial.response.prefixLimiter=e}get responseSufixLimited(){return this.__internal__.serial.response.sufixLimiter}set responseSufixLimited(e){if(typeof e!="boolean")throw new Error("responseSufixLimited must be a boolean");this.__internal__.serial.response.sufixLimiter=e}get responseLimiter(){return this.__internal__.serial.response.limiter}set responseLimiter(e){if(typeof e!="string"&&!(e instanceof RegExp))throw new Error("responseLimiter must be a string or a RegExp");this.__internal__.serial.response.limiter=e}get fixedBytesMessage(){return this.__internal__.serial.response.length}set fixedBytesMessage(e){if(e!==null&&(typeof e!="number"||e<1))throw new Error("Invalid length for fixed bytes message");this.__internal__.serial.response.length=e}get timeoutBeforeResponseBytes(){return this.__internal__.serial.free_timeout_ms||50}set timeoutBeforeResponseBytes(e){if(e!==void 0&&(typeof e!="number"||e<1))throw new Error("Invalid timeout for response bytes");this.__internal__.serial.free_timeout_ms=e??50}get bypassSerialBytesConnection(){return this.__internal__.bypassSerialBytesConnection}set bypassSerialBytesConnection(e){if(typeof e!="boolean")throw new Error("bypassSerialBytesConnection must be a boolean");this.__internal__.bypassSerialBytesConnection=e}get useSocket(){return this.__internal__.serial.socket}get connectionBytes(){const e=this.__internal__.serial.bytes_connection;return e instanceof Uint8Array?e:typeof e=="string"?this.stringArrayToUint8Array(this.parseStringToBytes(e,"")):Array.isArray(e)&&typeof e[0]=="string"?this.stringArrayToUint8Array(e):Array.isArray(e)&&typeof e[0]=="number"?new Uint8Array(e):new Uint8Array([])}set portPath(e){if(this.isConnected)throw new Error("Cannot change port path while connected");if(typeof e!="string"&&e!==null)throw new TypeError("vendorId must be string or null");this.__internal__.serial.portInfo.path=e}get portPath(){return this.__internal__.serial.portInfo.path}set portVendorId(e){if(this.isConnected)throw new Error("Cannot change port vendorId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("vendorId must be a number, string or null");this.__internal__.serial.portInfo.vendorId=e}get portVendorId(){return this.__internal__.serial.portInfo.vendorId}set portProductId(e){if(this.isConnected)throw new Error("Cannot change port productId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("productId must be a number, string or null");this.__internal__.serial.portInfo.productId=e}get portProductId(){return this.__internal__.serial.portInfo.productId}set socketPortParser(e){if(["byte-length","inter-byte-timeout"].includes(e))throw new TypeError("socketPortParser must be a string, either 'byte-length' or 'inter-byte-timeout'");this.__internal__.serial.portInfo.parser.name=e}get socketPortParser(){return this.__internal__.serial.portInfo.parser.name}set socketPortParserInterval(e){if(typeof e!="number"||e<1)throw new TypeError("Interval must be a positive number");this.__internal__.serial.portInfo.parser.interval=e}get socketPortParserInterval(){return this.__internal__.serial.portInfo.parser.interval||50}set socketPortParserLength(e){if(typeof e!="number"||e<1)throw new TypeError("Length must be a positive number or null");this.__internal__.serial.portInfo.parser.length=e}get socketPortParserLength(){return this.__internal__.serial.portInfo.parser.length||14}get parserForSocket(){return this.socketPortParser==="byte-length"?{name:this.socketPortParser,length:this.socketPortParserLength}:{name:this.socketPortParser,interval:this.socketPortParserInterval}}get configDeviceSocket(){return{uuid:this.uuid,name:this.typeDevice,deviceNumber:this.deviceNumber,connectionBytes:Array.from(this.connectionBytes),config:{baudRate:this.__internal__.serial.config_port.baudRate,dataBits:this.__internal__.serial.config_port.dataBits,stopBits:this.__internal__.serial.config_port.stopBits,parity:this.__internal__.serial.config_port.parity,bufferSize:this.__internal__.serial.config_port.bufferSize,flowControl:this.__internal__.serial.config_port.flowControl},info:{vendorId:this.portVendorId,productId:this.portProductId,portName:this.portPath},response:{automatic:this.__internal__.auto_response,autoResponse:this.__internal__.serial.auto_response,parser:this.parserForSocket,timeout:{general:this.__internal__.time.response_general,engines:this.__internal__.time.response_engines,connection:this.__internal__.time.response_connection}}}}#i(e){return this.useSocket?this.__internal__.serial.connected:!!(e&&e.readable&&e.writable)}async timeout(e,t){this.__internal__.last_error.message="Operation response timed out.",this.__internal__.last_error.action=t,this.__internal__.last_error.code=e,this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),t==="connect"?(this.__internal__.serial.connected=!1,this.dispatch("serial:reconnect",{}),n.$dispatchChange(this)):t==="connection:start"&&(await this.serialDisconnect(),this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector+=1,n.$dispatchChange(this),await this.serialConnect()),this.__internal__.serial.queue.length>0&&this.dispatch("internal:queue",{}),this.dispatch("serial:timeout",{...this.__internal__.last_error,bytes:e,action:t})}async disconnect(e=null){await this.serialDisconnect(),this.#e(e)}#e(e=null){this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector=0,this.dispatch("serial:disconnected",e),n.$dispatchChange(this)}#r(e){this.__internal__.serial.aux_connecting=e.detail.active?"connecting":"finished"}socketResponse(e){const t=this.__internal__.serial.connected;if(e.type==="disconnect"||e.type==="error"&&e.data==="DISCONNECTED"?this.__internal__.serial.connected=!1:e.type==="success"&&(this.__internal__.serial.connected=!0),n.$dispatchChange(this),!t&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#s(!1)),e.type==="success")this.#n(new Uint8Array(e.data));else if(e.type==="error"){const i=new Error("The port is closed or is not readable/writable");this.serialErrors(i)}else e.type==="timeout"&&this.timeout(e.data.bytes??[],this.lastAction||"unknown");this.__internal__.serial.last_action=null}async connect(){return this.isConnected?!0:(this.__internal__.serial.aux_connecting="idle",new Promise((e,t)=>{this.#t||(this.#t=this.#r.bind(this)),this.on("internal:connecting",this.#t);const i=setInterval(()=>{this.__internal__.serial.aux_connecting==="finished"?(clearInterval(i),this.__internal__.serial.aux_connecting="idle",this.#t!==null&&this.off("internal:connecting",this.#t),this.isConnected?e(!0):t(`${this.typeDevice} device ${this.deviceNumber} not connected`)):this.__internal__.serial.aux_connecting==="connecting"&&(this.__internal__.serial.aux_connecting="idle",this.dispatch("internal:connecting",{active:!0}),this.dispatch("serial:connecting",{active:!0}))},100);this.serialConnect()}))}async serialDisconnect(){try{if(this.useSocket)u.disconnectDevice(this.configDeviceSocket);else{const e=this.__internal__.serial.reader,t=this.__internal__.serial.output_stream;e&&(await e.cancel().catch(s=>this.serialErrors(s)),await this.__internal__.serial.input_done),t&&(await t.getWriter().close(),await this.__internal__.serial.output_done),this.__internal__.serial.connected&&this.__internal__.serial&&this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}catch(e){this.serialErrors(e)}finally{this.__internal__.serial.reader=null,this.__internal__.serial.input_done=null,this.__internal__.serial.output_stream=null,this.__internal__.serial.output_done=null,this.__internal__.serial.connected=!1,this.__internal__.serial.port=null,n.$dispatchChange(this)}}async#a(e){if(this.isDisconnected)throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const t=this.validateBytes(e);u.write({config:this.configDeviceSocket,bytes:Array.from(t)})}async#o(e){if(this.useSocket){await this.#a(e);return}const t=this.__internal__.serial.port;if(!t||t&&(!t.readable||!t.writable))throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const i=this.validateBytes(e);if(this.useRTSCTS&&await this.#l(t,5e3),t.writable===null)return;const s=t.writable.getWriter();await s.write(i),s.releaseLock()}async#l(e,t=5e3){const i=Date.now();for(;;){if(Date.now()-i>t)throw new Error("Timeout waiting for clearToSend signal");const{clearToSend:s}=await e.getSignals();if(s)return;await b(100)}}#n(e=new Uint8Array([]),t=!1){if(e&&e.length>0){const i=this.__internal__.serial.connected;if(this.__internal__.serial.connected=this.#i(this.__internal__.serial.port),n.$dispatchChange(this),!i&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#s(!1)),this.__internal__.interval.reconnection&&(clearInterval(this.__internal__.interval.reconnection),this.__internal__.interval.reconnection=0),this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),this.__internal__.serial.response.as==="hex")t?this.serialCorruptMessage(this.parseUint8ToHex(e)):this.serialMessage(this.parseUint8ToHex(e));else if(this.__internal__.serial.response.as==="uint8")t?this.serialCorruptMessage(e):this.serialMessage(e);else if(this.__internal__.serial.response.as==="string"){const s=this.parseUint8ArrayToString(e);if(this.__internal__.serial.response.limiter!==null){const a=s.split(this.__internal__.serial.response.limiter);for(const r in a)a[r]&&(t?this.serialCorruptMessage(a[r]):this.serialMessage(a[r]))}else t?this.serialCorruptMessage(s):this.serialMessage(s)}else{const s=this.stringToArrayBuffer(this.parseUint8ArrayToString(e));t?this.serialCorruptMessage(s):this.serialMessage(s)}}if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.dispatch("internal:queue",{})}getResponseAsArrayBuffer(){this.__internal__.serial.response.as="arraybuffer"}getResponseAsArrayHex(){this.__internal__.serial.response.as="hex"}getResponseAsUint8Array(){this.__internal__.serial.response.as="uint8"}getResponseAsString(){this.__internal__.serial.response.as="string"}async#_(){const e=this.serialFilters,t=await navigator.serial.getPorts({filters:e});return e.length===0?t:t.filter(s=>{const a=s.getInfo();return e.some(r=>a.usbProductId===r.usbProductId&&a.usbVendorId===r.usbVendorId)}).filter(s=>!this.#i(s))}async serialPortsSaved(e){const t=this.serialFilters;if(this.__internal__.aux_port_connector{this.__internal__.aux_port_connector+=1,await this.serialConnect()});break;case t.includes("cannot read properties of undefined (reading 'writable')"):case t.includes("cannot read properties of null (reading 'writable')"):case t.includes("cannot read property 'writable' of null"):case t.includes("cannot read property 'writable' of undefined"):this.serialDisconnect().then(async()=>{await this.serialConnect()});break;case t.includes("'close' on 'serialport': a call to close() is already in progress."):break;case t.includes("failed to execute 'open' on 'serialport': a call to open() is already in progress."):break;case t.includes("the port is already closed."):break;case t.includes("the device has been lost"):this.dispatch("serial:lost",{}),n.$dispatchChange(this);break;case t.includes("navigator.serial is undefined"):this.dispatch("serial:unsupported",{});break;default:console.error(e);break}this.dispatch("serial:error",e)}#c(e){if(e){const t=this.__internal__.serial.response.buffer,i=new Uint8Array(t.length+e.byteLength);i.set(t,0),i.set(new Uint8Array(e),t.length),this.__internal__.serial.response.buffer=i}}async#h(){this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.__internal__.serial.response.buffer&&this.#n(this.__internal__.serial.response.buffer),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms||50)}async#u(){const e=this.__internal__.serial.response.length;let t=this.__internal__.serial.response.buffer;if(this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),!(e===null||!t||t.length===0)){for(;t.length>=e;){const i=t.slice(0,e);this.#n(i),t=t.slice(e)}this.__internal__.serial.response.buffer=t,t.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#n(this.__internal__.serial.response.buffer,!0)},this.__internal__.serial.free_timeout_ms||50))}}async#d(){const{limiter:e,prefixLimiter:t=!1,sufixLimiter:i=!0}=this.__internal__.serial.response;if(!e)throw new Error("No limiter defined for delimited serial response");const s=this.__internal__.serial.response.buffer;if(!e||!s||s.length===0)return;this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0);let r=new TextDecoder().decode(s);const p=[];if(typeof e=="string"){let o;if(t&&i)o=new RegExp(`${e}([^${e}]+)${e}`,"g");else if(t)o=new RegExp(`${e}([^${e}]*)`,"g");else if(i)o=new RegExp(`([^${e}]+)${e}`,"g");else return;let c,_=0;for(;(c=o.exec(r))!==null;)p.push(new TextEncoder().encode(c[1])),_=o.lastIndex;r=r.slice(_)}else if(e instanceof RegExp){let o,c=0;if(t&&i){const _=new RegExp(`${e.source}(.*?)${e.source}`,"g");for(;(o=_.exec(r))!==null;)p.push(new TextEncoder().encode(o[1])),c=_.lastIndex}else if(i)for(;(o=e.exec(r))!==null;){const _=o.index,y=r.slice(c,_);p.push(new TextEncoder().encode(y)),c=e.lastIndex}else if(t){const _=r.split(e);_.shift();for(const y of _)p.push(new TextEncoder().encode(y));r=""}r=r.slice(c)}for(const o of p)this.#n(o);const w=new TextEncoder().encode(r);this.__internal__.serial.response.buffer=w,w.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#n(this.__internal__.serial.response.buffer,!0),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms??50))}async#p(){const e=this.__internal__.serial.port;if(!e||!e.readable)throw new Error("Port is not readable");const t=e.readable.getReader();this.__internal__.serial.reader=t;try{for(;this.__internal__.serial.keep_reading;){const{value:i,done:s}=await t.read();if(s)break;this.#c(i),this.__internal__.serial.response.delimited?await this.#d():this.__internal__.serial.response.length===null?await this.#h():await this.#u()}}catch(i){this.serialErrors(i)}finally{t.releaseLock(),this.__internal__.serial.keep_reading=!0,this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}#s(e){e!==this.__internal__.serial.connecting&&(this.__internal__.serial.connecting=e,this.dispatch("serial:connecting",{active:e}),this.dispatch("internal:connecting",{active:e}))}async serialConnect(){try{if(this.#s(!0),this.useSocket)u.prepare(),this.__internal__.serial.last_action="connect",this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(this.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),u.connectDevice(this.configDeviceSocket),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection});else{const e=await this.#_();if(e.length>0)await this.serialPortsSaved(e);else{const s=this.serialFilters;this.__internal__.serial.port=await navigator.serial.requestPort({filters:s})}const t=this.__internal__.serial.port;if(!t)throw new Error("No port selected by the user");await t.open(this.serialConfigPort);const i=this;t.onconnect=s=>{i.dispatch("serial:connected",s),i.#s(!1),n.$dispatchChange(this),i.__internal__.serial.queue.length>0?i.dispatch("internal:queue",{}):i.__internal__.serial.running_queue=!1},t.ondisconnect=async()=>{await i.disconnect()},await b(this.__internal__.serial.delay_first_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await i.timeout(i.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),this.__internal__.serial.last_action="connect",await this.#o(this.__internal__.serial.bytes_connection??[]),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection}),this.__internal__.auto_response&&this.#n(this.__internal__.serial.auto_response),await this.#p()}}catch(e){this.#s(!1),this.serialErrors(e)}}async#f(){return typeof window>"u"?!1:"serial"in navigator&&"forget"in SerialPort.prototype&&this.__internal__.serial.port?(await this.__internal__.serial.port.forget(),!0):!1}async serialForget(){return await this.#f()}decToHex(e){return typeof e=="string"&&(e=parseInt(e,10)),e.toString(16)}hexToDec(e){return parseInt(e,16)}hexMaker(e="00",t=2){return e.toString().padStart(t,"0").toLowerCase()}add0x(e){const t=[];return e.forEach((i,s)=>{t[s]="0x"+i}),t}bytesToHex(e){return this.add0x(Array.from(e,t=>this.hexMaker(t)))}#g(){["serial:connected","serial:connecting","serial:reconnect","serial:timeout","serial:disconnected","serial:sent","serial:soft-reload","serial:message","serial:corrupt-message","unknown","serial:need-permission","serial:lost","serial:unsupported","serial:error","debug"].forEach(t=>{this.serialRegisterAvailableListener(t)})}#y(){const e=this;this.on("internal:queue",async()=>{await e.#b()}),this.#m()}#m(){const e=this;navigator.serial.addEventListener("connect",async()=>{e.isDisconnected&&await e.serialConnect().catch(()=>{})})}async#b(){if(!this.#i(this.__internal__.serial.port)){this.#e({error:"Port is closed, not readable or writable."}),await this.serialConnect();return}if(this.__internal__.timeout.until_response)return;if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.__internal__.serial.running_queue=!0;const e=this.__internal__.serial.queue[0];let t=this.__internal__.time.response_general;if(e.action==="connect"&&(t=this.__internal__.time.response_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(e.bytes,e.action)},t),this.__internal__.serial.last_action=e.action??"unknown",await this.#o(e.bytes),this.dispatch("serial:sent",{action:e.action,bytes:e.bytes}),this.__internal__.auto_response){let s=new Uint8Array(0);try{s=this.validateBytes(this.__internal__.serial.auto_response)}catch(a){this.serialErrors(a)}this.#n(s)}const i=[...this.__internal__.serial.queue];this.__internal__.serial.queue=i.splice(1),this.__internal__.serial.queue.length>0&&(this.__internal__.serial.running_queue=!0)}validateBytes(e){let t=new Uint8Array(0);if(e instanceof Uint8Array)t=e;else if(typeof e=="string")t=this.parseStringToTextEncoder(e);else if(Array.isArray(e)&&typeof e[0]=="string")t=this.stringArrayToUint8Array(e);else if(Array.isArray(e)&&typeof e[0]=="number")t=new Uint8Array(e);else throw new Error("Invalid data type");return t}async appendToQueue(e,t){const i=this.validateBytes(e);if(["connect","connection:start"].includes(t)){if(this.__internal__.serial.connected)return;await this.serialConnect();return}this.__internal__.serial.queue.push({bytes:i,action:t}),this.dispatch("internal:queue",{})}#w(e=1){this.__internal__.device_number=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}serialSetConnectionConstant(e=1){if(this.__internal__.bypassSerialBytesConnection)return this.__internal__.serial.bytes_connection;throw new Error(`Method not implemented 'serialSetConnectionConstant' to listen on channel ${e}`)}serialMessage(e){throw console.log(e),this.dispatch("serial:message",{code:e}),new Error("Method not implemented 'serialMessage'")}serialCorruptMessage(e){throw console.log(e),this.dispatch("serial:corrupt-message",{code:e}),new Error("Method not implemented 'serialCorruptMessage'")}#C(){this.__internal__.last_error={message:null,action:null,code:null,no_code:0}}clearSerialQueue(){this.__internal__.serial.queue=[]}sumHex(e){let t=0;return e.forEach(i=>{t+=parseInt(i,16)}),t.toString(16)}toString(){return JSON.stringify({__class:this.typeDevice,device_number:this.deviceNumber,uuid:this.uuid,connected:this.isConnected,connection:this.__internal__.serial.bytes_connection})}softReload(){this.#C(),this.dispatch("serial:soft-reload",{})}async sendConnect(){if(!this.__internal__.serial.bytes_connection)throw new Error("No connection bytes defined");await this.appendToQueue(this.__internal__.serial.bytes_connection,"connect")}async sendCustomCode({code:e=[]}={code:[]}){if(!e)throw new Error("No data to send");this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.validateBytes(e)),await this.appendToQueue(e,"custom")}stringToArrayHex(e){return Array.from(e).map(t=>t.charCodeAt(0).toString(16))}stringToArrayBuffer(e,t=` +(function(l,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("socket.io-client")):typeof define=="function"&&define.amd?define(["exports","socket.io-client"],u):(l=typeof globalThis<"u"?globalThis:l||self,u(l.WebSerialCore={},l.io))})(this,(function(l,u){"use strict";class b extends CustomEvent{constructor(e,t){super(e,t)}}class f extends EventTarget{__listeners__={debug:!1};__debug__=!1;__listenersCallbacks__=[];dispatch(e,t=null){const i=new b(e,{detail:t});this.dispatchEvent(i),this.__debug__&&this.dispatchEvent(new b("debug",{detail:{type:e,data:t}}))}dispatchAsync(e,t=null,i=100){const s=this;setTimeout(()=>{s.dispatch(e,t)},i)}on(e,t){typeof this.__listeners__[e]<"u"&&!this.__listeners__[e]&&(this.__listeners__[e]=!0),this.__listenersCallbacks__.push({key:e,callback:t}),this.addEventListener(e,t)}off(e,t){this.__listenersCallbacks__=this.__listenersCallbacks__.filter(i=>!(i.key===e&&i.callback===t)),this.removeEventListener(e,t)}serialRegisterAvailableListener(e){this.__listeners__[e]||(this.__listeners__[e]=!1)}get availableListeners(){return Object.keys(this.__listeners__).sort().map(t=>({type:t,listening:this.__listeners__[t]}))}removeAllListeners(){for(const e of this.__listenersCallbacks__)["internal:queue"].includes(e.key)||(this.__listenersCallbacks__=this.__listenersCallbacks__.filter(t=>!(t.key===e.key&&t.callback===e.callback)),this.removeEventListener(e.key,e.callback));for(const e of Object.keys(this.__listeners__))this.__listeners__[e]=!1}}class n extends f{static instance;static devices={};constructor(){super(),["change"].forEach(t=>{this.serialRegisterAvailableListener(t)})}static $dispatchChange(e=null){e&&e.$checkAndDispatchConnection(),n.instance.dispatch("change",{devices:n.devices,dispatcher:e})}static typeError(e){const t=new Error;throw t.message=`Type ${e} is not supported`,t.name="DeviceTypeError",t}static registerType(e){typeof n.devices[e]>"u"&&(n.devices={...n.devices,[e]:{}})}static add(e){const t=e.typeDevice;typeof n.devices[t]>"u"&&n.registerType(t);const i=e.uuid;if(typeof n.devices[t]>"u"&&n.typeError(t),n.devices[t][i])throw new Error(`Device with id ${i} already exists`);return n.devices[t][i]=e,n.$dispatchChange(e),Object.keys(n.devices[t]).indexOf(i)}static get(e,t){return typeof n.devices[e]>"u"&&n.registerType(e),typeof n.devices[e]>"u"&&n.typeError(e),n.devices[e][t]}static getAll(e=null){return e===null?n.devices:(typeof n.devices[e]>"u"&&n.typeError(e),n.devices[e])}static getList(){return Object.values(n.devices).map(t=>Object.values(t)).flat()}static getByNumber(e,t){return typeof n.devices[e]>"u"&&n.typeError(e),Object.values(n.devices[e]).find(s=>s.deviceNumber===t)??null}static getCustom(e,t=1){return typeof n.devices[e]>"u"&&n.typeError(e),Object.values(n.devices[e]).find(s=>s.deviceNumber===t)??null}static async connectToAll(){const e=n.getList();for(const t of e)t.isConnected||await t.connect().catch(console.warn);return Promise.resolve(n.areAllConnected())}static async disconnectAll(){const e=n.getList();for(const t of e)t.isDisconnected||await t.disconnect().catch(console.warn);return Promise.resolve(n.areAllDisconnected())}static async areAllConnected(){const e=n.getList();for(const t of e)if(!t.isConnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async areAllDisconnected(){const e=n.getList();for(const t of e)if(!t.isDisconnected)return Promise.resolve(!1);return Promise.resolve(!0)}static async getAllConnected(){const e=n.getList();return Promise.resolve(e.filter(t=>t.isConnected))}static async getAllDisconnected(){const e=n.getList();return Promise.resolve(e.filter(t=>t.isDisconnected))}}n.instance||(n.instance=new n);function w(a=100){return new Promise(e=>setTimeout(()=>e(),a))}class T{#t="http://localhost:3000";#i={transports:["websocket"]};#e=null;#r=!1;#a;constructor(){this.#a={onResponse:this.onResponse.bind(this)}}set uri(e){const t=new URL(e);if(!["http:","https:","ws:","wss:"].includes(t.protocol))throw new Error("URI must start with http://, https://, ws://, or wss://");this.#t=e}get uri(){return this.#t}set options(e){if(typeof e!="object")throw new Error("Options must be an object");this.#i=e}get options(){return this.#i}disconnect(){this.#e&&(this.#e.off("response",this.#a.onResponse),this.#e.disconnect(),this.#e=null),this.#r=!1}prepare(){this.#r||(this.#e=u.io(this.#t,this.#i),this.#r=!0,this.#e.on("response",this.#a.onResponse))}connectDevice(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("connectDevice",{config:e})}disconnectDevice(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("disconnectDevice",{config:e})}disconnectAllDevices(){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("disconnectAll")}write(e){if(!this.#e)throw new Error("Socket not connected. Call prepare() first.");this.#e.emit("cmd",e)}onResponse(e){let t=n.get(e.name,e.uuid);t||(t=n.getByNumber(e.name,e.deviceNumber)),t&&t.socketResponse(e)}}const d=new T,g={baudRate:9600,dataBits:8,stopBits:1,parity:"none",bufferSize:32768,flowControl:"none"};class S extends f{__internal__={bypassSerialBytesConnection:!1,auto_response:!1,device_number:1,aux_port_connector:0,last_error:{message:null,action:null,code:null,no_code:0},serial:{socket:!1,portInfo:{path:null,vendorId:null,productId:null,parser:{name:"inter-byte-timeout",interval:50}},aux_connecting:"idle",connecting:!1,connected:!1,port:null,last_action:null,response:{length:null,buffer:new Uint8Array([]),as:"uint8",replacer:/[\n\r]+/g,limiter:null,prefixLimiter:!1,sufixLimiter:!0,delimited:!1},reader:null,input_done:null,output_done:null,input_stream:null,output_stream:null,keep_reading:!0,time_until_send_bytes:void 0,delay_first_connection:200,bytes_connection:null,filters:[],config_port:g,queue:[],running_queue:!1,auto_response:null,free_timeout_ms:50,useRTSCTS:!1},device:{type:"unknown",id:window.crypto.randomUUID(),listen_on_port:null},time:{response_connection:500,response_engines:2e3,response_general:2e3},timeout:{until_response:0},interval:{reconnection:0}};#t=null;constructor({filters:e=null,config_port:t=g,no_device:i=1,device_listen_on_channel:s=1,bypassSerialBytesConnection:o=!1,socket:r=!1}={filters:null,config_port:g,no_device:1,device_listen_on_channel:1,bypassSerialBytesConnection:!1,socket:!1}){if(super(),!("serial"in navigator))throw new Error("Web Serial not supported");e&&(this.serialFilters=e),t&&(this.serialConfigPort=t),o&&(this.__internal__.bypassSerialBytesConnection=o),i&&this.#w(i),s&&["number","string"].includes(typeof s)&&(this.listenOnChannel=s),this.__internal__.serial.socket=r,this.#g(),this.#y()}set listenOnChannel(e){if(typeof e=="string"&&(e=parseInt(e)),isNaN(e)||e<1||e>255)throw new Error("Invalid port number");this.__internal__.device.listen_on_port=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}get lastAction(){return this.__internal__.serial.last_action}get listenOnChannel(){return this.__internal__.device.listen_on_port??1}set serialFilters(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.filters=e}get serialFilters(){return this.__internal__.serial.filters}set serialConfigPort(e){if(this.isConnected)throw new Error("Cannot change serial filters while connected");this.__internal__.serial.config_port=e}get serialConfigPort(){return this.__internal__.serial.config_port}get useRTSCTS(){return this.__internal__.serial.useRTSCTS}set useRTSCTS(e){this.__internal__.serial.useRTSCTS=e}get isConnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return e&&!t&&this.#e({error:"Port is closed, not readable or writable."}),this.__internal__.serial.connected=t,this.__internal__.serial.connected}get isConnecting(){return this.__internal__.serial.connecting}get isDisconnected(){const e=this.__internal__.serial.connected,t=this.#i(this.__internal__.serial.port);return!e&&t&&(this.dispatch("serial:connected"),this.#s(!1),n.$dispatchChange(this)),this.__internal__.serial.connected=t,!this.__internal__.serial.connected}get deviceNumber(){return this.__internal__.device_number}get uuid(){return this.__internal__.device.id}get typeDevice(){return this.__internal__.device.type}get queue(){return this.__internal__.serial.queue}get responseDelimited(){return this.__internal__.serial.response.delimited}set responseDelimited(e){if(typeof e!="boolean")throw new Error("responseDelimited must be a boolean");this.__internal__.serial.response.delimited=e}get responsePrefixLimited(){return this.__internal__.serial.response.prefixLimiter}set responsePrefixLimited(e){if(typeof e!="boolean")throw new Error("responsePrefixLimited must be a boolean");this.__internal__.serial.response.prefixLimiter=e}get responseSufixLimited(){return this.__internal__.serial.response.sufixLimiter}set responseSufixLimited(e){if(typeof e!="boolean")throw new Error("responseSufixLimited must be a boolean");this.__internal__.serial.response.sufixLimiter=e}get responseLimiter(){return this.__internal__.serial.response.limiter}set responseLimiter(e){if(typeof e!="string"&&!(e instanceof RegExp))throw new Error("responseLimiter must be a string or a RegExp");this.__internal__.serial.response.limiter=e}get fixedBytesMessage(){return this.__internal__.serial.response.length}set fixedBytesMessage(e){if(e!==null&&(typeof e!="number"||e<1))throw new Error("Invalid length for fixed bytes message");this.__internal__.serial.response.length=e}get timeoutBeforeResponseBytes(){return this.__internal__.serial.free_timeout_ms||50}set timeoutBeforeResponseBytes(e){if(e!==void 0&&(typeof e!="number"||e<1))throw new Error("Invalid timeout for response bytes");this.__internal__.serial.free_timeout_ms=e??50}get bypassSerialBytesConnection(){return this.__internal__.bypassSerialBytesConnection}set bypassSerialBytesConnection(e){if(typeof e!="boolean")throw new Error("bypassSerialBytesConnection must be a boolean");this.__internal__.bypassSerialBytesConnection=e}get useSocket(){return this.__internal__.serial.socket}get connectionBytes(){const e=this.__internal__.serial.bytes_connection;return e instanceof Uint8Array?e:typeof e=="string"?this.stringArrayToUint8Array(this.parseStringToBytes(e,"")):Array.isArray(e)&&typeof e[0]=="string"?this.stringArrayToUint8Array(e):Array.isArray(e)&&typeof e[0]=="number"?new Uint8Array(e):new Uint8Array([])}set portPath(e){if(this.isConnected)throw new Error("Cannot change port path while connected");if(typeof e!="string"&&e!==null)throw new TypeError("vendorId must be string or null");this.__internal__.serial.portInfo.path=e}get portPath(){return this.__internal__.serial.portInfo.path}set portVendorId(e){if(this.isConnected)throw new Error("Cannot change port vendorId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("vendorId must be a number, string or null");this.__internal__.serial.portInfo.vendorId=e}get portVendorId(){return this.__internal__.serial.portInfo.vendorId}set portProductId(e){if(this.isConnected)throw new Error("Cannot change port productId while connected");if(typeof e=="number"&&typeof e!="string"&&e!==null)throw new TypeError("productId must be a number, string or null");this.__internal__.serial.portInfo.productId=e}get portProductId(){return this.__internal__.serial.portInfo.productId}set socketPortParser(e){if(["byte-length","inter-byte-timeout"].includes(e))throw new TypeError("socketPortParser must be a string, either 'byte-length' or 'inter-byte-timeout'");this.__internal__.serial.portInfo.parser.name=e}get socketPortParser(){return this.__internal__.serial.portInfo.parser.name}set socketPortParserInterval(e){if(typeof e!="number"||e<1)throw new TypeError("Interval must be a positive number");this.__internal__.serial.portInfo.parser.interval=e}get socketPortParserInterval(){return this.__internal__.serial.portInfo.parser.interval||50}set socketPortParserLength(e){if(typeof e!="number"||e<1)throw new TypeError("Length must be a positive number or null");this.__internal__.serial.portInfo.parser.length=e}get socketPortParserLength(){return this.__internal__.serial.portInfo.parser.length||14}get parserForSocket(){return this.socketPortParser==="byte-length"?{name:this.socketPortParser,length:this.socketPortParserLength}:{name:this.socketPortParser,interval:this.socketPortParserInterval}}get configDeviceSocket(){return{uuid:this.uuid,name:this.typeDevice,deviceNumber:this.deviceNumber,connectionBytes:Array.from(this.connectionBytes),config:{baudRate:this.__internal__.serial.config_port.baudRate,dataBits:this.__internal__.serial.config_port.dataBits,stopBits:this.__internal__.serial.config_port.stopBits,parity:this.__internal__.serial.config_port.parity,bufferSize:this.__internal__.serial.config_port.bufferSize,flowControl:this.__internal__.serial.config_port.flowControl},info:{vendorId:this.portVendorId,productId:this.portProductId,portName:this.portPath},response:{automatic:this.__internal__.auto_response,autoResponse:this.__internal__.serial.auto_response,parser:this.parserForSocket,timeout:{general:this.__internal__.time.response_general,engines:this.__internal__.time.response_engines,connection:this.__internal__.time.response_connection}}}}#i(e){return this.useSocket?this.__internal__.serial.connected:!!(e&&e.readable&&e.writable)}async timeout(e,t){this.__internal__.last_error.message="Operation response timed out.",this.__internal__.last_error.action=t,this.__internal__.last_error.code=e,this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),t==="connect"?(this.__internal__.serial.connected=!1,this.dispatch("serial:reconnect",{}),n.$dispatchChange(this)):t==="connection:start"&&(await this.serialDisconnect(),this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector+=1,n.$dispatchChange(this),await this.serialConnect()),this.__internal__.serial.queue.length>0&&this.dispatch("internal:queue",{}),this.dispatch("serial:timeout",{...this.__internal__.last_error,bytes:e,action:t})}async disconnect(e=null){await this.serialDisconnect(),this.#e(e)}#e(e=null){this.__internal__.serial.connected=!1,this.__internal__.aux_port_connector=0,this.dispatch("serial:disconnected",e),n.$dispatchChange(this)}#r(e){this.__internal__.serial.aux_connecting=e.detail.active?"connecting":"finished"}socketResponse(e){const t=this.__internal__.serial.connected;if(e.type==="disconnect"||e.type==="error"&&e.data==="DISCONNECTED"?this.__internal__.serial.connected=!1:e.type==="success"&&(this.__internal__.serial.connected=!0),n.$dispatchChange(this),!t&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#s(!1)),e.type==="success")this.#n(new Uint8Array(e.data));else if(e.type==="error"){const i=new Error("The port is closed or is not readable/writable");this.serialErrors(i)}else e.type==="timeout"&&this.timeout(e.data.bytes??[],this.lastAction||"unknown");this.__internal__.serial.last_action=null}async connect(){return this.isConnected?!0:(this.__internal__.serial.aux_connecting="idle",new Promise((e,t)=>{this.#t||(this.#t=this.#r.bind(this)),this.on("internal:connecting",this.#t);const i=setInterval(()=>{this.__internal__.serial.aux_connecting==="finished"?(clearInterval(i),this.__internal__.serial.aux_connecting="idle",this.#t!==null&&this.off("internal:connecting",this.#t),this.isConnected?e(!0):t(`${this.typeDevice} device ${this.deviceNumber} not connected`)):this.__internal__.serial.aux_connecting==="connecting"&&(this.__internal__.serial.aux_connecting="idle",this.dispatch("internal:connecting",{active:!0}),this.dispatch("serial:connecting",{active:!0}))},100);this.serialConnect()}))}async serialDisconnect(){try{if(this.useSocket)d.disconnectDevice(this.configDeviceSocket);else{const e=this.__internal__.serial.reader,t=this.__internal__.serial.output_stream;e&&(await e.cancel().catch(s=>this.serialErrors(s)),await this.__internal__.serial.input_done),t&&(await t.getWriter().close(),await this.__internal__.serial.output_done),this.__internal__.serial.connected&&this.__internal__.serial&&this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}catch(e){this.serialErrors(e)}finally{this.__internal__.serial.reader=null,this.__internal__.serial.input_done=null,this.__internal__.serial.output_stream=null,this.__internal__.serial.output_done=null,this.__internal__.serial.connected=!1,this.__internal__.serial.port=null,n.$dispatchChange(this)}}async#a(e){if(this.isDisconnected)throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const t=this.validateBytes(e);d.write({config:this.configDeviceSocket,bytes:Array.from(t)})}async#o(e){if(this.useSocket){await this.#a(e);return}const t=this.__internal__.serial.port;if(!t||t&&(!t.readable||!t.writable))throw this.#e({error:"Port is closed, not readable or writable."}),new Error("The port is closed or is not readable/writable");const i=this.validateBytes(e);if(this.useRTSCTS&&await this.#l(t,5e3),t.writable===null)return;const s=t.writable.getWriter();await s.write(i),s.releaseLock()}async#l(e,t=5e3){const i=Date.now();for(;;){if(Date.now()-i>t)throw new Error("Timeout waiting for clearToSend signal");const{clearToSend:s}=await e.getSignals();if(s)return;await w(100)}}#n(e=new Uint8Array([]),t=!1){if(e&&e.length>0){const i=this.__internal__.serial.connected;if(this.__internal__.serial.connected=this.#i(this.__internal__.serial.port),n.$dispatchChange(this),!i&&this.__internal__.serial.connected&&(this.dispatch("serial:connected"),this.#s(!1)),this.__internal__.interval.reconnection&&(clearInterval(this.__internal__.interval.reconnection),this.__internal__.interval.reconnection=0),this.__internal__.timeout.until_response&&(clearTimeout(this.__internal__.timeout.until_response),this.__internal__.timeout.until_response=0),this.__internal__.serial.response.as==="hex")t?this.serialCorruptMessage(this.parseUint8ToHex(e)):this.serialMessage(this.parseUint8ToHex(e));else if(this.__internal__.serial.response.as==="uint8")t?this.serialCorruptMessage(e):this.serialMessage(e);else if(this.__internal__.serial.response.as==="string"){const s=this.parseUint8ArrayToString(e);if(this.__internal__.serial.response.limiter!==null){const o=s.split(this.__internal__.serial.response.limiter);for(const r in o)o[r]&&(t?this.serialCorruptMessage(o[r]):this.serialMessage(o[r]))}else t?this.serialCorruptMessage(s):this.serialMessage(s)}else{const s=this.stringToArrayBuffer(this.parseUint8ArrayToString(e));t?this.serialCorruptMessage(s):this.serialMessage(s)}}if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.dispatch("internal:queue",{})}getResponseAsArrayBuffer(){this.__internal__.serial.response.as="arraybuffer"}getResponseAsArrayHex(){this.__internal__.serial.response.as="hex"}getResponseAsUint8Array(){this.__internal__.serial.response.as="uint8"}getResponseAsString(){this.__internal__.serial.response.as="string"}async#_(){const e=this.serialFilters,t=await navigator.serial.getPorts({filters:e});return e.length===0?t:t.filter(s=>{const o=s.getInfo();return e.some(r=>o.usbProductId===r.usbProductId&&o.usbVendorId===r.usbVendorId)}).filter(s=>!this.#i(s))}async serialPortsSaved(e){const t=this.serialFilters;if(this.__internal__.aux_port_connector{this.__internal__.aux_port_connector+=1,await this.serialConnect()});break;case t.includes("cannot read properties of undefined (reading 'writable')"):case t.includes("cannot read properties of null (reading 'writable')"):case t.includes("cannot read property 'writable' of null"):case t.includes("cannot read property 'writable' of undefined"):this.serialDisconnect().then(async()=>{await this.serialConnect()});break;case t.includes("'close' on 'serialport': a call to close() is already in progress."):break;case t.includes("failed to execute 'open' on 'serialport': a call to open() is already in progress."):break;case t.includes("the port is already closed."):break;case t.includes("the device has been lost"):this.dispatch("serial:lost",{}),n.$dispatchChange(this);break;case t.includes("navigator.serial is undefined"):this.dispatch("serial:unsupported",{});break;default:console.error(e);break}this.dispatch("serial:error",e)}#c(e){if(e){const t=this.__internal__.serial.response.buffer,i=new Uint8Array(t.length+e.byteLength);i.set(t,0),i.set(new Uint8Array(e),t.length),this.__internal__.serial.response.buffer=i}}async#h(){this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.__internal__.serial.response.buffer&&this.#n(this.__internal__.serial.response.buffer),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms||50)}async#u(){const e=this.__internal__.serial.response.length;let t=this.__internal__.serial.response.buffer;if(this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0),!(e===null||!t||t.length===0)){for(;t.length>=e;){const i=t.slice(0,e);this.#n(i),t=t.slice(e)}this.__internal__.serial.response.buffer=t,t.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#n(this.__internal__.serial.response.buffer,!0)},this.__internal__.serial.free_timeout_ms||50))}}async#d(){const{limiter:e,prefixLimiter:t=!1,sufixLimiter:i=!0}=this.__internal__.serial.response;if(!e)throw new Error("No limiter defined for delimited serial response");const s=this.__internal__.serial.response.buffer;if(!e||!s||s.length===0)return;this.__internal__.serial.time_until_send_bytes&&(clearTimeout(this.__internal__.serial.time_until_send_bytes),this.__internal__.serial.time_until_send_bytes=0);let r=new TextDecoder().decode(s);const p=[];if(typeof e=="string"){let _;if(t&&i)_=new RegExp(`${e}([^${e}]+)${e}`,"g");else if(t)_=new RegExp(`${e}([^${e}]*)`,"g");else if(i)_=new RegExp(`([^${e}]+)${e}`,"g");else return;let h,c=0;for(;(h=_.exec(r))!==null;)p.push(new TextEncoder().encode(h[1])),c=_.lastIndex;r=r.slice(c)}else if(e instanceof RegExp){let _,h=0;if(t&&i){const c=new RegExp(`${e.source}(.*?)${e.source}`,"g");for(;(_=c.exec(r))!==null;)p.push(new TextEncoder().encode(_[1])),h=c.lastIndex}else if(i)for(;(_=e.exec(r))!==null;){const c=_.index,m=r.slice(h,c);p.push(new TextEncoder().encode(m)),h=e.lastIndex}else if(t){const c=r.split(e);c.shift();for(const m of c)p.push(new TextEncoder().encode(m));r=""}r=r.slice(h)}for(const _ of p)this.#n(_);const E=new TextEncoder().encode(r);this.__internal__.serial.response.buffer=E,E.length>0&&(this.__internal__.serial.time_until_send_bytes=setTimeout(()=>{this.#n(this.__internal__.serial.response.buffer,!0),this.__internal__.serial.response.buffer=new Uint8Array(0)},this.__internal__.serial.free_timeout_ms??50))}async#p(){const e=this.__internal__.serial.port;if(!e||!e.readable)throw new Error("Port is not readable");const t=e.readable.getReader();this.__internal__.serial.reader=t;try{for(;this.__internal__.serial.keep_reading;){const{value:i,done:s}=await t.read();if(s)break;this.#c(i),this.__internal__.serial.response.delimited?await this.#d():this.__internal__.serial.response.length===null?await this.#h():await this.#u()}}catch(i){this.serialErrors(i)}finally{t.releaseLock(),this.__internal__.serial.keep_reading=!0,this.__internal__.serial.port&&await this.__internal__.serial.port.close()}}#s(e){e!==this.__internal__.serial.connecting&&(this.__internal__.serial.connecting=e,this.dispatch("serial:connecting",{active:e}),this.dispatch("internal:connecting",{active:e}))}async serialConnect(){try{if(this.#s(!0),this.useSocket)d.prepare(),this.__internal__.serial.last_action="connect",this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(this.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),d.connectDevice(this.configDeviceSocket),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection});else{const e=await this.#_();if(e.length>0)await this.serialPortsSaved(e);else{const s=this.serialFilters;this.__internal__.serial.port=await navigator.serial.requestPort({filters:s})}const t=this.__internal__.serial.port;if(!t)throw new Error("No port selected by the user");await t.open(this.serialConfigPort);const i=this;t.onconnect=s=>{i.dispatch("serial:connected",s),i.#s(!1),n.$dispatchChange(this),i.__internal__.serial.queue.length>0?i.dispatch("internal:queue",{}):i.__internal__.serial.running_queue=!1},t.ondisconnect=async()=>{await i.disconnect()},await w(this.__internal__.serial.delay_first_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await i.timeout(i.__internal__.serial.bytes_connection??[],"connection:start")},this.__internal__.time.response_connection),this.__internal__.serial.last_action="connect",await this.#o(this.__internal__.serial.bytes_connection??[]),this.dispatch("serial:sent",{action:"connect",bytes:this.__internal__.serial.bytes_connection}),this.__internal__.auto_response&&this.#n(this.__internal__.serial.auto_response),await this.#p()}}catch(e){this.#s(!1),this.serialErrors(e)}}async#f(){return typeof window>"u"?!1:"serial"in navigator&&"forget"in SerialPort.prototype&&this.__internal__.serial.port?(await this.__internal__.serial.port.forget(),!0):!1}async serialForget(){return await this.#f()}decToHex(e){return typeof e=="string"&&(e=parseInt(e,10)),e.toString(16)}hexToDec(e){return parseInt(e,16)}hexMaker(e="00",t=2){return e.toString().padStart(t,"0").toLowerCase()}add0x(e){const t=[];return e.forEach((i,s)=>{t[s]="0x"+i}),t}bytesToHex(e){return this.add0x(Array.from(e,t=>this.hexMaker(t)))}#g(){["serial:connected","serial:connecting","serial:reconnect","serial:timeout","serial:disconnected","serial:sent","serial:soft-reload","serial:message","serial:corrupt-message","unknown","serial:need-permission","serial:lost","serial:unsupported","serial:error","debug"].forEach(t=>{this.serialRegisterAvailableListener(t)})}#y(){const e=this;this.on("internal:queue",async()=>{await e.#b()}),this.#m()}#m(){const e=this;navigator.serial.addEventListener("connect",async()=>{e.isDisconnected&&await e.serialConnect().catch(()=>{})})}async#b(){if(!this.#i(this.__internal__.serial.port)){this.#e({error:"Port is closed, not readable or writable."}),await this.serialConnect();return}if(this.__internal__.timeout.until_response)return;if(this.__internal__.serial.queue.length===0){this.__internal__.serial.running_queue=!1;return}this.__internal__.serial.running_queue=!0;const e=this.__internal__.serial.queue[0];let t=this.__internal__.time.response_general;if(e.action==="connect"&&(t=this.__internal__.time.response_connection),this.__internal__.timeout.until_response=setTimeout(async()=>{await this.timeout(e.bytes,e.action)},t),this.__internal__.serial.last_action=e.action??"unknown",await this.#o(e.bytes),this.dispatch("serial:sent",{action:e.action,bytes:e.bytes}),this.__internal__.auto_response){let s=new Uint8Array(0);try{s=this.validateBytes(this.__internal__.serial.auto_response)}catch(o){this.serialErrors(o)}this.#n(s)}const i=[...this.__internal__.serial.queue];this.__internal__.serial.queue=i.splice(1),this.__internal__.serial.queue.length>0&&(this.__internal__.serial.running_queue=!0)}validateBytes(e){let t=new Uint8Array(0);if(e instanceof Uint8Array)t=e;else if(typeof e=="string")t=this.parseStringToTextEncoder(e);else if(Array.isArray(e)&&typeof e[0]=="string")t=this.stringArrayToUint8Array(e);else if(Array.isArray(e)&&typeof e[0]=="number")t=new Uint8Array(e);else throw new Error("Invalid data type");return t}async appendToQueue(e,t){const i=this.validateBytes(e);if(["connect","connection:start"].includes(t)){if(this.__internal__.serial.connected)return;await this.serialConnect();return}this.__internal__.serial.queue.push({bytes:i,action:t}),this.dispatch("internal:queue",{})}#w(e=1){this.__internal__.device_number=e,!this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.serialSetConnectionConstant(e))}serialSetConnectionConstant(e=1){if(this.__internal__.bypassSerialBytesConnection)return this.__internal__.serial.bytes_connection;throw new Error(`Method not implemented 'serialSetConnectionConstant' to listen on channel ${e}`)}serialMessage(e){throw console.log(e),this.dispatch("serial:message",{code:e}),new Error("Method not implemented 'serialMessage'")}serialCorruptMessage(e){throw console.log(e),this.dispatch("serial:corrupt-message",{code:e}),new Error("Method not implemented 'serialCorruptMessage'")}#C(){this.__internal__.last_error={message:null,action:null,code:null,no_code:0}}clearSerialQueue(){this.__internal__.serial.queue=[]}sumHex(e){let t=0;return e.forEach(i=>{t+=parseInt(i,16)}),t.toString(16)}toString(){return JSON.stringify({__class:this.typeDevice,device_number:this.deviceNumber,uuid:this.uuid,connected:this.isConnected,connection:this.__internal__.serial.bytes_connection})}softReload(){this.#C(),this.dispatch("serial:soft-reload",{})}async sendConnect(){if(!this.__internal__.serial.bytes_connection)throw new Error("No connection bytes defined");await this.appendToQueue(this.__internal__.serial.bytes_connection,"connect")}async sendCustomCode({code:e=[]}={code:[]}){if(!e)throw new Error("No data to send");this.__internal__.bypassSerialBytesConnection&&(this.__internal__.serial.bytes_connection=this.validateBytes(e)),await this.appendToQueue(e,"custom")}stringToArrayHex(e){return Array.from(e).map(t=>t.charCodeAt(0).toString(16))}stringToArrayBuffer(e,t=` `){return this.parseStringToTextEncoder(e,t).buffer}parseStringToTextEncoder(e="",t=` `){const i=new TextEncoder;return e+=t,i.encode(e)}parseStringToBytes(e="",t=` -`){const i=this.parseStringToTextEncoder(e,t);return Array.from(i).map(s=>s.toString(16))}parseUint8ToHex(e){return Array.from(e).map(t=>t.toString(16).padStart(2,"0").toLowerCase())}parseHexToUint8(e){return new Uint8Array(e.map(t=>parseInt(t,16)))}stringArrayToUint8Array(e){const t=[];return typeof e=="string"?this.parseStringToTextEncoder(e).buffer:(e.forEach(i=>{const s=i.replace("0x","");t.push(parseInt(s,16))}),new Uint8Array(t))}parseUint8ArrayToString(e){let t=new Uint8Array(0);e instanceof Uint8Array?t=e:t=this.stringArrayToUint8Array(e),e=this.parseUint8ToHex(t);const i=e.map(s=>parseInt(s,16));return this.__internal__.serial.response.replacer?String.fromCharCode(...i).replace(this.__internal__.serial.response.replacer,""):String.fromCharCode(...i)}hexToAscii(e){const t=e.toString();let i="";for(let s=0;ss.toString(16))}parseUint8ToHex(e){return Array.from(e).map(t=>t.toString(16).padStart(2,"0").toLowerCase())}parseHexToUint8(e){return new Uint8Array(e.map(t=>parseInt(t,16)))}stringArrayToUint8Array(e){const t=[];return typeof e=="string"?this.parseStringToTextEncoder(e).buffer:(e.forEach(i=>{const s=i.replace("0x","");t.push(parseInt(s,16))}),new Uint8Array(t))}parseUint8ArrayToString(e){let t=new Uint8Array(0);e instanceof Uint8Array?t=e:t=this.stringArrayToUint8Array(e),e=this.parseUint8ToHex(t);const i=e.map(s=>parseInt(s,16));return this.__internal__.serial.response.replacer?String.fromCharCode(...i).replace(this.__internal__.serial.response.replacer,""):String.fromCharCode(...i)}hexToAscii(e){const t=e.toString();let i="";for(let s=0;s(a.CONNECTION_FAILED="CONNECTION_FAILED",a.DISCONNECTION_FAILED="DISCONNECTION_FAILED",a.WRITE_FAILED="WRITE_FAILED",a.READ_FAILED="READ_FAILED",a.TIMEOUT="TIMEOUT",a.PORT_NOT_FOUND="PORT_NOT_FOUND",a.PERMISSION_DENIED="PERMISSION_DENIED",a.DEVICE_NOT_SUPPORTED="DEVICE_NOT_SUPPORTED",a.INVALID_CONFIGURATION="INVALID_CONFIGURATION",a.SOCKET_ERROR="SOCKET_ERROR",a.UNKNOWN_ERROR="UNKNOWN_ERROR",a))(C||{});class y extends Error{code;context;timestamp;constructor(e,t="UNKNOWN_ERROR",i){super(e),this.name="SerialError",this.code=t,this.context=i,this.timestamp=new Date,Error.captureStackTrace&&Error.captureStackTrace(this,y)}toJSON(){return{name:this.name,message:this.message,code:this.code,context:this.context,timestamp:this.timestamp.toISOString(),stack:this.stack}}toString(){const e=this.context?` | Context: ${JSON.stringify(this.context)}`:"";return`${this.name} [${this.code}]: ${this.message}${e}`}}l.Core=S,l.Devices=n,l.Dispatcher=f,l.SerialError=y,l.SerialErrorCode=C,l.Socket=d,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})})); +//# sourceMappingURL=webserial-core.umd.cjs.map diff --git a/dist/webserial-core.umd.cjs.map b/dist/webserial-core.umd.cjs.map new file mode 100644 index 0000000..8e407f7 --- /dev/null +++ b/dist/webserial-core.umd.cjs.map @@ -0,0 +1 @@ +{"version":3,"file":"webserial-core.umd.cjs","sources":["../lib/SerialEvent.ts","../lib/Dispatcher.ts","../lib/Devices.ts","../lib/utils.ts","../lib/Socket.ts","../lib/Core.ts","../lib/SerialError.ts"],"sourcesContent":["export class SerialEvent extends CustomEvent implements CustomEvent {\n constructor(type: string, options: CustomEventInit) {\n super(type, options);\n }\n}\n","import { SerialEvent } from \"./SerialEvent\";\n\ntype AvailableListener = { type: string; listening: boolean };\ntype AvailableListeners = AvailableListener[];\n\ntype DataType = string | number | boolean | object | null;\n\ninterface IDispatcher {\n dispatch(type: string, data?: DataType): void;\n\n dispatchAsync(type: string, data?: DataType, ms?: number): void;\n\n on(type: string, callback: EventListener): void;\n\n off(type: string, callback: EventListener): void;\n\n serialRegisterAvailableListener(type: string): void;\n\n availableListeners: AvailableListeners;\n}\n\ninterface Listeners {\n [key: string]: boolean;\n\n debug: boolean;\n}\n\nexport class Dispatcher extends EventTarget implements IDispatcher {\n __listeners__: Listeners = {\n debug: false,\n };\n __debug__: boolean = false;\n\n __listenersCallbacks__: { key: string; callback: EventListenerOrEventListenerObject }[] = [];\n\n /**\n * Dispatches an event with the specified type and data\n * @param type - The event type to dispatch\n * @param data - Optional data to attach to the event\n * @example\n * ```typescript\n * dispatcher.dispatch('connected', { port: 'COM3' });\n * ```\n */\n public dispatch(type: string, data: DataType = null) {\n const event = new SerialEvent(type, { detail: data });\n this.dispatchEvent(event);\n if (this.__debug__) {\n this.dispatchEvent(new SerialEvent(\"debug\", { detail: { type, data } }));\n }\n }\n\n /**\n * Dispatches an event asynchronously after a specified delay\n * @param type - The event type to dispatch\n * @param data - Optional data to attach to the event\n * @param ms - Delay in milliseconds (default: 100)\n * @example\n * ```typescript\n * dispatcher.dispatchAsync('timeout', { reason: 'no response' }, 500);\n * ```\n */\n public dispatchAsync(type: string, data = null, ms = 100) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1 = this;\n setTimeout(() => {\n this1.dispatch(type, data);\n }, ms);\n }\n\n /**\n * Registers an event listener for the specified event type\n * @param type - The event type to listen to\n * @param callback - The callback function to execute when the event is triggered\n * @example\n * ```typescript\n * dispatcher.on('connected', (event) => {\n * console.log('Device connected', event.detail);\n * });\n * ```\n */\n public on(type: string, callback: EventListenerOrEventListenerObject) {\n if (typeof this.__listeners__[type] !== \"undefined\" && !this.__listeners__[type]) {\n this.__listeners__[type] = true;\n }\n\n this.__listenersCallbacks__.push({ key: type, callback });\n this.addEventListener(type, callback);\n }\n\n /**\n * Removes an event listener for the specified event type\n * @param type - The event type to stop listening to\n * @param callback - The callback function to remove\n * @example\n * ```typescript\n * const handler = (event) => console.log(event.detail);\n * dispatcher.on('data', handler);\n * dispatcher.off('data', handler);\n * ```\n */\n public off(type: string, callback: EventListenerOrEventListenerObject) {\n this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((listener) => {\n return !(listener.key === type && listener.callback === callback);\n });\n\n this.removeEventListener(type, callback);\n }\n\n /**\n * Registers an available listener type for tracking\n * @param type - The event type to register\n * @internal\n */\n public serialRegisterAvailableListener(type: string) {\n if (this.__listeners__[type]) return;\n\n this.__listeners__[type] = false;\n }\n\n /**\n * Gets the list of all available listeners and their state\n * @returns Array of listener objects with type and listening status\n * @example\n * ```typescript\n * const listeners = dispatcher.availableListeners;\n * console.log(listeners); // [{ type: 'connected', listening: true }, ...]\n * ```\n */\n get availableListeners(): AvailableListeners {\n const keys = Object.keys(this.__listeners__).sort();\n return keys.map((type): AvailableListener => {\n return {\n type,\n listening: this.__listeners__[type],\n };\n });\n }\n\n /**\n * Removes all event listeners except internal ones (like queue listeners)\n * Resets all listener states to false\n * @example\n * ```typescript\n * dispatcher.removeAllListeners();\n * ```\n */\n public removeAllListeners(): void {\n for (const listener of this.__listenersCallbacks__) {\n if ([\"internal:queue\"].includes(listener.key)) continue; // Skip queue listener\n\n this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((l) => {\n return !(l.key === listener.key && l.callback === listener.callback);\n });\n this.removeEventListener(listener.key, listener.callback);\n }\n for (const key of Object.keys(this.__listeners__)) {\n this.__listeners__[key] = false;\n }\n }\n}\n","import { Core } from \"./Core\";\nimport { Dispatcher } from \"./Dispatcher\";\n\ninterface IDevice {\n [key: string]: Core;\n}\n\ninterface IDevices {\n [key: string]: IDevice;\n}\n\n/**\n * Manages and tracks all serial devices in the application\n * Provides a centralized registry for device instances\n * @extends Dispatcher\n */\nexport class Devices extends Dispatcher {\n static instance: Devices;\n static devices: IDevices = {};\n\n constructor() {\n super();\n\n const availableListeners: string[] = [\"change\"];\n\n availableListeners.forEach((event: string): void => {\n this.serialRegisterAvailableListener(event);\n });\n }\n\n public static $dispatchChange(device: Core | null = null): void {\n if (device) {\n device.$checkAndDispatchConnection();\n }\n Devices.instance.dispatch(\"change\", { devices: Devices.devices, dispatcher: device });\n }\n\n public static typeError(type: string): void {\n const error = new Error();\n error.message = `Type ${type} is not supported`;\n error.name = \"DeviceTypeError\";\n throw error;\n }\n\n /**\n * Registers a new device type in the registry\n * @param type - The type name of the device (e.g., 'arduino', 'esp32')\n * @internal\n */\n public static registerType(type: string): void {\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.devices = { ...Devices.devices, [type]: {} };\n }\n }\n\n /**\n * Adds a device to the registry\n * @param device - The Core device instance to add\n * @returns The index of the device in its type registry\n * @throws {Error} If device with the same ID already exists\n * @example\n * ```typescript\n * const arduino = new Arduino();\n * Devices.add(arduino);\n * ```\n */\n public static add(device: Core): number {\n const type = device.typeDevice;\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.registerType(type);\n }\n\n const id: string = device.uuid;\n\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n if (Devices.devices[type][id]) {\n throw new Error(`Device with id ${id} already exists`);\n }\n\n Devices.devices[type][id] = device;\n\n Devices.$dispatchChange(device);\n return Object.keys(Devices.devices[type]).indexOf(id);\n }\n\n /**\n * Gets a specific device by type and UUID\n * @param type - The device type\n * @param id - The device UUID\n * @returns The device instance\n * @throws {Error} If the device type is not supported\n * @example\n * ```typescript\n * const device = Devices.get('arduino', 'uuid-123');\n * ```\n */\n public static get(type: string, id: string): Core {\n if (typeof Devices.devices[type] === \"undefined\") {\n Devices.registerType(type);\n }\n\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n return Devices.devices[type][id];\n }\n\n public static getAll(type: string | null = null): IDevice | IDevices {\n if (type === null) return Devices.devices;\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n return Devices.devices[type];\n }\n\n public static getList(): Core[] {\n // get all devices in list mode no matter the type\n // by some reason the array is empty so we need to use Object.values and map\n const devices: IDevice[] = Object.values(Devices.devices);\n return devices\n .map((device: IDevice): Core[] => {\n return Object.values(device);\n })\n .flat();\n }\n\n public static getByNumber(type: string, device_number: number): Core | null {\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n const devices = Object.values(Devices.devices[type]);\n return devices.find((device) => device.deviceNumber === device_number) ?? null;\n }\n\n public static getCustom(type: string, device_number: number = 1): Core | null {\n if (typeof Devices.devices[type] === \"undefined\") Devices.typeError(type);\n\n const devices = Object.values(Devices.devices[type]);\n return devices.find((device) => device.deviceNumber === device_number) ?? null;\n }\n\n public static async connectToAll(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (device.isConnected) continue;\n await device.connect().catch(console.warn);\n }\n\n return Promise.resolve(Devices.areAllConnected());\n }\n\n public static async disconnectAll(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (device.isDisconnected) continue;\n await device.disconnect().catch(console.warn);\n }\n\n return Promise.resolve(Devices.areAllDisconnected());\n }\n\n public static async areAllConnected(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (!device.isConnected) return Promise.resolve(false);\n }\n\n return Promise.resolve(true);\n }\n\n public static async areAllDisconnected(): Promise {\n const devices: Core[] = Devices.getList();\n\n for (const device of devices) {\n if (!device.isDisconnected) return Promise.resolve(false);\n }\n\n return Promise.resolve(true);\n }\n\n public static async getAllConnected(): Promise {\n const devices: Core[] = Devices.getList();\n return Promise.resolve(devices.filter((device: Core): boolean => device.isConnected));\n }\n\n public static async getAllDisconnected(): Promise {\n const devices: Core[] = Devices.getList();\n return Promise.resolve(devices.filter((device: Core): boolean => device.isDisconnected));\n }\n}\n\nif (!Devices.instance) {\n Devices.instance = new Devices();\n}\n","type empty = void | PromiseLike;\n\nexport function wait(ms: number = 100): Promise {\n return new Promise(\n (resolve: (value: empty) => void): ReturnType => setTimeout((): void => resolve(), ms),\n );\n}\n\n/*\n * @deprecated This function is deprecated and will be removed in future versions.\n */\nexport function supportWebSerial(): boolean {\n return \"serial\" in navigator;\n}\n","import { io, ManagerOptions, SocketOptions, Socket as SocketIOClient } from \"socket.io-client\";\nimport { Devices } from \"./Devices\";\nimport { Core } from \"./Core\";\n\ninterface SocketResponseData {\n name: string;\n uuid: string;\n deviceNumber: number;\n [key: string]: unknown;\n}\n\ntype BoundedFunction = {\n onResponse: (data: SocketResponseData) => void;\n};\n\nclass MySocket {\n #uri: string = \"http://localhost:3000\";\n #options: Partial = {\n transports: [\"websocket\"],\n };\n #socket: SocketIOClient | null = null;\n #connected: boolean = false;\n\n #boundedFun: BoundedFunction;\n\n constructor() {\n this.#boundedFun = {\n onResponse: this.onResponse.bind(this),\n };\n }\n\n set uri(uri: string) {\n const url = new URL(uri);\n\n if (![\"http:\", \"https:\", \"ws:\", \"wss:\"].includes(url.protocol)) {\n throw new Error(\"URI must start with http://, https://, ws://, or wss://\");\n }\n this.#uri = uri;\n }\n\n get uri(): string {\n return this.#uri;\n }\n\n set options(options: Partial) {\n if (typeof options !== \"object\") {\n throw new Error(\"Options must be an object\");\n }\n this.#options = options;\n }\n\n get options(): Partial {\n return this.#options;\n }\n\n disconnect() {\n if (this.#socket) {\n this.#socket.off(\"response\", this.#boundedFun.onResponse);\n\n this.#socket.disconnect();\n this.#socket = null;\n }\n this.#connected = false;\n }\n\n prepare() {\n if (this.#connected) return;\n\n this.#socket = io(this.#uri, this.#options);\n this.#connected = true;\n\n this.#socket.on(\"response\", this.#boundedFun.onResponse);\n }\n\n connectDevice(config: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"connectDevice\", { config });\n }\n\n disconnectDevice(config: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"disconnectDevice\", { config });\n }\n\n disconnectAllDevices(): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"disconnectAll\");\n }\n\n write(data: object): void {\n if (!this.#socket) {\n throw new Error(\"Socket not connected. Call prepare() first.\");\n }\n this.#socket.emit(\"cmd\", data);\n }\n\n onResponse(data: SocketResponseData): void {\n let device: Core | null = Devices.get(data.name, data.uuid);\n if (!device) {\n device = Devices.getByNumber(data.name, data.deviceNumber);\n }\n if (!device) {\n return;\n }\n device.socketResponse(data);\n }\n}\n\nexport const Socket = new MySocket();\n","import { Dispatcher } from \"./Dispatcher\";\nimport { Devices } from \"./Devices\";\nimport { wait } from \"./utils\";\nimport { Socket } from \"./Socket\";\n\ninterface LastError {\n message: string | null;\n action: string | null;\n code: string | Uint8Array | Array | Array | null | number;\n no_code: number;\n}\n\ninterface DeviceData {\n type: string;\n id: string;\n listen_on_port: number | null;\n}\n\ntype SerialResponseAs = \"hex\" | \"uint8\" | \"string\" | \"arraybuffer\";\n\ninterface SerialResponse {\n length: number | null;\n buffer: Uint8Array;\n as: SerialResponseAs;\n replacer: RegExp | string;\n limiter: null | string | RegExp;\n prefixLimiter: boolean; // If true, the limiter is at the beginning of the message\n sufixLimiter: boolean; // If true, the limiter is at the end of the message\n delimited: boolean;\n}\n\ninterface QueueData {\n bytes: string | Uint8Array | Array | Array;\n action: string;\n}\n\ntype ParserSocketPort = {\n name: \"byte-length\" | \"inter-byte-timeout\";\n length?: number; // Length of each byte in the response, only for byte-length\n interval?: number; // Interval in milliseconds for inter-byte-timeout\n};\n\ntype PortInfo = {\n path: string | null;\n vendorId: number | string | null;\n productId: number | string | null;\n parser: ParserSocketPort;\n};\n\ntype SerialData = {\n socket: boolean;\n portInfo: PortInfo;\n aux_connecting: string;\n connecting: boolean;\n connected: boolean;\n port: SerialPort | null;\n last_action: string | null;\n response: SerialResponse;\n reader: ReadableStreamDefaultReader | null;\n input_done: Promise | null;\n output_done: Promise | null;\n input_stream: ReadableStream | null;\n output_stream: WritableStream | null;\n keep_reading: boolean;\n time_until_send_bytes: number | undefined | ReturnType;\n delay_first_connection: number;\n bytes_connection: string | Uint8Array | string[] | number[] | null;\n filters: SerialPortFilter[];\n config_port: SerialOptions;\n queue: QueueData[];\n running_queue: boolean;\n auto_response: any;\n free_timeout_ms: number;\n useRTSCTS: boolean;\n};\n\ninterface TimeResponse {\n response_connection: number;\n response_engines: number;\n response_general: number;\n}\n\ninterface Timeout {\n until_response: number | ReturnType;\n}\n\ninterface InternalIntervals {\n reconnection: number;\n}\n\nexport type Internal = {\n bypassSerialBytesConnection: boolean;\n auto_response: boolean;\n device_number: number;\n aux_port_connector: number;\n last_error: LastError;\n serial: SerialData;\n device: DeviceData;\n time: TimeResponse;\n timeout: Timeout;\n interval: InternalIntervals;\n};\n\ninterface CoreConstructorParams {\n filters?: SerialPortFilter[] | null;\n config_port?: SerialOptions;\n no_device?: number;\n device_listen_on_channel?: number | string;\n bypassSerialBytesConnection?: boolean;\n socket?: boolean;\n}\n\nconst defaultConfigPort: SerialOptions = {\n baudRate: 9600,\n dataBits: 8,\n stopBits: 1,\n parity: \"none\",\n bufferSize: 32768,\n flowControl: \"none\",\n};\n\ninterface CustomCode {\n code: string | Uint8Array | Array | Array;\n}\n\ninterface ICore {\n lastAction: string | null;\n\n set listenOnChannel(channel: string | number);\n\n set serialFilters(filters: SerialPortFilter[]);\n\n get serialFilters(): SerialPortFilter[];\n\n set serialConfigPort(config_port: SerialOptions);\n\n get serialConfigPort(): SerialOptions;\n\n get isConnected(): boolean;\n\n get isConnecting(): boolean;\n\n get isDisconnected(): boolean;\n\n get useRTSCTS(): boolean;\n\n set useRTSCTS(value: boolean);\n\n get deviceNumber(): number;\n\n get uuid(): string;\n\n get typeDevice(): string;\n\n get queue(): QueueData[];\n\n get timeoutBeforeResponseBytes(): number;\n\n set timeoutBeforeResponseBytes(value: number);\n\n get fixedBytesMessage(): number | null;\n\n set fixedBytesMessage(length: number | null);\n\n get responseDelimited(): boolean;\n\n set responseDelimited(value: boolean);\n\n get responsePrefixLimited(): boolean;\n\n set responsePrefixLimited(value: boolean);\n\n get responseSufixLimited(): boolean;\n\n set responseSufixLimited(value: boolean);\n\n get responseLimiter(): string | RegExp | null;\n\n set responseLimiter(limiter: string | RegExp | null);\n\n get bypassSerialBytesConnection(): boolean;\n\n set bypassSerialBytesConnection(value: boolean);\n\n timeout(bytes: string[], event: string): Promise;\n\n disconnect(detail?: null): Promise;\n\n connect(): Promise;\n\n serialDisconnect(): Promise;\n\n serialPortsSaved(ports: SerialPort[]): Promise;\n\n serialErrors(error: unknown | Error | DOMException): void;\n\n serialConnect(): Promise;\n\n serialForget(): Promise;\n\n decToHex(dec: number | string): string;\n\n hexToDec(hex: string): number;\n\n hexMaker(val?: string, min?: number): string;\n\n add0x(bytes: string[]): string[];\n\n bytesToHex(bytes: string[]): string[];\n\n appendToQueue(arr: string[], action: string): Promise;\n\n serialSetConnectionConstant(listen_on_port?: number): string | Uint8Array | string[] | number[] | null;\n\n serialMessage(code: string[]): void;\n\n serialCorruptMessage(data: Uint8Array | number[] | string[] | never | null | string | ArrayBuffer): void;\n\n clearSerialQueue(): void;\n\n sumHex(arr: string[]): string;\n\n softReload(): void;\n\n sendConnect(): Promise;\n\n sendCustomCode(customCode: CustomCode): Promise;\n\n stringToArrayHex(string: string): string[];\n\n stringToArrayBuffer(string: string, end: string): ArrayBufferLike;\n\n parseStringToBytes(string: string, end: string): string[];\n\n parseUint8ToHex(array: Uint8Array): string[];\n\n parseHexToUint8(array: string[]): Uint8Array;\n\n stringArrayToUint8Array(strings: string[]): Uint8Array;\n\n parseUint8ArrayToString(array: string[]): string;\n\n parseStringToTextEncoder(string: string, end: string): Uint8Array;\n\n hexToAscii(hex: string | number): string;\n\n asciiToHex(asciiString: string): string;\n\n getResponseAsArrayBuffer(): void;\n\n getResponseAsArrayHex(): void;\n\n getResponseAsUint8Array(): void;\n\n getResponseAsString(): void;\n}\n\nexport class Core extends Dispatcher implements ICore {\n protected __internal__: Internal = {\n bypassSerialBytesConnection: false,\n auto_response: false,\n device_number: 1,\n aux_port_connector: 0,\n last_error: {\n message: null,\n action: null,\n code: null,\n no_code: 0,\n },\n serial: {\n socket: false,\n portInfo: {\n path: null,\n vendorId: null,\n productId: null,\n parser: {\n name: \"inter-byte-timeout\",\n interval: 50,\n },\n },\n aux_connecting: \"idle\",\n connecting: false,\n connected: false,\n port: null,\n last_action: null,\n response: {\n length: null,\n buffer: new Uint8Array([]),\n as: \"uint8\",\n replacer: /[\\n\\r]+/g,\n limiter: null,\n prefixLimiter: false,\n sufixLimiter: true,\n delimited: false,\n },\n reader: null,\n input_done: null,\n output_done: null,\n input_stream: null,\n output_stream: null,\n keep_reading: true,\n time_until_send_bytes: undefined,\n delay_first_connection: 200,\n bytes_connection: null,\n filters: [],\n config_port: defaultConfigPort,\n queue: [],\n running_queue: false,\n auto_response: null,\n free_timeout_ms: 50, // In previous versions 400 was used\n useRTSCTS: false, // Use RTS/CTS flow control\n },\n device: {\n type: \"unknown\",\n id: window.crypto.randomUUID(),\n listen_on_port: null,\n },\n time: {\n response_connection: 500,\n response_engines: 2e3,\n response_general: 2e3,\n },\n timeout: {\n until_response: 0,\n },\n interval: {\n reconnection: 0,\n },\n };\n\n #boundFinishConnecting: EventListenerOrEventListenerObject | null = null;\n\n constructor(\n {\n filters = null,\n config_port = defaultConfigPort,\n no_device = 1,\n device_listen_on_channel = 1,\n bypassSerialBytesConnection = false,\n socket = false,\n }: CoreConstructorParams = {\n filters: null,\n config_port: defaultConfigPort,\n no_device: 1,\n device_listen_on_channel: 1,\n bypassSerialBytesConnection: false,\n socket: false,\n },\n ) {\n super();\n\n if (!(\"serial\" in navigator)) {\n throw new Error(\"Web Serial not supported\");\n }\n\n if (filters) {\n this.serialFilters = filters;\n }\n\n if (config_port) {\n this.serialConfigPort = config_port;\n }\n\n if (bypassSerialBytesConnection) {\n this.__internal__.bypassSerialBytesConnection = bypassSerialBytesConnection;\n }\n\n if (no_device) {\n this.#serialSetBytesConnection(no_device);\n }\n\n if (device_listen_on_channel && [\"number\", \"string\"].includes(typeof device_listen_on_channel)) {\n this.listenOnChannel = device_listen_on_channel;\n }\n\n this.__internal__.serial.socket = socket;\n\n this.#registerDefaultListeners();\n this.#internalEvents();\n }\n\n set listenOnChannel(channel: string | number) {\n if (typeof channel === \"string\") {\n channel = parseInt(channel);\n }\n if (isNaN(channel) || channel < 1 || channel > 255) {\n throw new Error(\"Invalid port number\");\n }\n this.__internal__.device.listen_on_port = channel;\n if (this.__internal__.bypassSerialBytesConnection) return;\n this.__internal__.serial.bytes_connection = this.serialSetConnectionConstant(channel);\n }\n\n get lastAction(): string | null {\n return this.__internal__.serial.last_action;\n }\n\n get listenOnChannel(): number {\n return this.__internal__.device.listen_on_port ?? 1;\n }\n\n set serialFilters(filters: SerialPortFilter[]) {\n if (this.isConnected) throw new Error(\"Cannot change serial filters while connected\");\n this.__internal__.serial.filters = filters;\n }\n\n get serialFilters(): SerialPortFilter[] {\n return this.__internal__.serial.filters;\n }\n\n set serialConfigPort(config_port: SerialOptions) {\n if (this.isConnected) throw new Error(\"Cannot change serial filters while connected\");\n this.__internal__.serial.config_port = config_port;\n }\n\n get serialConfigPort(): SerialOptions {\n return this.__internal__.serial.config_port;\n }\n\n get useRTSCTS(): boolean {\n return this.__internal__.serial.useRTSCTS;\n }\n\n set useRTSCTS(value: boolean) {\n this.__internal__.serial.useRTSCTS = value;\n }\n\n get isConnected(): boolean {\n const prevConnected = this.__internal__.serial.connected;\n const connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n if (prevConnected && !connected) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n }\n this.__internal__.serial.connected = connected;\n return this.__internal__.serial.connected;\n }\n\n get isConnecting(): boolean {\n return this.__internal__.serial.connecting;\n }\n\n get isDisconnected(): boolean {\n const prevConnected = this.__internal__.serial.connected;\n const connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n if (!prevConnected && connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n Devices.$dispatchChange(this);\n }\n this.__internal__.serial.connected = connected;\n return !this.__internal__.serial.connected;\n }\n\n get deviceNumber(): number {\n return this.__internal__.device_number;\n }\n\n get uuid(): string {\n return this.__internal__.device.id;\n }\n\n get typeDevice(): string {\n return this.__internal__.device.type;\n }\n\n get queue(): QueueData[] {\n return this.__internal__.serial.queue;\n }\n\n get responseDelimited(): boolean {\n return this.__internal__.serial.response.delimited;\n }\n\n set responseDelimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responseDelimited must be a boolean\");\n }\n this.__internal__.serial.response.delimited = value;\n }\n\n get responsePrefixLimited(): boolean {\n return this.__internal__.serial.response.prefixLimiter;\n }\n\n set responsePrefixLimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responsePrefixLimited must be a boolean\");\n }\n this.__internal__.serial.response.prefixLimiter = value;\n }\n\n get responseSufixLimited(): boolean {\n return this.__internal__.serial.response.sufixLimiter;\n }\n\n set responseSufixLimited(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"responseSufixLimited must be a boolean\");\n }\n this.__internal__.serial.response.sufixLimiter = value;\n }\n\n get responseLimiter(): string | RegExp | null {\n return this.__internal__.serial.response.limiter;\n }\n\n set responseLimiter(limiter: string | RegExp | null) {\n if (typeof limiter !== \"string\" && !(limiter instanceof RegExp)) {\n throw new Error(\"responseLimiter must be a string or a RegExp\");\n }\n\n this.__internal__.serial.response.limiter = limiter;\n }\n\n get fixedBytesMessage(): number | null {\n return this.__internal__.serial.response.length;\n }\n\n set fixedBytesMessage(length: number | null) {\n if (length !== null && (typeof length !== \"number\" || length < 1)) {\n throw new Error(\"Invalid length for fixed bytes message\");\n }\n this.__internal__.serial.response.length = length;\n }\n\n get timeoutBeforeResponseBytes(): number {\n return this.__internal__.serial.free_timeout_ms || 50;\n }\n\n set timeoutBeforeResponseBytes(value: number) {\n if (value !== undefined && (typeof value !== \"number\" || value < 1)) {\n throw new Error(\"Invalid timeout for response bytes\");\n }\n this.__internal__.serial.free_timeout_ms = value ?? 50;\n }\n\n get bypassSerialBytesConnection(): boolean {\n return this.__internal__.bypassSerialBytesConnection;\n }\n\n set bypassSerialBytesConnection(value: boolean) {\n if (typeof value !== \"boolean\") {\n throw new Error(\"bypassSerialBytesConnection must be a boolean\");\n }\n this.__internal__.bypassSerialBytesConnection = value;\n }\n\n get useSocket(): boolean {\n return this.__internal__.serial.socket;\n }\n\n get connectionBytes(): Uint8Array {\n const bytes = this.__internal__.serial.bytes_connection;\n\n if (bytes instanceof Uint8Array) {\n return bytes;\n }\n\n if (typeof bytes === \"string\") {\n return this.stringArrayToUint8Array(this.parseStringToBytes(bytes, \"\"));\n }\n\n if (Array.isArray(bytes) && typeof bytes[0] === \"string\") {\n return this.stringArrayToUint8Array(bytes as string[]);\n }\n\n if (Array.isArray(bytes) && typeof bytes[0] === \"number\") {\n return new Uint8Array(bytes as number[]);\n }\n\n return new Uint8Array([]);\n }\n\n set portPath(path: string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port path while connected\");\n if (typeof path !== \"string\" && path !== null) {\n throw new TypeError(\"vendorId must be string or null\");\n }\n this.__internal__.serial.portInfo.path = path;\n }\n\n get portPath(): string | null {\n return this.__internal__.serial.portInfo.path;\n }\n\n set portVendorId(vendorId: number | string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port vendorId while connected\");\n if (typeof vendorId! == \"number\" && typeof vendorId !== \"string\" && vendorId !== null) {\n throw new TypeError(\"vendorId must be a number, string or null\");\n }\n this.__internal__.serial.portInfo.vendorId = vendorId;\n }\n\n get portVendorId(): number | string | null {\n return this.__internal__.serial.portInfo.vendorId;\n }\n\n set portProductId(productId: number | string | null) {\n if (this.isConnected) throw new Error(\"Cannot change port productId while connected\");\n if (typeof productId! == \"number\" && typeof productId !== \"string\" && productId !== null) {\n throw new TypeError(\"productId must be a number, string or null\");\n }\n this.__internal__.serial.portInfo.productId = productId;\n }\n\n get portProductId(): number | string | null {\n return this.__internal__.serial.portInfo.productId;\n }\n\n set socketPortParser(string: \"byte-length\" | \"inter-byte-timeout\") {\n if ([\"byte-length\", \"inter-byte-timeout\"].includes(string)) {\n throw new TypeError(\"socketPortParser must be a string, either 'byte-length' or 'inter-byte-timeout'\");\n }\n this.__internal__.serial.portInfo.parser.name = string;\n }\n\n get socketPortParser(): \"byte-length\" | \"inter-byte-timeout\" {\n return this.__internal__.serial.portInfo.parser.name;\n }\n\n set socketPortParserInterval(value: number) {\n if (typeof value !== \"number\" || value < 1) {\n throw new TypeError(\"Interval must be a positive number\");\n }\n\n this.__internal__.serial.portInfo.parser.interval = value;\n }\n\n get socketPortParserInterval(): number {\n return this.__internal__.serial.portInfo.parser.interval || 50;\n }\n\n set socketPortParserLength(value: number) {\n if (typeof value !== \"number\" || value < 1) {\n throw new TypeError(\"Length must be a positive number or null\");\n }\n this.__internal__.serial.portInfo.parser.length = value;\n }\n\n get socketPortParserLength(): number {\n return this.__internal__.serial.portInfo.parser.length || 14;\n }\n\n get parserForSocket() {\n if (this.socketPortParser === \"byte-length\") {\n return {\n name: this.socketPortParser,\n length: this.socketPortParserLength,\n };\n }\n return {\n name: this.socketPortParser,\n interval: this.socketPortParserInterval,\n };\n }\n\n get configDeviceSocket(): object {\n return {\n uuid: this.uuid,\n name: this.typeDevice,\n deviceNumber: this.deviceNumber,\n connectionBytes: Array.from(this.connectionBytes),\n config: {\n baudRate: this.__internal__.serial.config_port.baudRate,\n dataBits: this.__internal__.serial.config_port.dataBits,\n stopBits: this.__internal__.serial.config_port.stopBits,\n parity: this.__internal__.serial.config_port.parity,\n bufferSize: this.__internal__.serial.config_port.bufferSize,\n flowControl: this.__internal__.serial.config_port.flowControl,\n },\n info: {\n vendorId: this.portVendorId, // vendor ID or null for auto-detect\n productId: this.portProductId, // product ID or null for auto-detect\n portName: this.portPath, // COM3, /dev/ttyUSB0, etc. null for auto-detect\n },\n response: {\n automatic: this.__internal__.auto_response, // true to auto-respond to commands this only for devices that doesn't respond nothing\n autoResponse: this.__internal__.serial.auto_response, // null or data to respond automatically, ie. [0x02, 0x06, 0xdd, 0xdd, 0xf0, 0xcf, 0x03] for relay\n parser: this.parserForSocket,\n timeout: {\n general: this.__internal__.time.response_general,\n engines: this.__internal__.time.response_engines,\n connection: this.__internal__.time.response_connection,\n },\n },\n };\n }\n\n #checkIfPortIsOpen(port: SerialPort | null): boolean {\n if (this.useSocket) return this.__internal__.serial.connected;\n\n return !!(port && port.readable && port.writable);\n }\n\n public async timeout(bytes: string | Uint8Array | Array | Array, event: string): Promise {\n this.__internal__.last_error.message = \"Operation response timed out.\";\n this.__internal__.last_error.action = event;\n this.__internal__.last_error.code = bytes;\n if (this.__internal__.timeout.until_response) {\n clearTimeout(this.__internal__.timeout.until_response);\n this.__internal__.timeout.until_response = 0;\n }\n if (event === \"connect\") {\n this.__internal__.serial.connected = false;\n this.dispatch(\"serial:reconnect\", {});\n Devices.$dispatchChange(this);\n } else if (event === \"connection:start\") {\n await this.serialDisconnect();\n this.__internal__.serial.connected = false;\n this.__internal__.aux_port_connector += 1;\n Devices.$dispatchChange(this);\n await this.serialConnect();\n }\n\n if (this.__internal__.serial.queue.length > 0) {\n this.dispatch(\"internal:queue\", {});\n }\n\n this.dispatch(\"serial:timeout\", {\n ...this.__internal__.last_error,\n bytes,\n action: event,\n });\n }\n\n public async disconnect(detail = null): Promise {\n await this.serialDisconnect();\n this.#disconnected(detail);\n }\n\n #disconnected(detail: object | null = null): void {\n this.__internal__.serial.connected = false;\n this.__internal__.aux_port_connector = 0;\n this.dispatch(\"serial:disconnected\", detail);\n Devices.$dispatchChange(this);\n }\n\n #onFinishConnecting(event: any): void {\n this.__internal__.serial.aux_connecting = event.detail.active ? \"connecting\" : \"finished\";\n }\n\n socketResponse(data: any) {\n const auxPrevConnected: boolean = this.__internal__.serial.connected;\n\n if (data.type === \"disconnect\" || (data.type === \"error\" && data.data === \"DISCONNECTED\")) {\n this.__internal__.serial.connected = false;\n } else if (data.type === \"success\") {\n this.__internal__.serial.connected = true;\n }\n\n Devices.$dispatchChange(this);\n if (!auxPrevConnected && this.__internal__.serial.connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n }\n\n // console.log(data, this.lastAction);\n if (data.type === \"success\") {\n this.#serialGetResponse(new Uint8Array(data.data));\n } else if (data.type === \"error\") {\n const error = new Error(\"The port is closed or is not readable/writable\");\n this.serialErrors(error);\n } else if (data.type === \"timeout\") {\n this.timeout(data.data.bytes ?? [], this.lastAction || \"unknown\");\n }\n\n this.__internal__.serial.last_action = null;\n }\n\n public async connect(): Promise {\n if (this.isConnected) {\n return true;\n }\n\n this.__internal__.serial.aux_connecting = \"idle\";\n\n return new Promise((resolve: (value: boolean) => void, reject: (reason: string) => void): void => {\n if (!this.#boundFinishConnecting) {\n this.#boundFinishConnecting = this.#onFinishConnecting.bind(this);\n }\n\n this.on(\"internal:connecting\", this.#boundFinishConnecting);\n\n const interval: ReturnType = setInterval((): void => {\n if (this.__internal__.serial.aux_connecting === \"finished\") {\n clearInterval(interval);\n this.__internal__.serial.aux_connecting = \"idle\";\n if (null !== this.#boundFinishConnecting) {\n this.off(\"internal:connecting\", this.#boundFinishConnecting);\n }\n\n if (this.isConnected) {\n resolve(true);\n } else {\n reject(`${this.typeDevice} device ${this.deviceNumber} not connected`);\n }\n } else if (this.__internal__.serial.aux_connecting === \"connecting\") {\n this.__internal__.serial.aux_connecting = \"idle\";\n this.dispatch(\"internal:connecting\", { active: true });\n this.dispatch(\"serial:connecting\", { active: true });\n }\n }, 100);\n\n this.serialConnect();\n });\n }\n\n public async serialDisconnect(): Promise {\n try {\n if (this.useSocket) {\n Socket.disconnectDevice(this.configDeviceSocket);\n } else {\n const reader: ReadableStreamDefaultReader | null = this.__internal__.serial.reader;\n const output_stream: WritableStream | null = this.__internal__.serial.output_stream;\n if (reader) {\n const reader_promise: Promise = reader.cancel();\n await reader_promise.catch((err: unknown): void => this.serialErrors(err));\n await this.__internal__.serial.input_done;\n }\n\n if (output_stream) {\n await output_stream.getWriter().close();\n await this.__internal__.serial.output_done;\n }\n\n if (this.__internal__.serial.connected && this.__internal__.serial && this.__internal__.serial.port) {\n await this.__internal__.serial.port.close();\n }\n }\n } catch (err: unknown) {\n this.serialErrors(err);\n } finally {\n this.__internal__.serial.reader = null;\n this.__internal__.serial.input_done = null;\n\n this.__internal__.serial.output_stream = null;\n this.__internal__.serial.output_done = null;\n\n this.__internal__.serial.connected = false;\n this.__internal__.serial.port = null;\n Devices.$dispatchChange(this);\n }\n }\n\n async #serialSocketWrite(data: string | Uint8Array | Array | Array): Promise {\n if (this.isDisconnected) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n throw new Error(\"The port is closed or is not readable/writable\");\n }\n\n const bytes: Uint8Array = this.validateBytes(data);\n Socket.write({ config: this.configDeviceSocket, bytes: Array.from(bytes) });\n }\n\n async #serialWrite(data: string | Uint8Array | Array | Array): Promise {\n if (this.useSocket) {\n await this.#serialSocketWrite(data);\n return;\n }\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port || (port && (!port.readable || !port.writable))) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n throw new Error(\"The port is closed or is not readable/writable\");\n }\n const bytes: Uint8Array = this.validateBytes(data);\n\n if (this.useRTSCTS) {\n await this.#waitForCTS(port, 5000);\n }\n\n if (port.writable === null) return; // never happens, it's already checked, but to suppress TS error\n const writer: WritableStreamDefaultWriter = port.writable.getWriter();\n await writer.write(bytes);\n writer.releaseLock();\n }\n\n async #waitForCTS(port: SerialPort, timeoutMs: number = 5000): Promise {\n const start = Date.now();\n while (true) {\n if (Date.now() - start > timeoutMs) {\n throw new Error(\"Timeout waiting for clearToSend signal\");\n }\n\n const { clearToSend } = await port.getSignals();\n if (clearToSend) return;\n await wait(100);\n }\n }\n\n #serialGetResponse(code: Uint8Array = new Uint8Array([]), corrupt: boolean = false) {\n if (code && code.length > 0) {\n const auxPrevConnected: boolean = this.__internal__.serial.connected;\n this.__internal__.serial.connected = this.#checkIfPortIsOpen(this.__internal__.serial.port);\n Devices.$dispatchChange(this);\n if (!auxPrevConnected && this.__internal__.serial.connected) {\n this.dispatch(\"serial:connected\");\n this.#connectingChange(false);\n }\n\n if (this.__internal__.interval.reconnection) {\n clearInterval(this.__internal__.interval.reconnection);\n this.__internal__.interval.reconnection = 0;\n }\n\n if (this.__internal__.timeout.until_response) {\n clearTimeout(this.__internal__.timeout.until_response);\n this.__internal__.timeout.until_response = 0;\n }\n\n if (this.__internal__.serial.response.as === \"hex\") {\n if (corrupt) {\n this.serialCorruptMessage(this.parseUint8ToHex(code));\n } else {\n this.serialMessage(this.parseUint8ToHex(code));\n }\n } else if (this.__internal__.serial.response.as === \"uint8\") {\n if (corrupt) {\n this.serialCorruptMessage(code);\n } else {\n this.serialMessage(code);\n }\n } else if (this.__internal__.serial.response.as === \"string\") {\n const str = this.parseUint8ArrayToString(code);\n if (this.__internal__.serial.response.limiter !== null) {\n const splited = str.split(this.__internal__.serial.response.limiter);\n for (const s in splited) {\n if (!splited[s]) continue;\n if (corrupt) {\n this.serialCorruptMessage(splited[s]);\n } else {\n this.serialMessage(splited[s]);\n }\n }\n } else {\n if (corrupt) {\n this.serialCorruptMessage(str);\n } else {\n this.serialMessage(str);\n }\n }\n } else {\n const arraybuffer: ArrayBuffer | ArrayBufferLike = this.stringToArrayBuffer(this.parseUint8ArrayToString(code));\n if (corrupt) {\n this.serialCorruptMessage(arraybuffer as ArrayBuffer);\n } else {\n this.serialMessage(arraybuffer as ArrayBuffer);\n }\n }\n }\n\n if (this.__internal__.serial.queue.length === 0) {\n this.__internal__.serial.running_queue = false;\n return;\n }\n this.dispatch(\"internal:queue\", {});\n }\n\n public getResponseAsArrayBuffer(): void {\n this.__internal__.serial.response.as = \"arraybuffer\";\n }\n\n public getResponseAsArrayHex(): void {\n this.__internal__.serial.response.as = \"hex\";\n }\n\n public getResponseAsUint8Array(): void {\n this.__internal__.serial.response.as = \"uint8\";\n }\n\n public getResponseAsString(): void {\n this.__internal__.serial.response.as = \"string\";\n }\n\n async #serialPortsFiltered(): Promise {\n const filters: SerialPortFilter[] = this.serialFilters;\n // @ts-expect-error getPorts can use parameters\n const ports: SerialPort[] = await navigator.serial.getPorts({ filters });\n if (filters.length === 0) return ports;\n\n const filteredPorts: SerialPort[] = ports.filter((port: SerialPort): boolean => {\n const info: SerialPortInfo = port.getInfo();\n return filters.some((filter: SerialPortFilter): boolean => {\n return info.usbProductId === filter.usbProductId && info.usbVendorId === filter.usbVendorId;\n });\n });\n\n // return only ports that are not open\n return filteredPorts.filter((port: SerialPort): boolean => !this.#checkIfPortIsOpen(port));\n }\n\n public async serialPortsSaved(ports: SerialPort[]): Promise {\n const filters: SerialPortFilter[] = this.serialFilters;\n if (this.__internal__.aux_port_connector < ports.length) {\n const aux = this.__internal__.aux_port_connector;\n this.__internal__.serial.port = ports[aux];\n } else {\n this.__internal__.aux_port_connector = 0;\n this.__internal__.serial.port = await navigator.serial.requestPort({\n filters,\n });\n }\n if (!this.__internal__.serial.port) {\n throw new Error(\"Select another port please\");\n }\n }\n\n public serialErrors(error: any): void {\n const err = error.toString().toLowerCase();\n switch (true) {\n case err.includes(\"must be handling a user gesture to show a permission request\"):\n case err.includes(\"the port is closed.\"):\n case err.includes(\"the port is closed or is not writable\"):\n case err.includes(\"the port is closed or is not readable\"):\n case err.includes(\"the port is closed or is not readable/writable\"):\n case err.includes(\"select another port please\"):\n case err.includes(\"no port selected by the user\"):\n case err.includes(\n \"this readable stream reader has been released and cannot be used to cancel its previous owner stream\",\n ):\n this.dispatch(\"serial:need-permission\", {});\n Devices.$dispatchChange(this);\n break;\n case err.includes(\"the port is already open.\"):\n case err.includes(\"failed to open serial port\"):\n this.serialDisconnect().then(async () => {\n this.__internal__.aux_port_connector += 1;\n await this.serialConnect();\n });\n break;\n case err.includes(\"cannot read properties of undefined (reading 'writable')\"):\n case err.includes(\"cannot read properties of null (reading 'writable')\"):\n case err.includes(\"cannot read property 'writable' of null\"):\n case err.includes(\"cannot read property 'writable' of undefined\"):\n this.serialDisconnect().then(async () => {\n await this.serialConnect();\n });\n break;\n case err.includes(\"'close' on 'serialport': a call to close() is already in progress.\"):\n // ... do something?\n break;\n case err.includes(\"failed to execute 'open' on 'serialport': a call to open() is already in progress.\"):\n // ... do something?\n break;\n case err.includes(\"the port is already closed.\"):\n // ... do something?\n break;\n case err.includes(\"the device has been lost\"):\n this.dispatch(\"serial:lost\", {});\n Devices.$dispatchChange(this);\n // dispatch event\n break;\n case err.includes(\"navigator.serial is undefined\"):\n this.dispatch(\"serial:unsupported\", {});\n // dispatch event\n break;\n default:\n // unhandled error\n console.error(error);\n break;\n }\n\n this.dispatch(\"serial:error\", error);\n }\n\n #appendBuffer(arraybuffer: Uint8Array | ArrayBuffer | null): void {\n if (arraybuffer) {\n const incoming: Uint8Array = this.__internal__.serial.response.buffer;\n const tmp: Uint8Array = new Uint8Array(incoming.length + arraybuffer.byteLength);\n tmp.set(incoming, 0);\n tmp.set(new Uint8Array(arraybuffer), incoming.length);\n this.__internal__.serial.response.buffer = tmp;\n }\n }\n\n async #freeSerialLoop(): Promise {\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n if (this.__internal__.serial.response.buffer) {\n this.#serialGetResponse(this.__internal__.serial.response.buffer);\n }\n\n this.__internal__.serial.response.buffer = new Uint8Array(0);\n }, this.__internal__.serial.free_timeout_ms || 50);\n }\n\n async #slicedSerialLoop(): Promise {\n const expectedLength = this.__internal__.serial.response.length;\n let buffer = this.__internal__.serial.response.buffer;\n\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n if (expectedLength === null || !buffer || buffer.length === 0) return;\n\n while (buffer.length >= expectedLength) {\n const message = buffer.slice(0, expectedLength);\n this.#serialGetResponse(message);\n\n buffer = buffer.slice(expectedLength);\n }\n this.__internal__.serial.response.buffer = buffer;\n\n if (buffer.length > 0) {\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n this.#serialGetResponse(this.__internal__.serial.response.buffer, true);\n }, this.__internal__.serial.free_timeout_ms || 50);\n }\n }\n\n async #delimitedSerialLoop(): Promise {\n const {\n limiter,\n prefixLimiter = false,\n sufixLimiter = true,\n }: {\n limiter: string | RegExp | null;\n prefixLimiter?: boolean;\n sufixLimiter?: boolean;\n } = this.__internal__.serial.response;\n\n if (!limiter) {\n throw new Error(\"No limiter defined for delimited serial response\");\n }\n\n const buffer = this.__internal__.serial.response.buffer;\n\n if (!limiter || !buffer || buffer.length === 0) return;\n\n if (this.__internal__.serial.time_until_send_bytes) {\n clearTimeout(this.__internal__.serial.time_until_send_bytes);\n this.__internal__.serial.time_until_send_bytes = 0;\n }\n\n const decoder = new TextDecoder();\n let decoded = decoder.decode(buffer);\n const messages: Uint8Array[] = [];\n\n if (typeof limiter === \"string\") {\n let pattern: RegExp;\n if (prefixLimiter && sufixLimiter) {\n pattern = new RegExp(`${limiter}([^${limiter}]+)${limiter}`, \"g\");\n } else if (prefixLimiter) {\n pattern = new RegExp(`${limiter}([^${limiter}]*)`, \"g\");\n } else if (sufixLimiter) {\n pattern = new RegExp(`([^${limiter}]+)${limiter}`, \"g\");\n } else {\n return;\n }\n\n let match;\n let lastIndex = 0;\n while ((match = pattern.exec(decoded)) !== null) {\n messages.push(new TextEncoder().encode(match[1]));\n lastIndex = pattern.lastIndex;\n }\n\n decoded = decoded.slice(lastIndex);\n } else if (limiter instanceof RegExp) {\n let match;\n let lastIndex = 0;\n if (prefixLimiter && sufixLimiter) {\n const pattern = new RegExp(`${limiter.source}(.*?)${limiter.source}`, \"g\");\n while ((match = pattern.exec(decoded)) !== null) {\n messages.push(new TextEncoder().encode(match[1]));\n lastIndex = pattern.lastIndex;\n }\n } else if (sufixLimiter) {\n while ((match = limiter.exec(decoded)) !== null) {\n const end = match.index;\n const chunk = decoded.slice(lastIndex, end);\n messages.push(new TextEncoder().encode(chunk));\n lastIndex = limiter.lastIndex;\n }\n } else if (prefixLimiter) {\n const parts = decoded.split(limiter);\n parts.shift();\n for (const part of parts) {\n messages.push(new TextEncoder().encode(part));\n }\n decoded = \"\";\n }\n\n decoded = decoded.slice(lastIndex);\n }\n\n for (const msg of messages) {\n this.#serialGetResponse(msg);\n }\n\n const leftoverBytes = new TextEncoder().encode(decoded);\n this.__internal__.serial.response.buffer = leftoverBytes;\n\n if (leftoverBytes.length > 0) {\n this.__internal__.serial.time_until_send_bytes = setTimeout((): void => {\n this.#serialGetResponse(this.__internal__.serial.response.buffer, true);\n this.__internal__.serial.response.buffer = new Uint8Array(0);\n }, this.__internal__.serial.free_timeout_ms ?? 50);\n }\n }\n\n async #readSerialLoop(): Promise {\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port || !port.readable) throw new Error(\"Port is not readable\");\n\n const reader: ReadableStreamDefaultReader = port.readable.getReader();\n this.__internal__.serial.reader = reader;\n\n try {\n while (this.__internal__.serial.keep_reading) {\n const { value, done } = await reader.read();\n if (done) break;\n\n this.#appendBuffer(value);\n\n if (this.__internal__.serial.response.delimited) {\n await this.#delimitedSerialLoop();\n } else if (this.__internal__.serial.response.length === null) {\n await this.#freeSerialLoop();\n } else {\n await this.#slicedSerialLoop();\n }\n }\n } catch (err: unknown) {\n this.serialErrors(err);\n } finally {\n reader.releaseLock();\n this.__internal__.serial.keep_reading = true;\n\n if (this.__internal__.serial.port) {\n await this.__internal__.serial.port.close();\n }\n }\n }\n\n #connectingChange(value: boolean): void {\n if (value === this.__internal__.serial.connecting) return;\n\n this.__internal__.serial.connecting = value;\n this.dispatch(\"serial:connecting\", { active: value });\n this.dispatch(\"internal:connecting\", { active: value });\n }\n\n public async serialConnect(): Promise {\n try {\n this.#connectingChange(true);\n\n if (this.useSocket) {\n Socket.prepare();\n this.__internal__.serial.last_action = \"connect\";\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this.timeout(this.__internal__.serial.bytes_connection ?? [], \"connection:start\");\n }, this.__internal__.time.response_connection);\n Socket.connectDevice(this.configDeviceSocket);\n this.dispatch(\"serial:sent\", {\n action: \"connect\",\n bytes: this.__internal__.serial.bytes_connection,\n });\n } else {\n const ports: SerialPort[] = await this.#serialPortsFiltered();\n if (ports.length > 0) {\n await this.serialPortsSaved(ports);\n } else {\n const filters: SerialPortFilter[] = this.serialFilters;\n this.__internal__.serial.port = await navigator.serial.requestPort({\n filters,\n });\n }\n\n const port: SerialPort | null = this.__internal__.serial.port;\n if (!port) {\n throw new Error(\"No port selected by the user\");\n }\n await port.open(this.serialConfigPort);\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n port.onconnect = (event: Event): void => {\n // console.log(event);\n this1.dispatch(\"serial:connected\", event);\n this1.#connectingChange(false);\n Devices.$dispatchChange(this);\n if (this1.__internal__.serial.queue.length > 0) {\n this1.dispatch(\"internal:queue\", {});\n } else {\n this1.__internal__.serial.running_queue = false;\n }\n };\n port.ondisconnect = async (): Promise => {\n await this1.disconnect();\n };\n\n await wait(this.__internal__.serial.delay_first_connection);\n\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this1.timeout(this1.__internal__.serial.bytes_connection ?? [], \"connection:start\");\n }, this.__internal__.time.response_connection);\n\n this.__internal__.serial.last_action = \"connect\";\n await this.#serialWrite(this.__internal__.serial.bytes_connection ?? []);\n\n this.dispatch(\"serial:sent\", {\n action: \"connect\",\n bytes: this.__internal__.serial.bytes_connection,\n });\n\n if (this.__internal__.auto_response) {\n this.#serialGetResponse(this.__internal__.serial.auto_response);\n }\n await this.#readSerialLoop();\n }\n } catch (e: unknown) {\n this.#connectingChange(false);\n this.serialErrors(e);\n }\n }\n\n async #forget(): Promise {\n if (typeof window === \"undefined\") return false;\n\n if (\"serial\" in navigator && \"forget\" in SerialPort.prototype && this.__internal__.serial.port) {\n await this.__internal__.serial.port.forget();\n return true;\n }\n return false;\n }\n\n public async serialForget(): Promise {\n return await this.#forget();\n }\n\n public decToHex(dec: number | string): string {\n if (typeof dec === \"string\") {\n dec = parseInt(dec, 10);\n }\n return dec.toString(16);\n }\n\n public hexToDec(hex: string): number {\n return parseInt(hex, 16);\n }\n\n public hexMaker(val = \"00\", min = 2): string {\n return val.toString().padStart(min, \"0\").toLowerCase();\n }\n\n public add0x(bytes: string[]): string[] {\n const new_bytes: string[] = [];\n bytes.forEach((value: string, index: number): void => {\n new_bytes[index] = \"0x\" + value;\n });\n return new_bytes;\n }\n\n public bytesToHex(bytes: string[]): string[] {\n return this.add0x(Array.from(bytes, (byte: string): string => this.hexMaker(byte)));\n }\n\n #registerDefaultListeners(): void {\n const availableListeners: string[] = [\n \"serial:connected\",\n \"serial:connecting\",\n \"serial:reconnect\",\n \"serial:timeout\",\n \"serial:disconnected\",\n \"serial:sent\",\n \"serial:soft-reload\",\n \"serial:message\",\n \"serial:corrupt-message\",\n \"unknown\",\n \"serial:need-permission\",\n \"serial:lost\",\n \"serial:unsupported\",\n \"serial:error\",\n \"debug\",\n ];\n\n availableListeners.forEach((event: string): void => {\n this.serialRegisterAvailableListener(event);\n });\n }\n\n #internalEvents(): void {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n this.on(\"internal:queue\", async (): Promise => {\n await this1.#runQueue();\n });\n\n this.#browserEvents();\n }\n\n #browserEvents(): void {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const this1: this = this;\n navigator.serial.addEventListener(\"connect\", async (): Promise => {\n if (!this1.isDisconnected) return;\n await this1.serialConnect().catch((): void => {});\n });\n }\n\n async #runQueue(): Promise {\n if (!this.#checkIfPortIsOpen(this.__internal__.serial.port)) {\n this.#disconnected({ error: \"Port is closed, not readable or writable.\" });\n await this.serialConnect();\n return;\n }\n\n // check if something is waiting for a response, when response arrives, the queue will be processed\n if (this.__internal__.timeout.until_response) return;\n\n if (this.__internal__.serial.queue.length === 0) {\n this.__internal__.serial.running_queue = false;\n return;\n }\n this.__internal__.serial.running_queue = true;\n\n // first element in queue\n const first: QueueData = this.__internal__.serial.queue[0];\n let limit_response: number = this.__internal__.time.response_general;\n if (first.action === \"connect\") {\n limit_response = this.__internal__.time.response_connection;\n }\n\n this.__internal__.timeout.until_response = setTimeout(async (): Promise => {\n await this.timeout(first.bytes, first.action);\n }, limit_response);\n\n this.__internal__.serial.last_action = first.action ?? \"unknown\";\n await this.#serialWrite(first.bytes);\n\n this.dispatch(\"serial:sent\", {\n action: first.action,\n bytes: first.bytes,\n });\n\n if (this.__internal__.auto_response) {\n let bytes: Uint8Array = new Uint8Array(0);\n try {\n bytes = this.validateBytes(this.__internal__.serial.auto_response);\n } catch (e: unknown) {\n this.serialErrors(e);\n }\n\n this.#serialGetResponse(bytes);\n }\n const copy_queue: QueueData[] = [...this.__internal__.serial.queue];\n this.__internal__.serial.queue = copy_queue.splice(1);\n\n if (this.__internal__.serial.queue.length > 0) {\n this.__internal__.serial.running_queue = true;\n //this.dispatch(\"internal:queue\", {});\n }\n }\n\n public validateBytes(data: string | Uint8Array | Array | Array): Uint8Array {\n let bytes: Uint8Array = new Uint8Array(0);\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (typeof data === \"string\") {\n bytes = this.parseStringToTextEncoder(data);\n } else if (Array.isArray(data) && typeof data[0] === \"string\") {\n bytes = this.stringArrayToUint8Array(data as string[]);\n } else if (Array.isArray(data) && typeof data[0] === \"number\") {\n bytes = new Uint8Array(data as unknown as number[]);\n } else {\n throw new Error(\"Invalid data type\");\n }\n return bytes;\n }\n\n public async appendToQueue(arr: string | Uint8Array | string[] | number[], action: string): Promise {\n const bytes: Uint8Array = this.validateBytes(arr);\n\n if ([\"connect\", \"connection:start\"].includes(action)) {\n if (this.__internal__.serial.connected) return;\n\n // ignore queue because the connection is not established, so first message is connection\n // queue will never send a message before connection is established\n\n await this.serialConnect();\n return;\n }\n\n this.__internal__.serial.queue.push({ bytes, action });\n this.dispatch(\"internal:queue\", {});\n }\n\n #serialSetBytesConnection(no_device = 1): void {\n this.__internal__.device_number = no_device;\n if (this.__internal__.bypassSerialBytesConnection) return;\n this.__internal__.serial.bytes_connection = this.serialSetConnectionConstant(no_device);\n }\n\n public serialSetConnectionConstant(listen_on_port = 1): string | Uint8Array | string[] | number[] | null {\n if (this.__internal__.bypassSerialBytesConnection) return this.__internal__.serial.bytes_connection;\n\n // console.warn(\"wtf?\", this.bypassSerialBytesConnection);\n\n throw new Error(`Method not implemented 'serialSetConnectionConstant' to listen on channel ${listen_on_port}`);\n // ... implement in subclass\n // return [];\n }\n\n public serialMessage(code: string[] | Uint8Array | string | ArrayBuffer): void {\n // this.dispatch('serial:message', code);\n // ... implement in subclass\n console.log(code);\n this.dispatch(\"serial:message\", { code: code });\n throw new Error(\"Method not implemented 'serialMessage'\");\n }\n\n public serialCorruptMessage(code: Uint8Array | number[] | string[] | never | null | string | ArrayBuffer): void {\n // ... implement in subclass\n console.log(code);\n this.dispatch(\"serial:corrupt-message\", { code });\n throw new Error(\"Method not implemented 'serialCorruptMessage'\");\n }\n\n #clearLastError(): void {\n this.__internal__.last_error = {\n message: null,\n action: null,\n code: null,\n no_code: 0,\n };\n }\n\n public clearSerialQueue(): void {\n this.__internal__.serial.queue = [];\n }\n\n public sumHex(arr: string[]): string {\n let sum: number = 0;\n arr.forEach((value: string): void => {\n sum += parseInt(value, 16);\n });\n return sum.toString(16);\n }\n\n public toString(): string {\n return JSON.stringify({\n __class: this.typeDevice,\n device_number: this.deviceNumber,\n uuid: this.uuid,\n connected: this.isConnected,\n connection: this.__internal__.serial.bytes_connection,\n });\n }\n\n public softReload(): void {\n this.#clearLastError();\n this.dispatch(\"serial:soft-reload\", {});\n }\n\n public async sendConnect(): Promise {\n if (!this.__internal__.serial.bytes_connection) {\n throw new Error(\"No connection bytes defined\");\n }\n await this.appendToQueue(this.__internal__.serial.bytes_connection, \"connect\");\n }\n\n public async sendCustomCode({ code = [] }: CustomCode = { code: [] }): Promise {\n if (!code) {\n throw new Error(\"No data to send\");\n }\n\n if (this.__internal__.bypassSerialBytesConnection) {\n this.__internal__.serial.bytes_connection = this.validateBytes(code);\n }\n\n await this.appendToQueue(code, \"custom\");\n }\n\n public stringToArrayHex(string: string): string[] {\n return Array.from(string).map((char: string): string => char.charCodeAt(0).toString(16));\n }\n\n public stringToArrayBuffer(string: string, end: string = \"\\n\"): ArrayBufferLike {\n return this.parseStringToTextEncoder(string, end).buffer;\n }\n\n public parseStringToTextEncoder(string: string = \"\", end: string = \"\\n\"): Uint8Array {\n const encoder = new TextEncoder();\n string += end; // to finish the command\n return encoder.encode(string);\n }\n\n public parseStringToBytes(string: string = \"\", end: string = \"\\n\"): string[] {\n const encoded: Uint8Array = this.parseStringToTextEncoder(string, end);\n return Array.from(encoded).map((byte: number): string => byte.toString(16));\n }\n\n public parseUint8ToHex(array: Uint8Array): string[] {\n return Array.from(array).map((byte: number): string => byte.toString(16).padStart(2, \"0\").toLowerCase());\n }\n\n public parseHexToUint8(array: string[]): Uint8Array {\n return new Uint8Array(array.map((hexString: string): number => parseInt(hexString, 16)));\n }\n\n public stringArrayToUint8Array(strings: string[]): Uint8Array {\n const bytes: number[] = [];\n if (typeof strings === \"string\") {\n return this.parseStringToTextEncoder(strings).buffer as unknown as Uint8Array;\n }\n strings.forEach((str: string): void => {\n const hex = str.replace(\"0x\", \"\");\n bytes.push(parseInt(hex, 16));\n });\n\n return new Uint8Array(bytes);\n }\n\n public parseUint8ArrayToString(array: Uint8Array | string[]): string {\n let arrayUint8: Uint8Array = new Uint8Array(0);\n if (array instanceof Uint8Array) {\n arrayUint8 = array;\n } else {\n arrayUint8 = this.stringArrayToUint8Array(array as string[]);\n }\n\n array = this.parseUint8ToHex(arrayUint8);\n const byteArray: number[] = array.map((hexString: string): number => parseInt(hexString, 16));\n if (this.__internal__.serial.response.replacer) {\n return String.fromCharCode(...byteArray).replace(this.__internal__.serial.response.replacer, \"\");\n }\n return String.fromCharCode(...byteArray);\n }\n\n public hexToAscii(hex: string | number): string {\n const hexString: string = hex.toString();\n let asciiString: string = \"\";\n for (let i: number = 0; i < hexString.length; i += 2) {\n asciiString += String.fromCharCode(parseInt(hexString.substring(i, 2), 16));\n }\n return asciiString;\n }\n\n public asciiToHex(asciiString: string): string {\n const hexArray: string[] = [];\n for (let i: number = 0, length: number = asciiString.length; i < length; i++) {\n const hex: string = Number(asciiString.charCodeAt(i)).toString(16);\n hexArray.push(hex);\n }\n return hexArray.join(\"\");\n }\n\n public $checkAndDispatchConnection(): boolean {\n return this.isConnected;\n }\n}\n","/**\n * Custom error codes for serial communication errors\n */\nexport enum SerialErrorCode {\n CONNECTION_FAILED = \"CONNECTION_FAILED\",\n DISCONNECTION_FAILED = \"DISCONNECTION_FAILED\",\n WRITE_FAILED = \"WRITE_FAILED\",\n READ_FAILED = \"READ_FAILED\",\n TIMEOUT = \"TIMEOUT\",\n PORT_NOT_FOUND = \"PORT_NOT_FOUND\",\n PERMISSION_DENIED = \"PERMISSION_DENIED\",\n DEVICE_NOT_SUPPORTED = \"DEVICE_NOT_SUPPORTED\",\n INVALID_CONFIGURATION = \"INVALID_CONFIGURATION\",\n SOCKET_ERROR = \"SOCKET_ERROR\",\n UNKNOWN_ERROR = \"UNKNOWN_ERROR\",\n}\n\n/**\n * Custom error class for WebSerial operations\n * Provides structured error information with codes and context\n * @extends Error\n */\nexport class SerialError extends Error {\n /**\n * Error code identifying the type of error\n */\n public readonly code: SerialErrorCode;\n\n /**\n * Additional context about the error\n */\n public readonly context?: Record;\n\n /**\n * Timestamp when the error occurred\n */\n public readonly timestamp: Date;\n\n /**\n * Creates a new SerialError\n * @param message - Human-readable error message\n * @param code - Error code from SerialErrorCode enum\n * @param context - Additional context information\n * @example\n * ```typescript\n * throw new SerialError(\n * 'Failed to connect to device',\n * SerialErrorCode.CONNECTION_FAILED,\n * { port: 'COM3', baudRate: 9600 }\n * );\n * ```\n */\n constructor(\n message: string,\n code: SerialErrorCode = SerialErrorCode.UNKNOWN_ERROR,\n context?: Record,\n ) {\n super(message);\n this.name = \"SerialError\";\n this.code = code;\n this.context = context;\n this.timestamp = new Date();\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SerialError);\n }\n }\n\n /**\n * Returns a JSON representation of the error\n * @returns Serialized error object\n */\n toJSON(): Record {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n context: this.context,\n timestamp: this.timestamp.toISOString(),\n stack: this.stack,\n };\n }\n\n /**\n * Returns a formatted string representation of the error\n * @returns Formatted error string\n */\n toString(): string {\n const contextStr = this.context ? ` | Context: ${JSON.stringify(this.context)}` : \"\";\n return `${this.name} [${this.code}]: ${this.message}${contextStr}`;\n }\n}\n"],"names":["SerialEvent","type","options","Dispatcher","data","event","ms","this1","callback","listener","l","key","Devices","device","error","id","device_number","devices","wait","resolve","MySocket","#uri","#options","#socket","#connected","#boundedFun","uri","url","io","config","Socket","defaultConfigPort","Core","#boundFinishConnecting","filters","config_port","no_device","device_listen_on_channel","bypassSerialBytesConnection","socket","#serialSetBytesConnection","#registerDefaultListeners","#internalEvents","channel","value","prevConnected","connected","#checkIfPortIsOpen","#disconnected","#connectingChange","limiter","length","bytes","path","vendorId","productId","string","port","detail","#onFinishConnecting","auxPrevConnected","#serialGetResponse","reject","interval","reader","output_stream","err","#serialSocketWrite","#serialWrite","#waitForCTS","writer","timeoutMs","start","clearToSend","code","corrupt","str","splited","s","arraybuffer","#serialPortsFiltered","ports","info","filter","aux","#appendBuffer","incoming","tmp","#freeSerialLoop","#slicedSerialLoop","expectedLength","buffer","message","#delimitedSerialLoop","prefixLimiter","sufixLimiter","decoded","messages","pattern","match","lastIndex","end","chunk","parts","part","msg","leftoverBytes","#readSerialLoop","done","#forget","dec","hex","val","min","new_bytes","index","byte","#runQueue","#browserEvents","first","limit_response","e","copy_queue","arr","action","listen_on_port","#clearLastError","sum","char","encoder","encoded","array","hexString","strings","arrayUint8","byteArray","asciiString","i","hexArray","SerialErrorCode","SerialError","context","contextStr"],"mappings":"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"} \ No newline at end of file diff --git a/lib/Core.ts b/lib/Core.ts index 67d9c6b..ba71972 100644 --- a/lib/Core.ts +++ b/lib/Core.ts @@ -781,7 +781,7 @@ export class Core extends Dispatcher implements ICore { this.on("internal:connecting", this.#boundFinishConnecting); - const interval: number = setInterval((): void => { + const interval: ReturnType = setInterval((): void => { if (this.__internal__.serial.aux_connecting === "finished") { clearInterval(interval); this.__internal__.serial.aux_connecting = "idle"; diff --git a/lib/Devices.ts b/lib/Devices.ts index 6533806..cc16266 100644 --- a/lib/Devices.ts +++ b/lib/Devices.ts @@ -9,6 +9,11 @@ interface IDevices { [key: string]: IDevice; } +/** + * Manages and tracks all serial devices in the application + * Provides a centralized registry for device instances + * @extends Dispatcher + */ export class Devices extends Dispatcher { static instance: Devices; static devices: IDevices = {}; @@ -37,12 +42,28 @@ export class Devices extends Dispatcher { throw error; } + /** + * Registers a new device type in the registry + * @param type - The type name of the device (e.g., 'arduino', 'esp32') + * @internal + */ public static registerType(type: string): void { if (typeof Devices.devices[type] === "undefined") { Devices.devices = { ...Devices.devices, [type]: {} }; } } + /** + * Adds a device to the registry + * @param device - The Core device instance to add + * @returns The index of the device in its type registry + * @throws {Error} If device with the same ID already exists + * @example + * ```typescript + * const arduino = new Arduino(); + * Devices.add(arduino); + * ``` + */ public static add(device: Core): number { const type = device.typeDevice; if (typeof Devices.devices[type] === "undefined") { @@ -63,6 +84,17 @@ export class Devices extends Dispatcher { return Object.keys(Devices.devices[type]).indexOf(id); } + /** + * Gets a specific device by type and UUID + * @param type - The device type + * @param id - The device UUID + * @returns The device instance + * @throws {Error} If the device type is not supported + * @example + * ```typescript + * const device = Devices.get('arduino', 'uuid-123'); + * ``` + */ public static get(type: string, id: string): Core { if (typeof Devices.devices[type] === "undefined") { Devices.registerType(type); diff --git a/lib/Dispatcher.ts b/lib/Dispatcher.ts index aae9401..b702db2 100644 --- a/lib/Dispatcher.ts +++ b/lib/Dispatcher.ts @@ -33,6 +33,15 @@ export class Dispatcher extends EventTarget implements IDispatcher { __listenersCallbacks__: { key: string; callback: EventListenerOrEventListenerObject }[] = []; + /** + * Dispatches an event with the specified type and data + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @example + * ```typescript + * dispatcher.dispatch('connected', { port: 'COM3' }); + * ``` + */ public dispatch(type: string, data: DataType = null) { const event = new SerialEvent(type, { detail: data }); this.dispatchEvent(event); @@ -41,6 +50,16 @@ export class Dispatcher extends EventTarget implements IDispatcher { } } + /** + * Dispatches an event asynchronously after a specified delay + * @param type - The event type to dispatch + * @param data - Optional data to attach to the event + * @param ms - Delay in milliseconds (default: 100) + * @example + * ```typescript + * dispatcher.dispatchAsync('timeout', { reason: 'no response' }, 500); + * ``` + */ public dispatchAsync(type: string, data = null, ms = 100) { // eslint-disable-next-line @typescript-eslint/no-this-alias const this1 = this; @@ -49,6 +68,17 @@ export class Dispatcher extends EventTarget implements IDispatcher { }, ms); } + /** + * Registers an event listener for the specified event type + * @param type - The event type to listen to + * @param callback - The callback function to execute when the event is triggered + * @example + * ```typescript + * dispatcher.on('connected', (event) => { + * console.log('Device connected', event.detail); + * }); + * ``` + */ public on(type: string, callback: EventListenerOrEventListenerObject) { if (typeof this.__listeners__[type] !== "undefined" && !this.__listeners__[type]) { this.__listeners__[type] = true; @@ -58,6 +88,17 @@ export class Dispatcher extends EventTarget implements IDispatcher { this.addEventListener(type, callback); } + /** + * Removes an event listener for the specified event type + * @param type - The event type to stop listening to + * @param callback - The callback function to remove + * @example + * ```typescript + * const handler = (event) => console.log(event.detail); + * dispatcher.on('data', handler); + * dispatcher.off('data', handler); + * ``` + */ public off(type: string, callback: EventListenerOrEventListenerObject) { this.__listenersCallbacks__ = this.__listenersCallbacks__.filter((listener) => { return !(listener.key === type && listener.callback === callback); @@ -66,12 +107,26 @@ export class Dispatcher extends EventTarget implements IDispatcher { this.removeEventListener(type, callback); } + /** + * Registers an available listener type for tracking + * @param type - The event type to register + * @internal + */ public serialRegisterAvailableListener(type: string) { if (this.__listeners__[type]) return; this.__listeners__[type] = false; } + /** + * Gets the list of all available listeners and their state + * @returns Array of listener objects with type and listening status + * @example + * ```typescript + * const listeners = dispatcher.availableListeners; + * console.log(listeners); // [{ type: 'connected', listening: true }, ...] + * ``` + */ get availableListeners(): AvailableListeners { const keys = Object.keys(this.__listeners__).sort(); return keys.map((type): AvailableListener => { @@ -82,6 +137,14 @@ export class Dispatcher extends EventTarget implements IDispatcher { }); } + /** + * Removes all event listeners except internal ones (like queue listeners) + * Resets all listener states to false + * @example + * ```typescript + * dispatcher.removeAllListeners(); + * ``` + */ public removeAllListeners(): void { for (const listener of this.__listenersCallbacks__) { if (["internal:queue"].includes(listener.key)) continue; // Skip queue listener diff --git a/lib/SerialError.ts b/lib/SerialError.ts new file mode 100644 index 0000000..cba5f99 --- /dev/null +++ b/lib/SerialError.ts @@ -0,0 +1,93 @@ +/** + * Custom error codes for serial communication errors + */ +export enum SerialErrorCode { + CONNECTION_FAILED = "CONNECTION_FAILED", + DISCONNECTION_FAILED = "DISCONNECTION_FAILED", + WRITE_FAILED = "WRITE_FAILED", + READ_FAILED = "READ_FAILED", + TIMEOUT = "TIMEOUT", + PORT_NOT_FOUND = "PORT_NOT_FOUND", + PERMISSION_DENIED = "PERMISSION_DENIED", + DEVICE_NOT_SUPPORTED = "DEVICE_NOT_SUPPORTED", + INVALID_CONFIGURATION = "INVALID_CONFIGURATION", + SOCKET_ERROR = "SOCKET_ERROR", + UNKNOWN_ERROR = "UNKNOWN_ERROR", +} + +/** + * Custom error class for WebSerial operations + * Provides structured error information with codes and context + * @extends Error + */ +export class SerialError extends Error { + /** + * Error code identifying the type of error + */ + public readonly code: SerialErrorCode; + + /** + * Additional context about the error + */ + public readonly context?: Record; + + /** + * Timestamp when the error occurred + */ + public readonly timestamp: Date; + + /** + * Creates a new SerialError + * @param message - Human-readable error message + * @param code - Error code from SerialErrorCode enum + * @param context - Additional context information + * @example + * ```typescript + * throw new SerialError( + * 'Failed to connect to device', + * SerialErrorCode.CONNECTION_FAILED, + * { port: 'COM3', baudRate: 9600 } + * ); + * ``` + */ + constructor( + message: string, + code: SerialErrorCode = SerialErrorCode.UNKNOWN_ERROR, + context?: Record, + ) { + super(message); + this.name = "SerialError"; + this.code = code; + this.context = context; + this.timestamp = new Date(); + + // Maintains proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, SerialError); + } + } + + /** + * Returns a JSON representation of the error + * @returns Serialized error object + */ + toJSON(): Record { + return { + name: this.name, + message: this.message, + code: this.code, + context: this.context, + timestamp: this.timestamp.toISOString(), + stack: this.stack, + }; + } + + /** + * Returns a formatted string representation of the error + * @returns Formatted error string + */ + toString(): string { + const contextStr = this.context ? ` | Context: ${JSON.stringify(this.context)}` : ""; + return `${this.name} [${this.code}]: ${this.message}${contextStr}`; + } +} diff --git a/lib/Socket.ts b/lib/Socket.ts index 640be31..2c4e706 100644 --- a/lib/Socket.ts +++ b/lib/Socket.ts @@ -1,9 +1,16 @@ -import { io, ManagerOptions, SocketOptions } from "socket.io-client"; +import { io, ManagerOptions, SocketOptions, Socket as SocketIOClient } from "socket.io-client"; import { Devices } from "./Devices"; import { Core } from "./Core"; +interface SocketResponseData { + name: string; + uuid: string; + deviceNumber: number; + [key: string]: unknown; +} + type BoundedFunction = { - onResponse?: (data: any) => void; + onResponse: (data: SocketResponseData) => void; }; class MySocket { @@ -11,10 +18,16 @@ class MySocket { #options: Partial = { transports: ["websocket"], }; - #socket: any; + #socket: SocketIOClient | null = null; #connected: boolean = false; - #boundedFun: BoundedFunction = {}; + #boundedFun: BoundedFunction; + + constructor() { + this.#boundedFun = { + onResponse: this.onResponse.bind(this), + }; + } set uri(uri: string) { const url = new URL(uri); @@ -40,10 +53,6 @@ class MySocket { return this.#options; } - constructor() { - this.#boundedFun.onResponse = this.onResponse.bind(this); - } - disconnect() { if (this.#socket) { this.#socket.off("response", this.#boundedFun.onResponse); @@ -63,23 +72,35 @@ class MySocket { this.#socket.on("response", this.#boundedFun.onResponse); } - connectDevice(config: object) { + connectDevice(config: object): void { + if (!this.#socket) { + throw new Error("Socket not connected. Call prepare() first."); + } this.#socket.emit("connectDevice", { config }); } - disconnectDevice(config: object) { + disconnectDevice(config: object): void { + if (!this.#socket) { + throw new Error("Socket not connected. Call prepare() first."); + } this.#socket.emit("disconnectDevice", { config }); } - disconnectAllDevices() { + disconnectAllDevices(): void { + if (!this.#socket) { + throw new Error("Socket not connected. Call prepare() first."); + } this.#socket.emit("disconnectAll"); } write(data: object): void { + if (!this.#socket) { + throw new Error("Socket not connected. Call prepare() first."); + } this.#socket.emit("cmd", data); } - onResponse(data: any) { + onResponse(data: SocketResponseData): void { let device: Core | null = Devices.get(data.name, data.uuid); if (!device) { device = Devices.getByNumber(data.name, data.deviceNumber); diff --git a/lib/main.ts b/lib/main.ts index 877b0ad..424b57a 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -12,3 +12,4 @@ export { Core } from "./Core"; export { Devices } from "./Devices"; export { Dispatcher } from "./Dispatcher"; export { Socket } from "./Socket"; +export { SerialError, SerialErrorCode } from "./SerialError"; diff --git a/package-lock.json b/package-lock.json index 6f55b2b..fc02add 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,554 @@ { "name": "webserial-core", - "version": "1.1.0", + "version": "1.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "webserial-core", - "version": "1.1.0", + "version": "1.1.3", "license": "MIT", "devDependencies": { - "@eslint/js": "^9.35.0", + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", + "@eslint/js": "^9.39.1", "@types/w3c-web-serial": "^1.0.8", - "eslint": "^9.35.0", - "globals": "^16.4.0", + "@vitest/coverage-v8": "^4.0.8", + "@vitest/ui": "^4.0.8", + "eslint": "^9.39.1", + "globals": "^16.5.0", + "happy-dom": "^20.0.10", + "husky": "^9.1.7", + "lint-staged": "^16.2.6", "prettier": "3.6.2", - "socket.io-client": "^4.8.1", - "typescript": "~5.9.2", - "typescript-eslint": "^8.43.0", - "vite": "^7.1.5" + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.3", + "vite": "^7.2.2", + "vitest": "^4.0.8" + }, + "peerDependencies": { + "socket.io-client": "^4.8.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/cli": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.1.0.tgz", + "integrity": "sha512-pW5ujjrOovhq5RcYv5xCpb4GkZxkO2+GtOdBW2/qrr0Ll9tl3PX0aBBobGQl3mdZUbOBgwAexEQLeH6uxL0VYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^20.0.0", + "@commitlint/lint": "^20.0.0", + "@commitlint/load": "^20.1.0", + "@commitlint/read": "^20.0.0", + "@commitlint/types": "^20.0.0", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/cli/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.0.0.tgz", + "integrity": "sha512-q7JroPIkDBtyOkVe9Bca0p7kAUYxZMxkrBArCfuD3yN4KjRAenP9PmYwnn7rsw8Q+hHq1QB2BRmBh0/Z19ZoJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.0.0.tgz", + "integrity": "sha512-BeyLMaRIJDdroJuYM2EGhDMGwVBMZna9UiIqV9hxj+J551Ctc6yoGuGSmghOy/qPhBSuhA6oMtbEiTmxECafsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@commitlint/ensure": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.0.0.tgz", + "integrity": "sha512-WBV47Fffvabe68n+13HJNFBqiMH5U1Ryls4W3ieGwPC0C7kJqp3OVQQzG2GXqOALmzrgAB+7GXmyy8N9ct8/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", + "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.0.0.tgz", + "integrity": "sha512-zrZQXUcSDmQ4eGGrd+gFESiX0Rw+WFJk7nW4VFOmxub4mAATNKBQ4vNw5FgMCVehLUKG2OT2LjOqD0Hk8HvcRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.0.0.tgz", + "integrity": "sha512-ayPLicsqqGAphYIQwh9LdAYOVAQ9Oe5QCgTNTj+BfxZb9b/JW222V5taPoIBzYnAP0z9EfUtljgBk+0BN4T4Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.0.0.tgz", + "integrity": "sha512-kWrX8SfWk4+4nCexfLaQT3f3EcNjJwJBsSZ5rMBw6JCd6OzXufFHgel2Curos4LKIxwec9WSvs2YUD87rXlxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^20.0.0", + "@commitlint/parse": "^20.0.0", + "@commitlint/rules": "^20.0.0", + "@commitlint/types": "^20.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.1.0.tgz", + "integrity": "sha512-qo9ER0XiAimATQR5QhvvzePfeDfApi/AFlC1G+YN+ZAY8/Ua6IRrDrxRvQAr+YXUKAxUsTDSp9KXeXLBPsNRWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.0.0", + "@commitlint/execute-rule": "^20.0.0", + "@commitlint/resolve-extends": "^20.1.0", + "@commitlint/types": "^20.0.0", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/message": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.0.0.tgz", + "integrity": "sha512-gLX4YmKnZqSwkmSB9OckQUrI5VyXEYiv3J5JKZRxIp8jOQsWjZgHSG/OgEfMQBK9ibdclEdAyIPYggwXoFGXjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.0.0.tgz", + "integrity": "sha512-j/PHCDX2bGM5xGcWObOvpOc54cXjn9g6xScXzAeOLwTsScaL4Y+qd0pFC6HBwTtrH92NvJQc+2Lx9HFkVi48cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.0.0", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.0.0.tgz", + "integrity": "sha512-Ti7Y7aEgxsM1nkwA4ZIJczkTFRX/+USMjNrL9NXwWQHqNqrBX2iMi+zfuzZXqfZ327WXBjdkRaytJ+z5vNqTOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^20.0.0", + "@commitlint/types": "^20.0.0", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.1.0.tgz", + "integrity": "sha512-cxKXQrqHjZT3o+XPdqDCwOWVFQiae++uwd9dUBC7f2MdV58ons3uUvASdW7m55eat5sRiQ6xUHyMWMRm6atZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.0.0", + "@commitlint/types": "^20.0.0", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.0.0.tgz", + "integrity": "sha512-gvg2k10I/RfvHn5I5sxvVZKM1fl72Sqrv2YY/BnM7lMHcYqO0E2jnRWoYguvBfEcZ39t+rbATlciggVe77E4zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^20.0.0", + "@commitlint/message": "^20.0.0", + "@commitlint/to-lines": "^20.0.0", + "@commitlint/types": "^20.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", + "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.0.0.tgz", + "integrity": "sha512-drXaPSP2EcopukrUXvUXmsQMu3Ey/FuJDc/5oiW4heoCfoE5BdLQyuc7veGeE3aoQaTVqZnh4D5WTWe2vefYKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.0.0.tgz", + "integrity": "sha512-bVUNBqG6aznYcYjTjnc3+Cat/iBgbgpflxbIBTnsHTX0YVpnmINPEkSRWymT2Q8aSH3Y7aKnEbunilkYe8TybA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@esbuild/aix-ppc64": { @@ -505,13 +1036,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -520,19 +1051,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -580,9 +1114,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", - "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -593,9 +1127,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -603,13 +1137,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -668,6 +1202,34 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -706,6 +1268,13 @@ "node": ">= 8" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.50.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", @@ -1004,6 +1573,41 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT", + "peer": true + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", + "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, @@ -1021,6 +1625,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@types/w3c-web-serial": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/w3c-web-serial/-/w3c-web-serial-1.0.8.tgz", @@ -1028,18 +1642,25 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", - "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", + "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/type-utils": "8.43.0", - "@typescript-eslint/utils": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/type-utils": "8.46.3", + "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1053,7 +1674,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.43.0", + "@typescript-eslint/parser": "^8.46.3", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -1069,16 +1690,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", - "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", + "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "engines": { @@ -1094,14 +1715,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", - "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", + "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.43.0", - "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/tsconfig-utils": "^8.46.3", + "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "engines": { @@ -1116,14 +1737,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", - "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", + "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0" + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1134,9 +1755,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", - "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", + "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", "dev": true, "license": "MIT", "engines": { @@ -1151,15 +1772,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", - "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", + "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0", - "@typescript-eslint/utils": "8.43.0", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1176,114 +1797,279 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", - "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", + "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", + "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.3", + "@typescript-eslint/tsconfig-utils": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", + "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", + "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.3", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.8.tgz", + "integrity": "sha512-wQgmtW6FtPNn4lWUXi8ZSYLpOIb92j3QCujxX3sQ81NTfQ/ORnE0HtK7Kqf2+7J9jeveMGyGyc4NWc5qy3rC4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.8", + "ast-v8-to-istanbul": "^0.3.8", + "debug": "^4.4.3", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.8", + "vitest": "4.0.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.8.tgz", + "integrity": "sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.8", + "@vitest/utils": "4.0.8", + "chai": "^6.2.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.8.tgz", + "integrity": "sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", - "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", + "node_modules/@vitest/pretty-format": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.8.tgz", + "integrity": "sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.43.0", - "@typescript-eslint/tsconfig-utils": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "tinyrainbow": "^3.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@vitest/runner": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.8.tgz", + "integrity": "sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@vitest/utils": "4.0.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@vitest/snapshot": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.8.tgz", + "integrity": "sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "@vitest/pretty-format": "4.0.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", - "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", + "node_modules/@vitest/spy": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.8.tgz", + "integrity": "sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.8.tgz", + "integrity": "sha512-F9jI5rSstNknPlTlPN2gcc4gpbaagowuRzw/OJzl368dvPun668Q182S8Q8P9PITgGCl5LAKXpzuue106eM4wA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@vitest/utils": "4.0.8", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "vitest": "4.0.8" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", - "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", + "node_modules/@vitest/utils": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.8.tgz", + "integrity": "sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.43.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@vitest/pretty-format": "4.0.8", + "tinyrainbow": "^3.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, "node_modules/acorn": { @@ -1326,6 +2112,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1349,6 +2164,35 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz", + "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1390,6 +2234,16 @@ "node": ">=6" } }, + "node_modules/chai": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.0.tgz", + "integrity": "sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1403,37 +2257,269 @@ "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "jiti": "^2.6.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1449,6 +2535,19 @@ "node": ">= 8" } }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1474,12 +2573,32 @@ "dev": true, "license": "MIT" }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/engine.io-client": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", @@ -1492,8 +2611,8 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1510,12 +2629,52 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", @@ -1558,6 +2717,16 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1572,25 +2741,24 @@ } }, "node_modules/eslint": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", - "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.35.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -1716,6 +2884,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1726,6 +2904,23 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1777,6 +2972,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -1787,6 +2999,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -1866,6 +3085,47 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1879,10 +3139,26 @@ "node": ">=10.13.0" } }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", "engines": { @@ -1899,6 +3175,21 @@ "dev": true, "license": "MIT" }, + "node_modules/happy-dom": { + "version": "20.0.10", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.0.10.tgz", + "integrity": "sha512-6umCCHcjQrhP5oXhrHQQvLB0bwb1UzHAHdsXy+FjtKoYjUhmNZsQL8NivwM1vDvNEChJabVrUYxUnp/ZdYmy2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1909,6 +3200,29 @@ "node": ">=8" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1936,6 +3250,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1946,6 +3271,23 @@ "node": ">=0.8.19" } }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1956,6 +3298,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1975,16 +3333,110 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.12.0" + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -2006,6 +3458,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2020,6 +3479,33 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2044,6 +3530,56 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.6.tgz", + "integrity": "sha512-s1gphtDbV4bmW1eylXpVMk2u7is7YsrLl8hzrtvC70h4ByhcMLZFY01Fx05ZUDNuv1H8HO4E+e2zgejV1jVwNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.1", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2060,6 +3596,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2067,6 +3624,112 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", + "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2091,6 +3754,19 @@ "node": ">=8.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2104,13 +3780,45 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -2137,6 +3845,22 @@ "dev": true, "license": "MIT" }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2200,6 +3924,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2220,6 +3963,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2240,6 +3990,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -2326,6 +4089,26 @@ ], "license": "MIT" }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2336,6 +4119,23 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -2347,6 +4147,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.50.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", @@ -2413,9 +4220,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -2432,28 +4239,93 @@ "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/socket.io-client": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -2468,8 +4340,8 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2486,8 +4358,8 @@ "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -2500,8 +4372,8 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2524,6 +4396,73 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2550,6 +4489,40 @@ "node": ">=8" } }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -2598,6 +4571,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2611,6 +4594,16 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -2638,9 +4631,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2652,16 +4645,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.43.0.tgz", - "integrity": "sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", + "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.43.0", - "@typescript-eslint/parser": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0", - "@typescript-eslint/utils": "8.43.0" + "@typescript-eslint/eslint-plugin": "8.46.3", + "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2675,6 +4668,26 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2686,9 +4699,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", + "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2791,6 +4804,107 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.8.tgz", + "integrity": "sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.8", + "@vitest/mocker": "4.0.8", + "@vitest/pretty-format": "4.0.8", + "@vitest/runner": "4.0.8", + "@vitest/snapshot": "4.0.8", + "@vitest/spy": "4.0.8", + "@vitest/utils": "4.0.8", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.8", + "@vitest/browser-preview": "4.0.8", + "@vitest/browser-webdriverio": "4.0.8", + "@vitest/ui": "4.0.8", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2807,6 +4921,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2817,12 +4948,68 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2843,11 +5030,111 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, + "peer": true, "engines": { "node": ">=0.4.0" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 71e41a9..e3d11be 100644 --- a/package.json +++ b/package.json @@ -42,20 +42,46 @@ "build": "rm -rf dist && prettier --write ./lib/**/*.ts && tsc && vite build", "preview": "vite preview", "lint": "eslint --ext .ts ./lib", - "format": "prettier --write ./lib/**/*.ts" + "format": "prettier --write ./lib/**/*.ts", + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run", + "test:coverage": "vitest run --coverage", + "prepare": "husky" }, "peerDependencies": { "socket.io-client": "^4.8.1" }, + "engines": { + "node": ">=18.0.0" + }, "devDependencies": { - "@eslint/js": "^9.35.0", + "@commitlint/cli": "^20.1.0", + "@commitlint/config-conventional": "^20.0.0", + "@eslint/js": "^9.39.1", "@types/w3c-web-serial": "^1.0.8", - "eslint": "^9.35.0", - "globals": "^16.4.0", + "@vitest/coverage-v8": "^4.0.8", + "@vitest/ui": "^4.0.8", + "eslint": "^9.39.1", + "globals": "^16.5.0", + "happy-dom": "^20.0.10", + "husky": "^9.1.7", + "lint-staged": "^16.2.6", "prettier": "3.6.2", - "typescript": "~5.9.2", - "typescript-eslint": "^8.44.0", - "vite": "^7.1.5" + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.3", + "vite": "^7.2.2", + "vitest": "^4.0.8" + }, + "lint-staged": { + "lib/**/*.ts": [ + "prettier --write", + "eslint --fix" + ], + "tests/**/*.ts": [ + "prettier --write", + "eslint --fix" + ] }, "publishConfig": { "access": "public" diff --git a/tests/Dispatcher.test.ts b/tests/Dispatcher.test.ts new file mode 100644 index 0000000..a257464 --- /dev/null +++ b/tests/Dispatcher.test.ts @@ -0,0 +1,176 @@ +import { describe, it, expect, vi } from "vitest"; +import { Dispatcher } from "../lib/Dispatcher"; + +describe("Dispatcher", () => { + describe("event registration and dispatching", () => { + it("should register available listeners", () => { + const dispatcher = new Dispatcher(); + dispatcher.serialRegisterAvailableListener("test-event"); + + const listeners = dispatcher.availableListeners; + const testListener = listeners.find((l) => l.type === "test-event"); + + expect(testListener).toBeDefined(); + expect(testListener?.listening).toBe(false); + }); + + it("should dispatch events", () => { + const dispatcher = new Dispatcher(); + const callback = vi.fn(); + + dispatcher.on("test-event", callback); + dispatcher.dispatch("test-event", { message: "hello" }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith( + expect.objectContaining({ + type: "test-event", + detail: { message: "hello" }, + }), + ); + }); + + it("should mark listener as listening when callback is attached", () => { + const dispatcher = new Dispatcher(); + dispatcher.serialRegisterAvailableListener("test-event"); + + const callback = vi.fn(); + dispatcher.on("test-event", callback); + + const listeners = dispatcher.availableListeners; + const testListener = listeners.find((l) => l.type === "test-event"); + + expect(testListener?.listening).toBe(true); + }); + + it("should remove event listeners", () => { + const dispatcher = new Dispatcher(); + const callback = vi.fn(); + + dispatcher.on("test-event", callback); + dispatcher.off("test-event", callback); + dispatcher.dispatch("test-event"); + + expect(callback).not.toHaveBeenCalled(); + }); + + it("should handle multiple callbacks for the same event", () => { + const dispatcher = new Dispatcher(); + const callback1 = vi.fn(); + const callback2 = vi.fn(); + + dispatcher.on("test-event", callback1); + dispatcher.on("test-event", callback2); + dispatcher.dispatch("test-event", { data: "test" }); + + expect(callback1).toHaveBeenCalledTimes(1); + expect(callback2).toHaveBeenCalledTimes(1); + }); + }); + + describe("async dispatch", () => { + it("should dispatch events asynchronously", async () => { + const dispatcher = new Dispatcher(); + const callback = vi.fn(); + + dispatcher.on("test-event", callback); + dispatcher.dispatchAsync("test-event", null, 50); + + expect(callback).not.toHaveBeenCalled(); + + await new Promise((resolve) => setTimeout(resolve, 100)); + + expect(callback).toHaveBeenCalledTimes(1); + }); + }); + + describe("debug mode", () => { + it("should dispatch debug events when debug is enabled", () => { + const dispatcher = new Dispatcher(); + dispatcher.__debug__ = true; + + const debugCallback = vi.fn(); + dispatcher.on("debug", debugCallback); + dispatcher.dispatch("test-event", { data: "test" }); + + expect(debugCallback).toHaveBeenCalledWith( + expect.objectContaining({ + type: "debug", + detail: { + type: "test-event", + data: { data: "test" }, + }, + }), + ); + }); + + it("should not dispatch debug events when debug is disabled", () => { + const dispatcher = new Dispatcher(); + dispatcher.__debug__ = false; + + const debugCallback = vi.fn(); + dispatcher.on("debug", debugCallback); + dispatcher.dispatch("test-event", { data: "test" }); + + expect(debugCallback).not.toHaveBeenCalled(); + }); + }); + + describe("removeAllListeners", () => { + it("should remove all listeners except internal ones", () => { + const dispatcher = new Dispatcher(); + const callback1 = vi.fn(); + const callback2 = vi.fn(); + const internalCallback = vi.fn(); + + dispatcher.on("test-event-1", callback1); + dispatcher.on("test-event-2", callback2); + dispatcher.on("internal:queue", internalCallback); + + dispatcher.removeAllListeners(); + + dispatcher.dispatch("test-event-1"); + dispatcher.dispatch("test-event-2"); + dispatcher.dispatch("internal:queue"); + + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + expect(internalCallback).toHaveBeenCalled(); + }); + + it("should reset listener states", () => { + const dispatcher = new Dispatcher(); + dispatcher.serialRegisterAvailableListener("test-event"); + + const callback = vi.fn(); + dispatcher.on("test-event", callback); + + let listeners = dispatcher.availableListeners; + let testListener = listeners.find((l) => l.type === "test-event"); + expect(testListener?.listening).toBe(true); + + dispatcher.removeAllListeners(); + + listeners = dispatcher.availableListeners; + testListener = listeners.find((l) => l.type === "test-event"); + expect(testListener?.listening).toBe(false); + }); + }); + + describe("availableListeners", () => { + it("should return sorted list of available listeners", () => { + const dispatcher = new Dispatcher(); + dispatcher.serialRegisterAvailableListener("z-event"); + dispatcher.serialRegisterAvailableListener("a-event"); + dispatcher.serialRegisterAvailableListener("m-event"); + + const listeners = dispatcher.availableListeners; + const types = listeners.map((l) => l.type); + + expect(types).toEqual(expect.arrayContaining(["a-event", "debug", "m-event", "z-event"])); + // Verificar que está ordenado + const sortedTypes = [...types].sort(); + expect(types).toEqual(sortedTypes); + }); + }); +}); diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..b1f1545 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,41 @@ +# Tests + +This directory contains the test suite for webserial-core. + +## Running Tests + +```bash +# Run tests in watch mode +npm test + +# Run tests once +npm run test:run + +# Run tests with coverage +npm run test:coverage + +# Run tests with UI +npm run test:ui +``` + +## Test Structure + +- `utils.test.ts` - Tests for utility functions +- `Dispatcher.test.ts` - Tests for the event dispatcher system +- `SerialError.test.ts` - Tests for custom error handling +- Additional test files should follow the pattern `*.test.ts` + +## Writing Tests + +Tests use [Vitest](https://vitest.dev/) as the testing framework with happy-dom for browser environment simulation. + +Example: +```typescript +import { describe, it, expect } from 'vitest'; + +describe('MyFeature', () => { + it('should do something', () => { + expect(true).toBe(true); + }); +}); +``` diff --git a/tests/SerialError.test.ts b/tests/SerialError.test.ts new file mode 100644 index 0000000..2d2cdad --- /dev/null +++ b/tests/SerialError.test.ts @@ -0,0 +1,113 @@ +import { describe, it, expect } from "vitest"; +import { SerialError, SerialErrorCode } from "../lib/SerialError"; + +describe("SerialError", () => { + describe("constructor", () => { + it("should create error with message and code", () => { + const error = new SerialError("Connection failed", SerialErrorCode.CONNECTION_FAILED); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SerialError); + expect(error.message).toBe("Connection failed"); + expect(error.code).toBe(SerialErrorCode.CONNECTION_FAILED); + expect(error.name).toBe("SerialError"); + }); + + it("should create error with context", () => { + const context = { port: "COM3", baudRate: 9600 }; + const error = new SerialError("Invalid configuration", SerialErrorCode.INVALID_CONFIGURATION, context); + + expect(error.context).toEqual(context); + }); + + it("should use UNKNOWN_ERROR as default code", () => { + const error = new SerialError("Something went wrong"); + + expect(error.code).toBe(SerialErrorCode.UNKNOWN_ERROR); + }); + + it("should have timestamp", () => { + const before = new Date(); + const error = new SerialError("Test error", SerialErrorCode.TIMEOUT); + const after = new Date(); + + expect(error.timestamp).toBeInstanceOf(Date); + expect(error.timestamp.getTime()).toBeGreaterThanOrEqual(before.getTime()); + expect(error.timestamp.getTime()).toBeLessThanOrEqual(after.getTime()); + }); + }); + + describe("toJSON", () => { + it("should serialize error to JSON", () => { + const context = { device: "arduino", port: 1 }; + const error = new SerialError("Write failed", SerialErrorCode.WRITE_FAILED, context); + + const json = error.toJSON(); + + expect(json).toMatchObject({ + name: "SerialError", + message: "Write failed", + code: SerialErrorCode.WRITE_FAILED, + context: context, + }); + expect(json.timestamp).toBeDefined(); + expect(typeof json.timestamp).toBe("string"); + expect(json.stack).toBeDefined(); + }); + + it("should serialize error without context", () => { + const error = new SerialError("Timeout", SerialErrorCode.TIMEOUT); + const json = error.toJSON(); + + expect(json.context).toBeUndefined(); + }); + }); + + describe("toString", () => { + it("should format error as string with context", () => { + const error = new SerialError("Permission denied", SerialErrorCode.PERMISSION_DENIED, { + path: "/dev/ttyUSB0", + }); + + const str = error.toString(); + + expect(str).toContain("SerialError"); + expect(str).toContain("[PERMISSION_DENIED]"); + expect(str).toContain("Permission denied"); + expect(str).toContain('{"path":"/dev/ttyUSB0"}'); + }); + + it("should format error as string without context", () => { + const error = new SerialError("Port not found", SerialErrorCode.PORT_NOT_FOUND); + + const str = error.toString(); + + expect(str).toBe("SerialError [PORT_NOT_FOUND]: Port not found"); + }); + }); + + describe("error codes", () => { + it("should have all expected error codes", () => { + expect(SerialErrorCode.CONNECTION_FAILED).toBe("CONNECTION_FAILED"); + expect(SerialErrorCode.DISCONNECTION_FAILED).toBe("DISCONNECTION_FAILED"); + expect(SerialErrorCode.WRITE_FAILED).toBe("WRITE_FAILED"); + expect(SerialErrorCode.READ_FAILED).toBe("READ_FAILED"); + expect(SerialErrorCode.TIMEOUT).toBe("TIMEOUT"); + expect(SerialErrorCode.PORT_NOT_FOUND).toBe("PORT_NOT_FOUND"); + expect(SerialErrorCode.PERMISSION_DENIED).toBe("PERMISSION_DENIED"); + expect(SerialErrorCode.DEVICE_NOT_SUPPORTED).toBe("DEVICE_NOT_SUPPORTED"); + expect(SerialErrorCode.INVALID_CONFIGURATION).toBe("INVALID_CONFIGURATION"); + expect(SerialErrorCode.SOCKET_ERROR).toBe("SOCKET_ERROR"); + expect(SerialErrorCode.UNKNOWN_ERROR).toBe("UNKNOWN_ERROR"); + }); + }); + + describe("stack trace", () => { + it("should capture stack trace", () => { + const error = new SerialError("Test error", SerialErrorCode.UNKNOWN_ERROR); + + expect(error.stack).toBeDefined(); + expect(error.stack).toContain("SerialError"); + }); + }); +}); diff --git a/tests/utils.test.ts b/tests/utils.test.ts new file mode 100644 index 0000000..3fcf0a3 --- /dev/null +++ b/tests/utils.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest"; +import { wait } from "../lib/utils"; + +describe("utils", () => { + describe("wait", () => { + it("should wait for the specified time", async () => { + const start = Date.now(); + await wait(100); + const end = Date.now(); + const elapsed = end - start; + + expect(elapsed).toBeGreaterThanOrEqual(90); + expect(elapsed).toBeLessThan(200); + }); + + it("should wait for default time (100ms) when no argument provided", async () => { + const start = Date.now(); + await wait(); + const end = Date.now(); + const elapsed = end - start; + + expect(elapsed).toBeGreaterThanOrEqual(90); + expect(elapsed).toBeLessThan(200); + }); + + it("should return a promise", () => { + const result = wait(10); + expect(result).toBeInstanceOf(Promise); + }); + }); +}); diff --git a/vite.config.js b/vite.config.js index 124ccd9..da5c90b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,6 +7,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); export default defineConfig({ build: { emptyOutDir: false, + sourcemap: true, lib: { entry: resolve(__dirname, "lib/main.ts"), name: "WebSerialCore", diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..349654c --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "happy-dom", + globals: true, + coverage: { + provider: "v8", + reporter: ["text", "json", "html"], + exclude: [ + "node_modules/", + "dist/", + "tests/", + "*.config.{js,ts}", + "**/*.d.ts", + ], + }, + }, +});