diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4362006..9f5c330 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,9 +5,12 @@ module.exports = { node: true }, extends: [ + 'plugin:quasar/standard', 'digitalbazaar', 'digitalbazaar/jsdoc', - 'digitalbazaar/module' + 'digitalbazaar/module', + 'digitalbazaar/vue3' ], + rules: {}, ignorePatterns: ['node_modules/'] }; diff --git a/.gitignore b/.gitignore index cfac74e..33d5369 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage node_modules reports .cache +.DS_STORE diff --git a/CHANGELOG.md b/CHANGELOG.md index 63ccf13..8846e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,114 +1,57 @@ -# bedrock-web-pouch-edv ChangeLog +# bedrock-vue-optical-scanner ChangeLog -## 8.2.0 - 2025-07-18 +## 1.2.0 - 2025-mm-dd ### Changed -- Update dependencies: - - `@digitalbazaar/ecdsa-multikey@1.8` - - `pouchdb@9` - - `pouchdb-adapter-indexeddb@9` - - `pouchdb-find@9` - - dev deps and removal of `uuid`. -- Note: While pouchdb* packages have been bumped by two major versions, no - significant breaking changes are expected. The changes included internal - ES5 => ES6+ API changes that were not relevant here and a new default - limit on `find()` queries of `25`, but every `find()` query is already - expected to have provided a default or a default is provided by this - library. - -## 8.1.0 - 2023-11-07 -### Added -- Add new `cipherVersion` parameter to control whether the cipher version - is "recommended" or "fips". The default (and previous only option) - remains "recommended". - -## 8.0.0 - 2023-10-16 - -### Changed -- **BREAKING**: Drop support for Node.js < 18. -- Use `@digitalbazaar/edv-client@16.0` that drops support for Node.js < 18 and - uses `@digitalbazaar/http-client@4` and `canonicalize@2`. - -## 7.0.0 - 2022-08-19 - -### Changed -- **BREAKING**: Use `exports` instead of `module`. -- Update dependencies. -- Lint module. +- **NOTE**: License key configuration moved from component props to `@bedrock/web/config` + - Removed `licenseKey` prop from OpticalScanner component API + - License keys now configured once in bedrock config, automatically read by components + - Enables future removal of third-party dependencies without breaking changes +- Updated props documentation to clarify mode-specific behavior + - `showQrBox` and `torchOn` props only apply to barcode/auto modes + - MRZ mode uses Dynamsoft native UI (Vue overlays automatically disabled) -## 6.0.1 - 2022-05-30 - -### Fixed -- Add purge operation to clean up deleted docs to prevent premature storage - quota overflow. - -## 6.0.0 - 2022-05-30 - -### Changed -- **BREAKING**: Use `indexeddb` adapter instead of `idb` adapter. This version - will also impose a `br_edv_` (bedrock EDV) prefix on database names, - causing all new databases to be created, leaving old ones alone. There is - no migration code available to convert an old database to a new one in this - version. +### Added -## 5.0.0 - 2022-05-05 +- Configuration section in README explaining bedrock config setup for license keys +- Troubleshooting section addressing common questions about mode-specific props +- Architecture documentation explaining license key management design rationale -### Changed -- **BREAKING**: Use `@digitalbazaar/edv-client@14` with new blind - attributes version. +### Improved -## 4.1.0 - 2022-05-03 +- Implemented `effectiveShowQrBox` computed property for mode-aware UI rendering + - Automatically disables QR box overlay when MRZ mode is active + - Makes mode-specific behavior explicit in code rather than implicit +- Enhanced PDF417 clarified as legacy feature accessible via `formats` prop override -### Changed -- Improve pouchdb index performance by marking deleted EDV doc - records with `_deleted` flag. +## 1.1.0 - 2025-09-27 -## 4.0.0 - 2022-04-05 - -### Changed -- **BREAKING**: Rename package to `@bedrock/web-pouch-edv`. -- **BREAKING**: Convert to module (ESM). -- **BREAKING**: Remove default export. -- **BREAKING**: Require node 14.x. +### Added -## 3.0.0 - 2022-03-01 +- **BREAKING**: Refactored Vue components to use CameraScanner delegation pattern + - OpticalScanner.vue now uses CameraScanner from `bedrock-web-optical-scanner` + - Thin wrapper architecture enables easy duplication in other frameworks ### Changed -- **BREAKING**: Use `@digitalbazaar/edv-client@13`. -## 2.0.1 - 2022-02-24 +- **NOTE**: Vue components are now thin wrappers focused purely on UI concerns +- ScannerUI.vue streamlined for pure framework integration + - Clean separation between CameraScanner area and Vue overlay area + - Removed scanning business logic to achieve framework-agnostic design +- Configuration simplified to scanType/scanMode props instead of complex plugin setup -### Fixed -- Provide alternative to `crypto.randomUUID` via `uuid` package. +### Improved -## 2.0.0 - 2022-02-23 +- All scanning complexity now delegated to `bedrock-web-optical-scanner` module +- Vue components leverage framework-specific features (Quasar UI, reactivity) without scanning logic +- Architecture enables rapid React/other framework development with minimal duplication +- Better separation of concerns between presentation layer and scanning engine -### Changed -- **BREAKING**: Use `@digitalbazaar/edv-client@12`. This new version - produces encrypted indexes differently (more privacy preserving) - and is incompatible with the previous version. - -## 1.2.0 - 2022-02-05 +## 1.0.0 - 2025-09-14 ### Added -- Add support for `limit` in EDV queries. - -## 1.1.0 - 2022-02-03 - -### Added -- Automatically initialize databases when using - `PouchEdvClient` to generate a new EDV or load an one. - -### Fixed -- Fix `db.type()` deprecation warnings. -- Prevent double initialization of databases. - -## 1.0.1 - 2022-02-02 - -### Changed -- Update dependencies. - -## 1.0.0 - 2022-01-31 -- See git history for changes. +- Vue.js components `OpticalScanner.vue` and `ScannerUI.vue` with reactive state management. +- Scan type selection interface allowing users to choose between barcode/QR scanning and MRZ document scanning. +- Timeout configuration system with format-specific timeouts (no timeout for MRZ camera mode, configurable timeouts for other formats). diff --git a/README.md b/README.md index 9dfa508..6b1c236 100644 --- a/README.md +++ b/README.md @@ -1 +1,332 @@ -# bedrock-web-pouch-edv +# @bedrock/vue-optical-scanner + +A Vue.js component library for optical scanning that provides reusable UI components +for barcode, QR code, MRZ (Machine Readable Zone), and other optical code scanning. +This library follows a plugin-based architecture and separates scanning logic from +UI components. + +## Architecture + +This library is part of a two-tier architecture for optical scanning: + +### Core Libraries + +- **@bedrock/web-optical-scanner**: Low-level scanning engine with plugin architecture + - **CameraScanner**: High-level abstraction for camera management and continuous scanning + - **OpticalScanner**: Core scanning engine with plugin system +- **@bedrock/vue-optical-scanner**: Vue-specific UI components (this library) + +### License Key Management + +License keys are managed through bedrock config rather than component props: + +**Why This Design?** + +- **Separation of Concerns:** License keys are deployment/environment concerns, not component concerns +- **Future-Proof:** Makes it easy to swap scanning providers without changing component APIs +- **No Breaking Changes:** Can remove third-party dependencies without affecting component usage +- **Single Source of Truth:** Configure once in app config, use everywhere + +### Architecture Benefits + +#### Separation of Concerns + +- **Vue Layer**: Handles only UI presentation, Quasar integration, and Vue-specific reactivity +- **Web Module**: Manages all scanning logic, camera control, and business rules +- **Clean Delegation**: Vue components pass containers to CameraScanner and handle results via events + +#### Framework Agnostic Design + +- All scanning improvements happen in one place (web module) +- Vue components can be rapidly duplicated for React, Angular, etc. +- Framework wrappers focus only on what frameworks do best (UI, reactivity, styling) + +### Architecture Principles + +#### Plugin-Based Scanning Engine + +The underlying `@bedrock/web-optical-scanner` provides: + +- Async API that accepts elements/images and options, returns scan results +- Multiple scan modes: + - `first`: Resolves as soon as any specified format is detected + - `all`: Resolves when all specified formats have results + - `exhaustive`: Resolves after all plugins have completed their efforts +- Extensible plugin system for adding new scanning formats +- Future-ready for web worker threading (API designed to support this) + +#### Vue UI Components (Thin Wrappers) + +This library provides: + +- **Thin wrapper components** that delegate all scanning complexity to CameraScanner +- Vue-specific UI components focused purely on presentation and framework integration +- Camera display containers that CameraScanner manages internally +- Simple prop-based configuration (scanType, scanMode, etc.) +- Event-based result handling with no business logic in Vue layer +- **Easy duplication**: Components designed for rapid porting to React/other frameworks + +### Supported Formats + +- **QR Codes**: Standard QR code scanning (open-source) +- **PDF417**: Standard PDF417 barcode format (open-source) +- **MRZ**: Machine Readable Zone for passports and ID documents (requires license) + +**Advanced/Legacy:** + +- **PDF417 Enhanced**: Advanced PDF417 with driver license parsing (accessible via `formats` prop + which override scanType prop value.) + - Used for backward compatibility with older implementations or if end client requests to use dynamsoft + for PDF417 scan over open source PDF417 scan. + - Requires Dynamsoft license if using Dynamsoft engine + - Not exposed in public API by default + +## Configuration + +### License Key Setup (Required for MRZ Scanning) + +MRZ scanning requires a Dynamsoft license key. Configure it in bedrock app's config: + +**File:** By default, it is `bedrock-web-optical-scanner/lib/config.js`. However, for a differet app/use case it can be - `your-app/lib/config.js`. + +```javascript +import {config} from '@bedrock/web'; + +config.opticalScanner = { + thirdParty: { + dynamsoft: { + licenseKey: 'YOUR-DYNAMSOFT-LICENSE-KEY-HERE' + } + } +}; + +// Component reads automatically +const licenseKey = getDynamsoftLicense(); // From @bedrock/web-optical-scanner +``` + +**Note:** The component automatically reads the license from config and do not need to pass it as a prop. + +## Components + +### OpticalScanner + +A thin wrapper component that delegates camera and scanning operations to CameraScanner from `@bedrock/web-optical-scanner`. + +**Architecture**: This component provides Vue-specific UI and event handling while CameraScanner handles all scanning complexity internally. + +#### Core Props (All Modes) + +- **`scanType`** (required): `'mrz'`, `'barcode'`, or `'auto'` + - `'mrz'`: Passport/ID card scanning + - `'barcode'`: QR codes and PDF417 barcodes + - `'auto'`: Auto-detect any supported format + +- **`scanMode`**: `'first'` (default), `'all'`, or `'exhaustive'` + - `'first'`: Return as soon as any format is detected + - `'all'`: Return when all specified formats found + - `'exhaustive'`: Let all plugins complete their attempts + +- **`tipText`**: Instruction text shown to users + +#### Barcode-Specific Props + +**Note:** These props only apply to `'barcode'` and `'auto'` modes. `showQrBox` prop value is automatically ignored for `'mrz'` mode (which uses Dynamsoft's native UI). + +- **`showQrBox`** (Boolean, default: `true`): Show yellow scanning overlay guide +- **`torchOn`** (Boolean, default: `false`): Enable camera flashlight on start + +#### Advanced Props + +- **`formats`** (Array|null, default: `null`): Override default formats for scanType + - Example: `:formats="['pdf417_enhanced']"` for legacy driver license parsing + - **Note:** This is for advanced use cases and legacy support only + +**Props:** + +- `scanType` (required): `'mrz'` or `'barcode'` +- `scanMode`: `'first'` (default), `'all'`, or `'exhaustive'` +- `tipText`: Instruction text for users +- `showQrBox`: Boolean to show/hide scanning frame overlay +- `torchOn`: Boolean to control camera flash + +**Events:** + +- `@result`: Emitted when scan is successful +- `@error`: Emitted when scan fails +- `@close`: Emitted when scanner is closed + +### ScannerUI + +Lower-level UI component that handles camera display and controls. + +## Usage + +### Basic Example - Barcode Scanning + +```vue + + + +``` + +### Basic Example - MRZ Scanning (Passport/ID) + +```vue + + + +``` + +**Note:** MRZ scanning requires license key configuration (see Configuration section). + +## Functional Demo App + +ScannerDemo Component + +The ScannerDemo component provides a complete example implementation showing: + +- Document type selection (MRZ vs Barcode) +- Scanner controls and modal integration +- Result display for different scan types +- Error handling + +Key Features Demonstrated: + +- MRZ Scanning: Passport and ID document recognition with field validation +- Barcode Scanning: QR codes, and PDF417 for driver licenses +- Result Processing: Type-specific result handling and display +- Camera Management: Start/stop controls with proper lifecycle management + +Usage in Demo: + +```vue + +``` + +## Development Setup + +### Prerequisites + +- Node.js >= 20 +- npm or yarn + +### Getting Started + +1. Clone and install dependencies: + +```bash +git clone URL +cd vue-optical-scanner +npm install +``` + +2. Start development server: + +```bash +npm run dev +``` + +3. Access the demo: Navigate to `http://localhost:5173` to see the ScannerDemo component in action. + +Development Scripts + +```bash +# Start development server with hot reload +npm run dev + +# Lint code +npm run lint + +# Fix linting issues +npm run lint:fix +``` + +## Understanding Mode-Specific Props + +**Question:** Why doesn't `showQrBox` work for MRZ mode? + +**Answer:** Different scan types use different UI architectures: + +| Scan Type | UI Architecture | Props Available | +|-----------|-----------------|-----------------| +| `barcode` | Vue-controlled video + overlays | `showQrBox`, `torchOn` | +| `mrz` | Dynamsoft native UI | None (Dynamsoft controls UI) | +| `auto` | Vue-controlled video + overlays | `showQrBox`, `torchOn` | + +**Why the difference?** + +- **Barcode mode:** Uses standard browser camera APIs... +- **MRZ mode:** Uses Dynamsoft's specialized document detection... +- **Auto mode:** Attempts all formats using browser APIs... + +**Implementation Detail:** The component uses `effectiveShowQrBox` computed property... + +## Troubleshooting | FAQ + +### QR box doesn't show for MRZ scanning + +**Expected behavior:** MRZ mode uses Dynamsoft's native UI, so Vue overlays (including QR box) are automatically disabled. This is correct behavior. diff --git a/components/OpticalScanner.vue b/components/OpticalScanner.vue new file mode 100644 index 0000000..b897b11 --- /dev/null +++ b/components/OpticalScanner.vue @@ -0,0 +1,373 @@ + + + diff --git a/components/ScannerUI.vue b/components/ScannerUI.vue new file mode 100644 index 0000000..d886cea --- /dev/null +++ b/components/ScannerUI.vue @@ -0,0 +1,612 @@ + + + + + diff --git a/demo/App.vue b/demo/App.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/demo/App.vue @@ -0,0 +1,3 @@ + diff --git a/demo/components/ScannerDemo.vue b/demo/components/ScannerDemo.vue new file mode 100644 index 0000000..35bcb3c --- /dev/null +++ b/demo/components/ScannerDemo.vue @@ -0,0 +1,444 @@ + + + + + diff --git a/demo/main.js b/demo/main.js new file mode 100644 index 0000000..c0a377c --- /dev/null +++ b/demo/main.js @@ -0,0 +1,10 @@ +import App from './App.vue'; +import {createApp} from 'vue'; +import {Quasar} from 'quasar'; +import router from './router'; +import 'quasar/dist/quasar.css'; + +const app = createApp(App); +app.use(router); +app.use(Quasar, {plugins: {}}); +app.mount('#app'); diff --git a/demo/router/index.js b/demo/router/index.js new file mode 100644 index 0000000..f7da1d2 --- /dev/null +++ b/demo/router/index.js @@ -0,0 +1,13 @@ +import {createRouter, createWebHistory} from 'vue-router'; +import HomeView from '@/views/HomeView.vue'; + +const routes = [ + {path: '/', name: 'home', component: HomeView} +]; + +const router = createRouter({ + history: createWebHistory(), + routes +}); + +export default router; diff --git a/demo/views/HomeView.vue b/demo/views/HomeView.vue new file mode 100644 index 0000000..140967c --- /dev/null +++ b/demo/views/HomeView.vue @@ -0,0 +1,13 @@ + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..c3ac859 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vue Optical Scanner Demo + + +
+ + + \ No newline at end of file diff --git a/lib/ConfigStorage.js b/lib/ConfigStorage.js deleted file mode 100644 index a782343..0000000 --- a/lib/ConfigStorage.js +++ /dev/null @@ -1,107 +0,0 @@ -/*! - * Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved. - */ -import {assert} from './assert.js'; -import {createDatabase} from './pouchdb.js'; - -export class ConfigStorage { - constructor({assertConfig = () => {}, collectionName} = {}) { - this.assertConfig = assertConfig; - this.collectionName = collectionName; - this.client = null; - } - - /** - * Initializes a configurations database if it has not already - * been initialized. - * - * @returns {Promise} Settles once the operation completes. - */ - async initialize() { - if(this.client) { - // already initialized - return; - } - - this.client = await createDatabase({name: this.collectionName}); - - // Note: `_id` is populated using `config.id` and serves as the primary - // unique index for this collection - } - - /** - * Establishes a new object by inserting its configuration into storage. - * - * @param {object} options - The options to use. - * @param {object} options.config - The configuration. - * - * @returns {Promise} Resolves to the database record. - */ - async insert({config} = {}) { - this.assertConfig(config); - - // require starting sequence to be 0 - if(config.sequence !== 0) { - throw new Error('Configuration sequence must be "0".'); - } - - // insert config and return record - const record = {_id: config.id, config}; - const result = await this.client.insertOne({doc: record}); - return result.record; - } - - /** - * Updates a config if its sequence number is next. - * - * @param {object} options - The options to use. - * @param {object} options.config - The configuration. - * - * @returns {Promise} Resolves to the database record. - */ - async update({config} = {}) { - this.assertConfig(config); - - const record = {_id: config.id, config}; - const result = await this.client.updateOne({ - doc: record, - query: { - selector: { - _id: config.id, - 'config.sequence': config.sequence - 1 - } - } - }); - if(!result) { - const error = new Error( - 'Could not update configuration. Sequence does not match or ' + - 'configuration does not exist.'); - error.name = 'InvalidStateError'; - throw error; - } - return result.record; - } - - /** - * Gets a configuration. - * - * @param {object} options - The options to use. - * @param {object} options.id - The ID of the configuration. - * - * @returns {Promise} Resolves to the database record. - */ - async get({id} = {}) { - assert.string(id, 'id'); - - const {docs: [record]} = await this.client.find({ - selector: {_id: id}, - limit: 1 - }); - if(!record) { - const error = new Error('Configuration not found.'); - error.name = 'NotFoundError'; - throw error; - } - return record; - } -} diff --git a/lib/Hmac.js b/lib/Hmac.js deleted file mode 100644 index ac2fa74..0000000 --- a/lib/Hmac.js +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import {assert} from './assert.js'; - -// eslint-disable-next-line no-undef -const {crypto} = globalThis; - -const ALGORITHM = {name: 'HMAC', hash: {name: 'SHA-256'}, length: 256}; -const EXTRACTABLE = true; -const KEY_USAGE = ['sign', 'verify']; - -export class Hmac { - constructor({key} = {}) { - this.key = key; - this.algorithm = 'HS256'; - this.type = 'Sha256HmacKey2019'; - } - - /** - * Signs some data. - * - * @param {object} options - The options to use. - * @param {Uint8Array} options.data - The data to sign. - * - * @returns {Promise} The signature. - */ - async sign({data} = {}) { - assert.uint8Array(data, 'data'); - const key = this.key; - return new Uint8Array(await crypto.subtle.sign(key.algorithm, key, data)); - } - - /** - * Verifies some data. - * - * @param {object} options - The options to use. - * @param {Uint8Array} options.data - The data to sign as a Uint8Array. - * @param {Uint8Array} options.signature - The Uint8Array signature - * to verify. - * - * @returns {Promise} `true` if verified, `false` if not. - */ - async verify({data, signature} = {}) { - assert.uint8Array(data, 'data'); - assert.uint8Array(signature, 'signature'); - const key = this.key; - return crypto.subtle.verify(key.algorithm, key, signature, data); - } - - static async generate() { - const key = await crypto.subtle.generateKey( - ALGORITHM, EXTRACTABLE, KEY_USAGE); - return new Hmac({key}); - } - - static async import({secret} = {}) { - assert.uint8Array(secret, 'secret'); - const key = await crypto.subtle.importKey( - 'raw', secret, ALGORITHM, EXTRACTABLE, KEY_USAGE); - return new Hmac({key}); - } -} diff --git a/lib/Kek.js b/lib/Kek.js deleted file mode 100644 index 1ff02be..0000000 --- a/lib/Kek.js +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -// eslint-disable-next-line no-undef -const {crypto} = globalThis; - -const EXTRACTABLE = true; - -export class Kek { - constructor({key} = {}) { - this.key = key; - } - - /** - * Wraps a cryptographic key. - * - * @param {object} options - The options to use. - * @param {Uint8Array|CryptoKey} options.unwrappedKey - The key material as a - * `Uint8Array` or CryptoKey. - * - * @returns {Promise} - The wrapped key bytes. - */ - async wrapKey({unwrappedKey} = {}) { - const kek = this.key; - - if(unwrappedKey instanceof Uint8Array) { - // Note: `HMAC` algorithm name doesn't matter; will be exported raw. - const extractable = true; - unwrappedKey = await crypto.subtle.importKey( - 'raw', unwrappedKey, { - name: 'HMAC', hash: 'SHA-512', length: unwrappedKey.length * 8 - }, - // key usage of `sign` refers to the key that is to be wrapped not - // the KEK itself; we just treat it like an HMAC key regardless of - // what it is - extractable, ['sign']); - } - const wrappedKey = await crypto.subtle.wrapKey( - 'raw', unwrappedKey, kek, kek.algorithm); - return new Uint8Array(wrappedKey); - } - - /** - * Unwraps a cryptographic key. - * - * @param {object} options - The options to use. - * @param {Uint8Array} options.wrappedKey - The wrapped key material. - * - * @returns {Promise} - Resolves to the key bytes or null if - * the unwrapping fails because the key does not match. - */ - async unwrapKey({wrappedKey}) { - const kek = this.key; - // Note: `HMAC` algorithm name doesn't matter; will be exported raw. - try { - const key = await crypto.subtle.unwrapKey( - 'raw', wrappedKey, kek, kek.algorithm, - // key usage of `encrypt` refers to the key that is being unwrapped; - // we just treat it like an HMAC key regardless of what it is - {name: 'HMAC', hash: 'SHA-512'}, EXTRACTABLE, ['sign']); - const keyBytes = await crypto.subtle.exportKey('raw', key); - return new Uint8Array(keyBytes); - } catch(e) { - // unwrapping key failed - return null; - } - } - - static async import({secret} = {}) { - const key = await crypto.subtle.importKey( - 'raw', secret, {name: 'AES-KW', length: 256}, EXTRACTABLE, - ['wrapKey', 'unwrapKey']); - return new Kek({key}); - } -} diff --git a/lib/P256Kak.js b/lib/P256Kak.js deleted file mode 100644 index 965d888..0000000 --- a/lib/P256Kak.js +++ /dev/null @@ -1,59 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey'; -import {assert} from './assert.js'; - -export class P256Kak { - constructor({id, keyPair} = {}) { - this.id = id; - this.keyPair = keyPair; - this.type = 'Multikey'; - } - - /** - * Derives a shared secret via the given peer public key, typically for use - * as one parameter for computing a shared key. It should not be used as - * a shared key itself, but rather input into a key derivation function (KDF) - * to produce a shared key. - * - * @param {object} options - The options to use. - * @param {object} options.publicKey - The public key to compute the shared - * secret against; the public key type must match this KeyAgreementKey's - * type. - * - * @returns {Promise} The shared secret bytes. - */ - async deriveSecret({publicKey} = {}) { - assert.object(publicKey); - if(publicKey.type !== this.type) { - throw Error( - `The given public key type "${publicKey.type}" does not match this ` + - `key agreement key's ${this.type}.`); - } - const {publicKeyMultibase} = publicKey; - assert.string(publicKeyMultibase); - return this.keyPair.deriveSecret({publicKey}); - } - - async export({publicKey = true, privateKey = false} = {}) { - const {id, type, keyPair} = this; - const exported = await keyPair.export( - {publicKey, secretKey: privateKey, includeContext: true}); - exported.id = id; - exported.type = type; - return exported; - } - - static async generate() { - const keyPair = await EcdsaMultikey.generate( - {curve: 'P-256', keyAgreement: true}); - return new P256Kak({keyPair}); - } - - static async import({secretKey, publicKey} = {}) { - const keyPair = await EcdsaMultikey.fromRaw( - {curve: 'P-256', secretKey, publicKey, keyAgreement: true}); - return new P256Kak({keyPair}); - } -} diff --git a/lib/PouchEdvClient.js b/lib/PouchEdvClient.js deleted file mode 100644 index 3020537..0000000 --- a/lib/PouchEdvClient.js +++ /dev/null @@ -1,344 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -import * as edvs from './edvs.js'; -import * as secrets from './secrets.js'; -import {assert} from './assert.js'; -import {EdvClientCore} from '@digitalbazaar/edv-client'; -import {initialize} from './initialize.js'; -import {PouchTransport} from './PouchTransport.js'; - -export class PouchEdvClient extends EdvClientCore { - /** - * Creates a new EDV client for connecting to an Encrypted Data Vault (EDV) - * that is stored in PouchDB. - * - * @param {object} options - The options to use. - * @param {object} [options.hmac] - A default HMAC API for blinding - * indexable attributes. - * @param {string} [options.id] - The ID of the EDV. - * @param {object} [options.keyAgreementKey] - A default KeyAgreementKey - * API for deriving shared KEKs for wrapping content encryption keys. - * @param {Function} [options.keyResolver] - A default function that returns - * a Promise that resolves a key ID to a DH public key. - * @param {string} [options.cipherVersion='recommended'] - Sets the cipher - * version to either "recommended" or "fips". - * - * @returns {PouchEdvClient} - PouchEdvClient. - */ - constructor({hmac, id, keyAgreementKey, keyResolver, cipherVersion} = {}) { - super({hmac, id, keyAgreementKey, keyResolver, cipherVersion}); - this.transport = new PouchTransport({edvId: this.id}); - // create a transport for marking tombstoned EDV docs as deleted to - // improve pouchdb index performance - this._deleteTransport = Object.create(this.transport); - this._deleteTransport.update = async ({encrypted}) => - this.transport.update({encrypted, deleted: true}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to insert. - * @param {ReadableStream} [options.stream] - A WHATWG Readable stream to read - * from to associate chunked data with this document. - * @param {number} [options.chunkSize=1048576] - The size, in bytes, of the - * chunks to break the incoming stream data into. - * @param {object[]} [options.recipients=[]] - A set of JWE recipients - * to encrypt the document for; if not present, a default recipient will - * be added using `this.keyAgreementKey` and if no `keyAgreementKey` is - * set, an error will be thrown. - * - * @returns {Promise} - Resolves to the inserted document. - */ - async insert({ - doc, stream, chunkSize, recipients = [] - } = {}) { - const {transport} = this; - return super.insert({doc, stream, chunkSize, recipients, transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to insert. - * @param {ReadableStream} [options.stream] - A WHATWG Readable stream to read - * from to associate chunked data with this document. - * @param {number} [options.chunkSize=1048576] - The size, in bytes, of the - * chunks to break the incoming stream data into. - * @param {object} [options.recipients=[]] - A set of JWE recipients to - * encrypt the document for; if present, recipients will be added to any - * existing recipients; to remove existing recipients, modify the - * `encryptedDoc.jwe.recipients` field. - * - * @returns {Promise} - Resolves to the updated document. - */ - async update({doc, stream, chunkSize, recipients = []} = {}) { - const {transport} = this; - return super.update({doc, stream, chunkSize, recipients, transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to delete. - * @param {object} [options.recipients=[]] - A set of JWE recipients to - * encrypt the document for; if present, recipients will be added to - * any existing recipients; to remove existing recipients, modify - * the `encryptedDoc.jwe.recipients` field. - * - * @returns {Promise} - Resolves to `true` if the document was - * deleted. - */ - async delete({doc, recipients = []} = {}) { - const {_deleteTransport} = this; - return super.delete({doc, recipients, transport: _deleteTransport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {string} options.id - The ID of the document to get. - * - * @returns {Promise} - Resolves to the document. - */ - async get({id} = {}) { - const {transport} = this; - return super.get({id, transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to get a stream for. - * - * @returns {Promise} - Resolves to a `ReadableStream` to - * read the chunked data from. - */ - async getStream({doc} = {}) { - const {transport} = this; - return super.getStream({doc, transport}); - } - - /** - * @inheritdoc - * - * @see find - For more detailed documentation on the search options. - * - * @param {object} options - The options to use. - * @param {object|Array} [options.equals] - An object with key-value - * attribute pairs to match or an array of such objects. - * @param {string|Array} [options.has] - A string with an attribute name to - * match or an array of such strings. - * - * @returns {Promise} - Resolves to the number of matching documents. - */ - async count({equals, has} = {}) { - const {transport} = this; - return super.count({equals, has, transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object|Array} [options.equals] - An object with key-value - * attribute pairs to match or an array of such objects. - * @param {string|Array} [options.has] - A string with an attribute name to - * match or an array of such strings. - * @param {boolean} [options.count] - Set to `false` to find all documents - * that match a query or to `true` to give a count of documents. - * @param {number} [options.limit] - Set to limit the number of documents - * to be returned from a query (min=1, max=1000). - * - * @returns {Promise} - Resolves to the matching documents: - * {documents: [...]}. - */ - async find({equals, has, count = false, limit} = {}) { - const {transport} = this; - return super.find({equals, has, count, limit, transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * - * @returns {Promise} - Resolves to the configuration for the EDV. - */ - async getConfig({} = {}) { - const {transport} = this; - return super.getConfig({transport}); - } - - /** - * @inheritdoc - * - * @param {object} options - The options to use. - * @param {object} options.config - The new EDV config. - * - * @returns {Promise} - Resolves once the operation completes. - */ - async updateConfig({config} = {}) { - const {transport} = this; - return super.updateConfig({config, transport}); - } - - /** - * Creates a new EDV using the given configuration. - * - * @param {object} options - The options to use. - * @param {string} options.config - The EDV's configuration. - * @param {string} [options.password] - A password to use if the keys for - * the EDV should be generated and stored locally in encrypted storage. - * @param {string} [options.cipherVersion='recommended'] - Sets the cipher - * version to either "recommended" or "fips". - * - * @returns {Promise} - Resolves to an object with: - * `{config, edvClient}`; `edvClient` will be set if `password` is passed. - */ - static async createEdv({config, password, cipherVersion} = {}) { - // initialize EDV databases (if not already initialized) - await initialize(); - - let edvClient; - if(password !== undefined) { - if(config.hmac || config.keyAgreementKey) { - throw new Error( - '"config" must not have "hmac" or "keyAgreementKey" if these are ' + - 'to be populated using locally generated secrets.'); - } - - // generate encrypted secret, use the EDV ID as the secret's ID ... if - // it already exists, try to reuse it - const {hmac, keyAgreementKey} = await _lazyCreateSecret({ - id: config.id, password, cipherVersion - }); - config = { - ...config, - hmac: {id: hmac.id, type: hmac.type}, - keyAgreementKey: {id: keyAgreementKey.id, type: keyAgreementKey.type} - }; - edvClient = new PouchEdvClient({ - hmac, id: config.id, keyAgreementKey, - keyResolver: await _createKeyResolver({ - keyAgreementKey, edvConfig: config - }), - cipherVersion - }); - } - - assert.edvConfig(config); - const transport = new PouchTransport({edvId: config.id}); - const newConfig = await transport.createEdv({config}); - return {config: newConfig, edvClient}; - } - - /** - * Creates a new EDV client based on encrypted secrets saved in a local - * PouchDB instance. The password to decrypt the secrets must be given. - * - * @param {object} options - The options to use. - * @param {string} [options.edvId] - The ID of the EDV. - * @param {string} [options.password] - The password to use to decrypt - * the secrets. - * - * @returns {PouchEdvClient} - PouchEdvClient. - */ - static async fromLocalSecrets({edvId, password} = {}) { - // initialize EDV databases (if not already initialized) - await initialize(); - - // start getting EDV config - const edvConfigPromise = edvs.get({id: edvId}); - edvConfigPromise.catch(e => e); - - // load secret using the EDV ID as the secret ID - const {config} = await secrets.get({id: edvId}); - const result = await secrets.decrypt({config, password}); - if(!result) { - throw new Error('Invalid password.'); - } - const {hmac, keyAgreementKey, cipherVersion} = result; - - // finish getting EDV config - const edvConfigResult = await edvConfigPromise; - if(edvConfigResult instanceof Error) { - throw edvConfigResult; - } - const {config: edvConfig} = edvConfigResult; - - return new PouchEdvClient({ - hmac, id: config.id, keyAgreementKey, - keyResolver: await _createKeyResolver({keyAgreementKey, edvConfig}), - cipherVersion - }); - } - - /** - * Generates a multibase encoded random 128-bit identifier for a document. - * - * @returns {Promise} - Resolves to the identifier. - */ - static async generateId() { - return EdvClientCore.generateId(); - } -} - -async function _createKeyResolver({keyAgreementKey, edvConfig} = {}) { - // create key resolver for the EDV's key agreement key (and no other keys) - const key = await keyAgreementKey.export(); - key.controller = edvConfig.controller; - return async function keyResolver({id}) { - if(id === keyAgreementKey.id) { - return key; - } - const error = new Error('Key not found.'); - error.name = 'NotFoundError'; - throw error; - }; -} - -// called during EDV creation -async function _lazyCreateSecret({id, password, cipherVersion} = {}) { - // generate encrypted secret, use the EDV ID as the secret's ID - const {hmac, keyAgreementKey, config} = await secrets.generate( - {id, password, cipherVersion}); - try { - await secrets.insert({config}); - return {hmac, keyAgreementKey, config}; - } catch(e) { - if(e.name === 'ConstraintError') { - // secret already exists... if EDV config already exists, throw a - // duplicate error - try { - await edvs.get({id}); - const error = new Error('Duplicate EDV configuration.'); - error.name = 'DuplicateError'; - throw error; - } catch(e) { - if(e.name !== 'NotFoundError') { - throw e; - } - } - - // try to load existing secret and reuse it as it doesn't have a - // matching EDV config yet - const {config} = await secrets.get({id}); - const result = await secrets.decrypt({config, password}); - if(!result) { - throw new Error( - `Secret already exists for EDV ID (${id}) but password to unlock ` + - 'it is invalid.'); - } - const {hmac, keyAgreementKey} = result; - return {hmac, keyAgreementKey, config}; - } - throw e; - } -} diff --git a/lib/PouchTransport.js b/lib/PouchTransport.js deleted file mode 100644 index 7a3c8c2..0000000 --- a/lib/PouchTransport.js +++ /dev/null @@ -1,140 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import * as chunks from './chunks.js'; -import * as docs from './docs.js'; -import * as edvs from './edvs.js'; - -export class PouchTransport { - /** - * Creates a transport layer for an EDV client to use to perform an - * operation with an EDV stored using PouchDB. - * - * @param {object} options - The options to use. - * @param {string} [options.edvId] - The ID of the target EDV. - * - * @returns {PouchTransport} - PouchTransport. - */ - constructor({edvId} = {}) { - this.edvId = edvId; - } - - /** - * @inheritdoc - */ - async createEdv({config} = {}) { - try { - const record = await edvs.insert({config}); - return record.config; - } catch(e) { - if(e.name === 'ConstraintError') { - const err = new Error('Duplicate error.'); - err.name = 'DuplicateError'; - err.cause = e; - throw err; - } - throw e; - } - } - - /** - * @inheritdoc - */ - async getConfig({id = this.edvId} = {}) { - const record = await edvs.get({id}); - return record.config; - } - - /** - * @inheritdoc - */ - async updateConfig({config} = {}) { - const record = await edvs.update({config}); - return record.config; - } - - /** - * @inheritdoc - */ - async insert({encrypted} = {}) { - try { - await docs.insert({edvId: this.edvId, doc: encrypted}); - } catch(e) { - if(e.name === 'ConstraintError') { - const err = new Error('Duplicate error.'); - err.name = 'DuplicateError'; - err.cause = e; - throw err; - } - throw e; - } - } - - /** - * @inheritdoc - */ - async update({encrypted, deleted = false} = {}) { - try { - await docs.upsert({edvId: this.edvId, doc: encrypted, deleted}); - } catch(e) { - if(e.name === 'ConstraintError') { - const err = new Error('Duplicate error.'); - err.name = 'DuplicateError'; - err.cause = e; - throw err; - } - throw e; - } - } - - /** - * @inheritdoc - */ - async get({id} = {}) { - const record = await docs.get({edvId: this.edvId, id}); - return record.doc; - } - - /** - * @inheritdoc - */ - async find({query} = {}) { - const {edvId} = this; - const findQuery = docs.createQuery({edvId, edvQuery: query}); - const {limit} = query; - if(limit) { - // add `1` to limit to detect if more results were possible - findQuery.options.limit = limit + 1; - } - const {records} = await docs.find({edvId, query: findQuery}); - if(query.count === true) { - return {count: records.length}; - } - const result = {documents: records}; - if(limit) { - result.hasMore = result.documents.length > limit; - if(result.hasMore) { - result.documents.length = limit; - } - } - result.documents = result.documents.map(({doc}) => doc); - return result; - } - - /** - * @inheritdoc - */ - async storeChunk({docId, chunk}) { - return chunks.upsert({edvId: this.edvId, docId, chunk}); - } - - /** - * @inheritdoc - */ - async getChunk({docId, chunkIndex} = {}) { - const record = await chunks.get({ - edvId: this.edvId, docId, index: chunkIndex - }); - return record.doc; - } -} diff --git a/lib/X25519Kak.js b/lib/X25519Kak.js deleted file mode 100644 index 827e401..0000000 --- a/lib/X25519Kak.js +++ /dev/null @@ -1,87 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -import {multibaseDecode, multibaseEncode} from './helpers.js'; -import {assert} from './assert.js'; -import nacl from 'tweetnacl'; - -const CONTEXT_URL = 'https://w3id.org/security/suites/x25519-2020/v1'; -// multicodec x25519-pub header as varint -const MULTICODEC_X25519_PUB_HEADER = new Uint8Array([0xec, 0x01]); -// multicodec x25519-priv header as varint -const MULTICODEC_X25519_PRIV_HEADER = new Uint8Array([0x82, 0x26]); - -export class X25519Kak { - constructor({id, keyPair} = {}) { - this.id = id; - this.keyPair = keyPair; - this.type = 'X25519KeyAgreementKey2020'; - } - - /** - * Derives a shared secret via the given peer public key, typically for use - * as one parameter for computing a shared key. It should not be used as - * a shared key itself, but rather input into a key derivation function (KDF) - * to produce a shared key. - * - * @param {object} options - The options to use. - * @param {object} options.publicKey - The public key to compute the shared - * secret against; the public key type must match this KeyAgreementKey's - * type. - * - * @returns {Promise} The shared secret bytes. - */ - async deriveSecret({publicKey} = {}) { - assert.object(publicKey); - if(publicKey.type !== this.type) { - throw Error( - `The given public key type "${publicKey.type}" does not match this ` + - `key agreement key's ${this.type}.`); - } - const {publicKeyMultibase} = publicKey; - assert.string(publicKeyMultibase); - - // get public key bytes as remote public key - const remotePublicKey = multibaseDecode({ - expectedHeader: MULTICODEC_X25519_PUB_HEADER, - encoded: publicKeyMultibase - }); - - // `scalarMult` takes secret key as param 1, public key as param 2 - const {keyPair: {privateKey}} = this; - return nacl.scalarMult(privateKey, remotePublicKey); - } - - async export({publicKey = true, privateKey = false} = {}) { - const {id, type, keyPair} = this; - const exported = { - '@context': CONTEXT_URL, - id, - type - }; - if(publicKey) { - exported.publicKeyMultibase = multibaseEncode({ - header: MULTICODEC_X25519_PUB_HEADER, data: keyPair.publicKey - }); - } - if(privateKey) { - exported.privateKeyMultibase = multibaseEncode({ - header: MULTICODEC_X25519_PRIV_HEADER, data: keyPair.privateKey - }); - } - return exported; - } - - static async generate() { - const {secretKey: privateKey, publicKey} = nacl.box.keyPair(); - const keyPair = {privateKey, publicKey}; - return new X25519Kak({keyPair}); - } - - static async import({secret} = {}) { - assert.uint8Array(secret, 'secret'); - const {publicKey} = nacl.box.keyPair.fromSecretKey(secret); - const keyPair = {privateKey: new Uint8Array(secret).slice(), publicKey}; - return new X25519Kak({keyPair}); - } -} diff --git a/lib/assert.js b/lib/assert.js deleted file mode 100644 index 6188107..0000000 --- a/lib/assert.js +++ /dev/null @@ -1,170 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import * as base58 from 'base58-universal'; - -export const assert = { - array, chunk, doc, edvConfig, edvQuery, localId, nonNegativeSafeInteger, - number, object, secretConfig, sequence, string, uint8Array -}; - -function array(x, name) { - if(!(x && Array.isArray(x))) { - throw new TypeError(`"${name}" must be an array.`); - } -} - -function chunk(x) { - assert.object(x, 'chunk'); - assert.nonNegativeSafeInteger(x.index, 'chunk.index'); - assert.nonNegativeSafeInteger(x.offset, 'chunk.offset'); - assert.sequence(x.sequence, 'chunk.sequence'); - assert.object(x.jwe, 'chunk.jwe'); -} - -function doc(x) { - assert.object(x, 'doc'); - assert.localId(x.id, 'doc.id'); - assert.sequence(x.sequence, 'doc.sequence'); - assert.object(x.jwe, 'doc.jwe'); - if(x.indexed !== undefined) { - assert.array(x.indexed, 'doc.indexed'); - } -} - -function edvConfig(x) { - assert.object(x, 'config'); - assert.string(x.id, 'config.id'); - assert.string(x.controller, 'config.controller'); - assert.sequence(x.sequence, 'config.sequence'); - - assert.object(x.keyAgreementKey, 'config.keyAgreementKey'); - assert.object(x.hmac, 'config.hmac'); - if(x.keyAgreementKey) { - assert.string(x.keyAgreementKey.id, 'config.keyAgreementKey.id'); - assert.string(x.keyAgreementKey.type, 'config.keyAgreementKey.type'); - } - if(x.hmac) { - assert.string(x.hmac.id, 'config.hmac.id'); - assert.string(x.hmac.type, 'config.hmac.type'); - } -} - -function edvQuery(x, name) { - assert.object(x, name); - const {index, equals, has, count, limit} = x; - assert.string(index, `${name}.index`); - if(!!equals === !!has) { - throw new TypeError( - `"${name}" must contain exactly one of "equals" or "has".`); - } - if(equals !== undefined) { - assert.array(equals, `${name}.equals`); - if(equals.length === 0) { - throw new Error(`"${name}.equals" must have of a length > 0.`); - } - // every `equals` entry must be an object w/key+value strings - for(const [i, e] of equals.entries()) { - const subName = `${name}.equals[${i}]`; - assert.object(e, subName); - for(const key in e) { - assert.string(e[key], `${subName}.${key}`); - } - } - } - if(has !== undefined) { - assert.array(has, `${name}.has`); - if(has.length === 0) { - throw new Error(`"${name}.equals" must have of a length > 0.`); - } - // every `has` entry must be a string - for(const [i, e] of has.entries()) { - assert.string(e, `${name}.equals[${i}]`); - } - } - if(count !== undefined && typeof count !== 'boolean') { - throw new TypeError(`"${name}.count" must be a boolean.`); - } - if(limit !== undefined) { - assert.number(limit, `${name}.limit`); - if(!(limit >= 1 && limit <= 1000)) { - throw new Error(`"${name}.limit" must be an integer >= 1 and <= 1000.`); - } - } -} - -function localId(x, name) { - assert.string(x, name); - - try { - // verify ID is base58-encoded multibase multihash encoded 16 bytes - const buf = base58.decode(x.substr(1)); - // multibase base58 (starts with 'z') - // 128-bit random number, identity multihash encoded - // 0x00 = identity tag, 0x10 = length (16 bytes) + 16 random bytes - if(!(x.startsWith('z') && - buf.length === 18 && buf[0] === 0x00 && buf[1] === 0x10)) { - throw new Error('Invalid identifier.'); - } - } catch(e) { - const error = new Error( - `Identifier "${x}" must be base58-encoded multibase, ` + - 'multihash array of 16 random bytes.'); - error.name = 'ConstraintError'; - throw error; - } -} - -function nonNegativeSafeInteger(x, name) { - if(!(x >= 0 && Number.isSafeInteger(x))) { - throw new TypeError(`"${name}" must be a non-negative safe integer.`); - } -} - -function number(x, name) { - _assertType({x, name, type: 'number', article: 'a'}); -} - -function object(x, name) { - _assertType({x, name, type: 'object', article: 'an', truthy: true}); -} - -function secretConfig(x) { - assert.object(x, 'config'); - assert.string(x.id, 'config.id'); - assert.string(x.hmacId, 'config.hmacId'); - assert.string(x.keyAgreementKeyId, 'config.keyAgreementKeyId'); - assert.object(x.secret, 'config.secret'); - assert.string(x.secret.version, 'config.secret.version'); - if(x.secret.version !== '1') { - throw new Error('"config.secret.version" must be "1".'); - } - assert.string(x.secret.salt, 'config.secret.salt'); - assert.string(x.secret.wrappedKey, 'config.secret.wrappedKey'); - assert.sequence(x.sequence, 'config.sequence'); -} - -function sequence(x, name) { - // `sequence` is limited to MAX_SAFE_INTEGER - 1 - assert.nonNegativeSafeInteger(x, name); - if(x === Number.MAX_SAFE_INTEGER) { - throw new TypeError( - `"${name}" must be less than ${Number.MAX_SAFE_INTEGER}.`); - } -} - -function string(x, name) { - _assertType({x, name, type: 'string', article: 'a'}); -} - -function uint8Array(x, name) { - if(!(x instanceof Uint8Array)) { - throw new TypeError(`"${name}" must be a Uint8Array.`); - } -} - -function _assertType({x, name, type, article, truthy}) { - if(!(typeof x === type && (x || !truthy))) { - throw new TypeError(`"${name}" must be ${article} ${type}.`); - } -} diff --git a/lib/chunks.js b/lib/chunks.js deleted file mode 100644 index f149461..0000000 --- a/lib/chunks.js +++ /dev/null @@ -1,178 +0,0 @@ -/*! - * Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved. - */ -import {createDatabase, purge} from './pouchdb.js'; -import {assert} from './assert.js'; -import {get as getDoc} from './docs.js'; -import {parseLocalId} from './helpers.js'; - -const COLLECTION_NAME = 'edv-storage-chunk'; - -let _client; -let _purgeOp; - -/** - * Initializes the encrypted document chunks database if it has not already - * been initialized. - * - * @returns {Promise} Settles once the operation completes. - */ -export async function initialize() { - if(_client) { - // already initialized - return; - } - - _client = await createDatabase({name: COLLECTION_NAME}); - - // Note: `_id` is populated using the combination of `localEdvId`, - // `docId` and `chunk.index` and serves as the primary unique index for - // this collection - - // since no indexes are created, get database info to ensure database upgrade - // events fire and object stores are created, etc. prior to scheduling a - // purge -- otherwise database upgrades could be blocked and the database - // will fail to load / be created properly - await _client.info(); - - // schedule purge op to clean up any deleted docs - _schedulePurge(); -} - -/** - * Updates (replaces) an EDV document chunk. If the document chunk does not - * exist, it will be inserted. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV to store the chunk in. - * @param {string} options.docId - The ID of the document the chunk is - * associated with. - * @param {object} options.chunk - The chunk to store. - * - * @returns {Promise} Resolves to the database record. - */ -export async function upsert({edvId, docId, chunk} = {}) { - assert.string(edvId, 'edvId'); - assert.localId(docId, 'docId'); - assert.chunk(chunk); - - // TODO: implement garbage collector worker that removes chunks with stale - // sequences (e.g., can happen because uploads failed or because associated - // data shrunk in size, i.e., fewer chunks) - - // ensure `chunk.sequence` is proper (on par with associated doc) - // TODO: optimize retrieval of only sequence number - const {doc} = await getDoc({edvId, id: docId}); - if(chunk.sequence !== doc.sequence) { - const error = new Error( - 'Could not update document chunk. Sequence does not match the ' + - 'associated document.'); - error.name = 'InvalidStateError'; - error.expected = doc.sequence; - error.actual = chunk.sequence; - throw error; - } - - // create record - const {localId: localEdvId} = parseLocalId({id: edvId}); - const _id = _createId({localEdvId, docId, index: chunk.index}); - const record = {_id, localEdvId, docId, chunk}; - - let result; - try { - result = await _client.updateOne({ - doc: record, - query: { - selector: {_id} - }, - upsert: true - }); - } catch(e) { - if(e.name === 'ConstraintError') { - // if the error was with the same document, then the same chunk was - // upserted concurrently -- and we treat this one as if it succeeded - // but was overwritten by whatever is in the database now - if(e.existing._id === _id) { - return e.existing; - } - } - throw e; - } - return result.record; -} - -/** - * Gets an EDV document chunk. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV. - * @param {string} options.docId - The ID of the document the chunk is - * associated with. - * @param {number} options.index - The index of the chunk. - * - * @returns {Promise} Resolves to the database record. - */ -export async function get({edvId, docId, index} = {}) { - assert.string(edvId, 'edvId'); - assert.localId(docId, 'docId'); - assert.nonNegativeSafeInteger(index, 'index'); - - const {localId: localEdvId} = parseLocalId({id: edvId}); - const {docs: [record]} = await _client.find({ - selector: {_id: _createId({localEdvId, docId, index})}, - limit: 1 - }); - if(!record) { - const error = new Error('Document chunk not found.'); - error.name = 'NotFoundError'; - throw error; - } - return record; -} - -/** - * Removes an EDV document chunk. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV. - * @param {string} options.docId - The ID of the document the chunk is - * associated with. - * @param {number} options.index - The index of the chunk. - * - * @returns {Promise} `true` if the chunk was removed, `false` if it - * was not found. - */ -export async function remove({edvId, docId, index} = {}) { - // sadly, this is non-atomic because PouchDB does not offer the necessary - // primitives - let record; - try { - record = await get({edvId, docId, index}); - } catch(e) { - if(e.name === 'NotFoundError') { - return false; - } - } - - // delete record - record._deleted = true; - await _client.put(record); - - // schedule purge operation to clean up deleted docs - _schedulePurge(); - - return true; -} - -function _createId({localEdvId, docId, index}) { - return `${localEdvId}:${docId}:${index}`; -} - -function _schedulePurge() { - if(_purgeOp) { - return; - } - _purgeOp = purge({name: COLLECTION_NAME}) - .catch(e => console.error(e)) - .finally(() => _purgeOp = null); -} diff --git a/lib/docs.js b/lib/docs.js deleted file mode 100644 index e36719c..0000000 --- a/lib/docs.js +++ /dev/null @@ -1,345 +0,0 @@ -/*! - * Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved. - */ -import {createDatabase, purge} from './pouchdb.js'; -import {assert} from './assert.js'; -import {parseLocalId} from './helpers.js'; - -const COLLECTION_NAME = 'edv-storage-doc'; - -let _client; -let _purgeOp; - -/** - * Initializes the encrypted documents database if it has not already - * been initialized. - * - * @returns {Promise} Settles once the operation completes. - */ -export async function initialize() { - if(_client) { - // already initialized - return; - } - - _client = await createDatabase({name: COLLECTION_NAME}); - - /* Note: `_id` is populated using the combination of `localEdvId` and - `doc.id` and serves as the primary unique index for this collection. - - Additionally, index information from each encrypted document is massaged to - work within the limitations of PouchDB's indexing system. For example, since - PouchDB does not support deep referencing fields within arrays (only within - objects), the `doc.indexed` array must be transformed to enable indexing - here. That array is transformed into three different arrays that are stored - at the top level of each record in the database: - - `attributes` - Holds the names and values of every encrypted attribute and - value pair, enabling queries that check full attributes to be checked. - - `attributeNames` - Holds the names of every encrypted attribute so that - queries that just check for the presence of attributes (but not their - values) can be performed. - - `uniqueAttributes` - Holds the names and values of every encrypted attribute - that is marked as `unique`. This allows unique constraints to be applied - to encrypted attributes. - */ - - // attribute name + value queries - await _client.createIndex({ - index: { - ddoc: 'edv-doc', - name: 'attributes', - fields: [ - 'localEdvId', - 'attributes' - ], - partial_filter_selector: { - attributes: {$exists: true} - } - } - }); - - // attribute name queries - await _client.createIndex({ - index: { - ddoc: 'edv-doc', - name: 'attributes.name', - fields: [ - 'localEdvId', - 'attributeNames' - ], - partial_filter_selector: { - attributeNames: {$exists: true} - } - } - }); - - // used to enforce uniqueness on insert / update - await _client.createIndex({ - index: { - ddoc: 'edv-doc', - name: 'attributes.unique', - fields: [ - 'localEdvId', - 'uniqueAttributes' - ], - partial_filter_selector: { - uniqueAttributes: {$exists: true} - } - } - }); - - // schedule purge op to clean up any deleted docs - _schedulePurge(); -} - -/** - * Inserts an EDV document. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV to store the document in. - * @param {object} options.doc - The document to insert. - * - * @returns {Promise} Resolves to the database record. - */ -export async function insert({edvId, doc} = {}) { - assert.string(edvId, 'edvId'); - assert.doc(doc); - - // create record - const {record, uniqueConstraints} = _createRecord({edvId, doc}); - - // insert and return updated record - const result = await _client.insertOne({doc: record, uniqueConstraints}); - return result.record; -} - -/** - * Updates (replaces) an EDV document. If the document does not exist, it will - * be inserted. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV to store the document in. - * @param {object} options.doc - The document to store. - * @param {boolean} [options.deleted=false] - Set to `true` if the EDV - * document is a tombstone, i.e., it has been deleted. - * - * @returns {Promise} Resolves to the database record. - */ -export async function upsert({edvId, doc, deleted = false} = {}) { - assert.string(edvId, 'edvId'); - assert.doc(doc); - - // create record - const {localEdvId, record, uniqueConstraints} = _createRecord({edvId, doc}); - const _id = _createId({localEdvId, docId: doc.id}); - - if(deleted) { - // mark record as deleted; important to improve pouchdb indexing speed - record._deleted = true; - } - - let result; - try { - result = await _client.updateOne({ - doc: record, - query: { - selector: { - _id, - 'doc.sequence': doc.sequence - 1 - } - }, - upsert: true, - uniqueConstraints - }); - } catch(e) { - if(e.name === 'ConstraintError') { - // if the error was with the same document, then the sequence did not - // match - if(e.existing._id === _id) { - const error = new Error( - 'Could not update document. Sequence does not match.'); - error.name = 'InvalidStateError'; - throw error; - } - } - throw e; - } - - if(deleted) { - // schedule purge operation to clean up deleted docs - _schedulePurge(); - } - - return result.record; -} - -/** - * Gets an EDV document. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV. - * @param {string} options.id - The ID of the document. - * - * @returns {Promise} Resolves to the database record. - */ -export async function get({edvId, id} = {}) { - assert.string(edvId, 'edvId'); - assert.localId(id, 'id'); - - const {localId: localEdvId} = parseLocalId({id: edvId}); - const {docs: [record]} = await _client.find({ - selector: {_id: _createId({localEdvId, docId: id})}, - limit: 1 - }); - if(!record) { - const error = new Error('Document not found.'); - error.name = 'NotFoundError'; - throw error; - } - return record; -} - -/** - * Retrieves all EDV documents matching the given query. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV. - * @param {object} [options.query={}] - The query to use with `selector` and - * `options`, etc. - * - * @returns {Promise} Resolves with the records that matched the query. - */ -export async function find({edvId, query = {selector: {}}}) { - assert.object(query); - assert.object(query.selector); - - // force local EDV ID to be in query if not present - let {selector} = query; - if(!selector.localEdvId) { - const {localId: localEdvId} = parseLocalId({id: edvId}); - selector = {localEdvId, ...selector}; - } - - const {docs: records} = await _client.find({ - selector, - ...query.options - }); - return {records}; -} - -/** - * Creates a query to pass to `find` based on the given `edvQuery`. The - * `edvQuery` can have these properties: `{index, equals, has, count, limit}`. - * The `index` property must be given and include an hmac ID associated with an - * encrypted index and only one of `equals` or `has` must be given. - * - * @param {object} options - The options to use. - * @param {string} options.edvId - The ID of the EDV. - * @param {object} options.edvQuery - The EDV query. - * - * @returns {Promise} Resolves to a `query` to pass to `find`. - */ -export function createQuery({edvId, edvQuery} = {}) { - assert.string(edvId, 'edvId'); - assert.edvQuery(edvQuery, 'edvQuery'); - - const use_index = ['edv-doc']; - const {index, equals, has} = edvQuery; - const encodedIndex = encodeURIComponent(index); - const {localId: localEdvId} = parseLocalId({id: edvId}); - const selector = {localEdvId}; - if(equals) { - // must provide this to enable use of the attributes query; the PouchDB - // query planner needs it to determine start / end keys in the index - selector.attributes = {$gt: null}; - selector.$or = equals.map(e => ({ - attributes: { - $all: Object.entries(e).map(([name, value]) => - `${encodedIndex}:${encodeURIComponent(name)}:` + - `${encodeURIComponent(value)}`) - } - })); - use_index.push('attributes'); - } else { - // `has` query - selector.attributeNames = { - $all: has.map(name => `${encodedIndex}:${encodeURIComponent(name)}`) - }; - use_index.push('attributes.name'); - } - return {selector, options: {use_index}}; -} - -function _createRecord({edvId, doc}) { - const {localId: localEdvId} = parseLocalId({id: edvId}); - const _id = _createId({localEdvId, docId: doc.id}); - const record = {_id, localEdvId, doc}; - - // build top-level attribute index fields - const { - attributes, attributeNames, uniqueAttributes - } = _buildAttributesIndex({doc}); - - if(attributes.length > 0) { - record.attributes = attributes; - } - - if(attributeNames.length > 0) { - record.attributeNames = attributeNames; - } - - const uniqueConstraints = []; - if(uniqueAttributes.length > 0) { - record.uniqueAttributes = uniqueAttributes; - uniqueConstraints.push({ - selector: {localEdvId, uniqueAttributes: {$in: uniqueAttributes}}, - options: {use_index: ['edv-doc', 'attributes.unique']} - }); - } - - return {localEdvId, record, uniqueConstraints}; -} - -function _buildAttributesIndex({doc}) { - const attributes = []; - const attributeNames = []; - const uniqueAttributes = []; - - // build top-level index fields - if(doc.indexed) { - for(const entry of doc.indexed) { - if(!entry.attributes) { - continue; - } - const encodedHmacId = encodeURIComponent(entry.hmac.id); - for(const attribute of entry.attributes) { - // concat hash of hmac ID, name, and value - const name = `${encodedHmacId}:${encodeURIComponent(attribute.name)}`; - const full = `${name}:${encodeURIComponent(attribute.value)}`; - attributes.push(full); - attributeNames.push(name); - if(attribute.unique) { - uniqueAttributes.push(full); - } - } - } - } - - return {attributes, attributeNames, uniqueAttributes}; -} - -function _createId({localEdvId, docId}) { - return `${localEdvId}:${docId}`; -} - -function _schedulePurge() { - if(_purgeOp) { - return; - } - _purgeOp = purge({name: COLLECTION_NAME}) - .catch(e => console.error(e)) - .finally(() => _purgeOp = null); -} diff --git a/lib/edvs.js b/lib/edvs.js deleted file mode 100644 index cccaab9..0000000 --- a/lib/edvs.js +++ /dev/null @@ -1,70 +0,0 @@ -/*! - * Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved. - */ -import {assert} from './assert.js'; -import {ConfigStorage} from './ConfigStorage.js'; - -let _storage; - -/** - * Initializes the encrypted data vault configurations database if it has not - * already been initialized. - * - * @returns {Promise} Settles once the operation completes. - */ -export async function initialize() { - if(_storage) { - // already initialized - return; - } - - _storage = new ConfigStorage({ - assertConfig: assert.edvConfig, - collectionName: 'edv-storage-config' - }); - - await _storage.initialize(); - - // queries by controller - await _storage.client.createIndex({ - index: { - fields: ['config.controller'] - } - }); -} - -/** - * Establishes a new EDV by inserting its configuration into storage. - * - * @param {object} options - The options to use. - * @param {object} options.config - The EDV configuration. - * - * @returns {Promise} Resolves to the database record. - */ -export async function insert({config} = {}) { - return _storage.insert({config}); -} - -/** - * Updates an EDV config if its sequence number is next. - * - * @param {object} options - The options to use. - * @param {object} options.config - The EDV configuration. - * - * @returns {Promise} Resolves to the database record. - */ -export async function update({config} = {}) { - return _storage.update({config}); -} - -/** - * Gets an EDV configuration. - * - * @param {object} options - The options to use. - * @param {object} options.id - The ID of the EDV. - * - * @returns {Promise} Resolves to the database record. - */ -export async function get({id} = {}) { - return _storage.get({id}); -} diff --git a/lib/helpers.js b/lib/helpers.js deleted file mode 100644 index d6c1410..0000000 --- a/lib/helpers.js +++ /dev/null @@ -1,116 +0,0 @@ -/*! - * Copyright (c) 2022-2025 Digital Bazaar, Inc. All rights reserved. - */ -import * as base58 from 'base58-universal'; -import {assert} from './assert.js'; - -let _getRandomBytes; - -export async function generateLocalId() { - // 128-bit random number, multibase + identity multihash encoded - // 0x00 = identity tag, 0x10 = length (16 bytes) - const data = await getRandomBytes(16); - return multihashEncode({codec: 0x00, data, multibase: 'z'}); -} - -export function parseLocalId({id} = {}) { - assert.string(id, 'id'); - /* Note: Current implementation just returns the `id` as it is not prefixed - with a base URI. This may change in the future. */ - return {base: '', localId: id}; -} - -export function getRandomBytes(n) { - assert.number(n, 'n'); - // lazily create random bytes function - if(!_getRandomBytes) { - _getRandomBytes = _createGetRandomBytes(); - } - return _getRandomBytes(n); -} - -export function multibaseDecode({expectedHeader, encoded} = {}) { - assert.uint8Array(expectedHeader, 'expectedHeader'); - assert.string(encoded, 'encoded'); - if(!encoded.startsWith('z')) { - throw new Error('"encoded" must be base58-multibase-encoded.'); - } - - const mcValue = base58.decode(encoded.slice(1)); - if(!expectedHeader.every((val, i) => mcValue[i] === val)) { - throw new Error('Multibase value does not have expected header.'); - } - return mcValue.slice(expectedHeader.length); -} - -export function multibaseEncode({header, data} = {}) { - assert.uint8Array(header, 'header'); - assert.uint8Array(data, 'data'); - const buf = new Uint8Array(header.length + data.length); - buf.set(header); - buf.set(data, header.length); - // multibase encoding for base58 starts with 'z' - return `z${base58.encode(buf)}`; -} - -export function multihashDecode({ - expectedCodec = 0x00, expectedSize, encoded -} = {}) { - assert.number(expectedSize, 'expectedSize'); - assert.string(encoded, 'encoded'); - if(expectedSize >= 128) { - // varint encoding required and not supported - throw new Error('"data.length" must be less than 128.'); - } - const expectedHeader = new Uint8Array([expectedCodec, expectedSize]); - const decoded = multibaseDecode({expectedHeader, encoded}); - if(decoded.length !== expectedSize) { - throw new Error( - `Actual decoded data size (${decoded.length}) is not as ` + - `expected (${expectedSize}).`); - } - return decoded; -} - -export function multihashEncode({codec = 0x00, data, multibase = 'z'} = {}) { - assert.uint8Array(data, 'data'); - if(multibase !== 'z') { - // only base58 encoding is supported - throw new Error('"multibase" character must be "z".'); - } - if(data.length >= 128) { - // varint encoding required and not supported - throw new Error('"data.length" must be less than 128.'); - } - - // multibase + multihash encoded - // byte 1 = multihash identifier (e.g., 0x00 = identity tag) - // byte 2 = length - const buf = new Uint8Array(2 + data.length); - buf[0] = codec; - buf[1] = data.length; - buf.set(data, 2); - // multibase encoding for base58 starts with 'z' - return `z${base58.encode(buf)}`; -} - -export function uuid() { - return globalThis.crypto.randomUUID(); -} - -function _createGetRandomBytes() { - // eslint-disable-next-line no-undef - const {crypto} = globalThis; - - if(crypto.getRandomValues) { - return async n => crypto.getRandomValues(new Uint8Array(n)); - } - - if(crypto.randomFill) { - return async n => new Promise( - (resolve, reject) => crypto.randomFill( - new Uint8Array(n), (err, buf) => err ? reject(err) : resolve(buf))); - } - - throw new Error('"crypto.getRandomValues" or "crypto.randomFill" required.'); -} diff --git a/lib/index.js b/lib/index.js index 63cc352..f7bac8c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,12 +1,7 @@ /*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. + * Copyright (c) 2025 Digital Bazaar, Inc. All rights reserved. */ -import * as chunks from './chunks.js'; -import * as docs from './docs.js'; -import * as edvs from './edvs.js'; -import * as secrets from './secrets.js'; -export {chunks, docs, edvs, secrets}; -export {initialize} from './initialize.js'; -export {generateLocalId} from './helpers.js'; -export {PouchEdvClient} from './PouchEdvClient.js'; +export {default as OpticalScanner} from '../components/OpticalScanner.vue'; +export {default as ScannerUI} from '../components/ScannerUI.vue'; +export {CameraScanner} from '@bedrock/web-optical-scanner'; diff --git a/lib/initialize.js b/lib/initialize.js deleted file mode 100644 index d75e7c7..0000000 --- a/lib/initialize.js +++ /dev/null @@ -1,21 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import * as chunks from './chunks.js'; -import * as docs from './docs.js'; -import * as edvs from './edvs.js'; -import * as secrets from './secrets.js'; - -/** - * Initializes all databases required to work with local EDVs. - * - * @returns {Promise} Settles once the operation completes. - */ -export async function initialize() { - return Promise.all([ - chunks.initialize(), - docs.initialize(), - edvs.initialize(), - secrets.initialize() - ]); -} diff --git a/lib/pbkdf2.js b/lib/pbkdf2.js deleted file mode 100644 index 057085b..0000000 --- a/lib/pbkdf2.js +++ /dev/null @@ -1,55 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import {assert} from './assert.js'; -import {getRandomBytes} from './helpers.js'; - -// eslint-disable-next-line no-undef -const {crypto} = globalThis; - -const ALGORITHM = {name: 'PBKDF2'}; -const EXTRACTABLE = false; -const KEY_USAGE = ['deriveBits', 'deriveKey']; - -/** - * Derive key bits from a password. - * - * @param {object} options - The options to use. - * @param {number} options.bitLength - The number of bytes to derive. - * @param {number} [options.iterations=100000] - The number of iterations to - * use. - * @param {string} options.password - The password to use. - * @param {Uint8Array} [options.salt] - The salt to use; one will be - * generated if not provided. - * @param {number} [options.saltSize] - The salt size, in bytes, to generate, - * if `salt` was not provided. - * - * @returns {Promise} The derived bits. - */ -export async function deriveBits({ - bitLength, iterations = 100000, password, salt, saltSize -} = {}) { - assert.nonNegativeSafeInteger(bitLength, 'bitLength'); - assert.nonNegativeSafeInteger(iterations, 'iterations'); - assert.string(password, 'password'); - if(salt !== undefined) { - assert.uint8Array(salt, 'salt'); - } else { - assert.nonNegativeSafeInteger(saltSize, 'saltSize'); - salt = await getRandomBytes(saltSize); - } - - const kdk = await crypto.subtle.importKey( - 'raw', new TextEncoder().encode(password), - ALGORITHM, EXTRACTABLE, KEY_USAGE); - - const algorithm = { - ...ALGORITHM, - salt, - iterations, - hash: 'SHA-256' - }; - const derivedBits = new Uint8Array(await crypto.subtle.deriveBits( - algorithm, kdk, bitLength)); - return {algorithm, derivedBits}; -} diff --git a/lib/pouchdb.js b/lib/pouchdb.js deleted file mode 100644 index 3942a62..0000000 --- a/lib/pouchdb.js +++ /dev/null @@ -1,297 +0,0 @@ -/*! - * Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved. - */ -import {assert} from './assert.js'; -import PouchDB from 'pouchdb'; -import pouchFind from 'pouchdb-find'; -import pouchIndexedDB from 'pouchdb-adapter-indexeddb'; - -export {PouchDB}; - -const PREFIX = 'br_edv_'; - -// support queries and indexing -PouchDB.plugin(pouchFind); -// support native IndexedDB indexes -PouchDB.plugin(pouchIndexedDB); - -/* Add some plugins to get pseudo-uniqueness properties. PouchDB does not have -some basic uniqueness primitives that other databases have. This includes no -ability to set unique constraints on custom indexes. These plugins provide -`insertOne` and `updateOne` APIs that approximate these behaviors but since the -system primitives are not present, they do not have atomic guarantees. */ -PouchDB.plugin({insertOne, updateOne}); - -// enable for debugging purposes only -// import debugPouch from 'pouchdb-debug'; -// debugPouch(PouchDB); -// PouchDB.debug.enable('pouchdb:find'); - -// create a pouch DB database w/auto-upgrade flag -export async function createDatabase({ - name, auto_compaction = true, adapter = 'indexeddb' -} = {}) { - const client = new PouchDB(PREFIX + name, { - auto_compaction, - adapter - }); - // force internal remote flag to false to avoid deprecation warnings - client._remote = false; - return client; -} - -// purge all deleted documents in a pouch DB database -export async function purge({name} = {}) { - // purge any deleted records to recover storage space - let db; - let deleted = 0; - try { - // apply prefixes to database name - name = '_pouch_' + PREFIX + name; - // open database and handle various events - db = await new Promise((resolve, reject) => { - const request = indexedDB.open(name); - request.onblocked = event => { - db = event.target.result; - reject(new Error('Cannot purge right now; database access blocked.')); - }; - request.onupgradeneeded = event => { - db = event.target.result; - reject(new Error('Nothing to purge; database not ready yet.')); - }; - request.onsuccess = event => resolve(event.target.result); - request.onerror = event => reject(event.target.error); - }); - if(![...db.objectStoreNames].includes('docs')) { - return; - } - const transaction = db.transaction(['docs'], 'readwrite'); - const transactionPromise = new Promise((resolve, reject) => { - transaction.oncomplete = resolve; - transaction.onabort = () => reject(transaction.error); - transaction.onerror = () => reject(transaction.error); - }); - const objectStore = transaction.objectStore('docs'); - const request = objectStore.openCursor(); - let cursor = await _promisifyIDBInterface(request); - while(cursor) { - const {value} = cursor; - if(value.deleted === 1) { - await _promisifyIDBInterface(cursor.delete()); - deleted++; - } - const next = _promisifyIDBInterface(request); - cursor.continue(); - cursor = await next; - } - await transactionPromise; - } catch(e) { - throw e; - } finally { - db?.close(); - } - - return {deleted}; -} - -/** - * Inserts a document into a PouchDB database, if `doc._id` is unique and if - * the selectors and options in the given `uniqueConstraints` array do not - * return any results. - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to insert. - * @param {object} [options.uniqueConstraints] - Any additional uniqueness - * constraints to enforce beyond the `_id` index (each entry includes - * `selector` and `options`). - * - * @returns {Promise} Resolves to the insert result. - */ -async function insertOne({doc, uniqueConstraints = []} = {}) { - assert.object(doc, 'doc'); - assert.array(uniqueConstraints, 'uniqueConstraints'); - - // build default unique constraints for `_id` - if(doc._id) { - uniqueConstraints = uniqueConstraints.slice(); - uniqueConstraints.push({selector: {_id: doc._id}}); - } - - // keep attempting insert whilst conflict errors arise -- this mitigates - // concurrency issues w/`_id` (but not other unique constraints) - while(true) { - try { - // check all uniqueness constraints - const [existing] = await Promise.all(uniqueConstraints - .map(({selector, options}) => _getExisting.call( - this, {doc, selector, options}))); - if(existing) { - // document already exists, throw error - const error = new Error( - 'Could not insert document; uniqueness constraint violation.'); - error.name = 'ConstraintError'; - error.doc = doc; - error.existing = existing; - error.uniqueConstraints = uniqueConstraints; - throw error; - } - - // this is not atomic w/the unique contraints check, so if concurrent - // processes update the same information the constraints may no longer - // be met - const result = await (doc._id ? this.put(doc) : this.post(doc)); - - // build the full record - const record = { - ...doc, - _id: result.id, - _rev: result.rev - }; - return {...result, record}; - } catch(e) { - // only capture PouchErrors here, not local `ConstraintError`, the latter - // should be thrown and stop this loop - if(e.status === 409) { - continue; - } - throw e; - } - } -} - -/** - * Updates a single document in the PouchDB database, if the selectors and - * options in the given `uniqueConstraints` array return a single result and - * the given `query` matches that same result. If `upsert` is set to `true` - * and both the `query` does not match and there are no documents matching - * the uniqueness constraints, the document will be inserted. If `doc._id` is - * set then unique constraints for it will be auto-generated. - * - * @param {object} options - The options to use. - * @param {object} options.doc - The document to update. - * @param {object} options.query - The selector and options to use to query. - * @param {boolean} [options.upsert=false] - `true` to insert the document - * if the query does not match, `false` if not. - * @param {object} [options.uniqueConstraints] - Any additional uniqueness - * constraints to enforce beyond the `_id` index (each entry includes - * `selector` and `options`). - * - * @returns {Promise} Resolves to the update result or - * `false` if `upsert=false` and no matching record was found. - */ -async function updateOne({ - doc, query, upsert = false, uniqueConstraints = [] -} = {}) { - // use a separate `uniqueConstraints` variable for updating (vs. inserting) - // to avoid passing a modified version to `insertOne` - let updateUniqueConstraints; - - // keep attempting update whilst conflict errors arise -- this mitigates - // concurrency issues w/`_id` (but not other unique constraints) - while(true) { - try { - // get matching record - const {selector, options} = query; - const existing = await _getExisting.call(this, {doc, selector, options}); - if(!existing) { - if(!upsert) { - // no match, and no insert requested, so nothing to update - return false; - } - // attempt an insert - return await this.insertOne({doc, uniqueConstraints}); - } - - // create unique constraints for update - if(!updateUniqueConstraints) { - if(!doc._id) { - updateUniqueConstraints = uniqueConstraints; - } else { - updateUniqueConstraints = uniqueConstraints.slice(); - updateUniqueConstraints.push({selector: {_id: doc._id}}); - } - } - - // check all uniqueness constraints - const existingRecords = await Promise.all(updateUniqueConstraints - .map(({selector, options}) => _getExisting.call( - this, {doc, selector, options}))); - // ensure all matching records are the same as the existing record across - // all uniqueness constraints, otherwise reject the update as it would - // violate them - for(const record of existingRecords) { - if(existing._id !== record?._id) { - const error = new Error( - 'Could not update document; uniqueness constraint violation.'); - error.name = 'ConstraintError'; - error.doc = doc; - error.existing = record; - error.uniqueConstraints = updateUniqueConstraints; - throw error; - } - } - - /* Note: Sadly, this is not atomic with the unique contraints checks, so - degenerate cases where concurrent inserts/updates are made may cause - uniqueness violations. This is an accepted limitation of this - implementation and the primitives currently offered by PouchDB. The - remedy to this problem when it occurs is to update N-1 of the documents - that duplicate each other in some way (to remove the duplication). */ - const result = await this.put({ - _id: existing._id, - _rev: existing._rev, - ...doc - }); - - // build the full record - const record = { - ...doc, - _id: result.id, - _rev: result.rev - }; - return {...result, record}; - } catch(e) { - // only capture PouchErrors here, not local `ConstraintError`, the latter - // should be thrown and stop this loop - if(e.status === 409) { - continue; - } - throw e; - } - } -} - -async function _getExisting({doc, selector, options}) { - if(!selector && !doc._id) { - throw new Error('Either "selector" or "doc._id" must be set.'); - } - - // set default selector - if(!selector) { - selector = {_id: doc._id}; - } - - /* Note: The calls that use this helper function expect a single document - to be returned. Those calls are either updating a single document or are - checking for uniqueness. If we are updating a single document, any document - will do (to be returned). - - In the case of a uniqueness check, we presume a single document will be - returned at most from a query based on unique constraints. This will only not - be the case if the lack of atomic primitives for enforcing uniqueness have - been violated via concurrent inserts/updates. In that case, the database has - entered a state with a unique constraints violation that must be remedied - by updating N-1 of the documents that duplicate one another in some way (to - remove the duplication). */ - const {docs: [existing]} = await this.find({ - selector, limit: 1, ...options - }); - return existing; -} - -function _promisifyIDBInterface(idbInterface) { - return new Promise((resolve, reject) => { - idbInterface.onsuccess = event => resolve(event.target.result); - idbInterface.onerror = event => reject(event.target.error); - }); -} diff --git a/lib/secrets.js b/lib/secrets.js deleted file mode 100644 index a925bfe..0000000 --- a/lib/secrets.js +++ /dev/null @@ -1,279 +0,0 @@ -/*! - * Copyright (c) 2021-2023 Digital Bazaar, Inc. All rights reserved. - */ -import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey'; -import {multihashDecode, multihashEncode, uuid} from './helpers.js'; -import {assert} from './assert.js'; -import {ConfigStorage} from './ConfigStorage.js'; -import {deriveBits} from './pbkdf2.js'; -import {Hmac} from './Hmac.js'; -import {Kek} from './Kek.js'; -import {P256Kak} from './P256Kak.js'; -import {X25519Kak} from './X25519Kak.js'; - -// P-256 unwrapped key is 32 secret key bytes + 33 public key bytes + 7 padding -const UNWRAPPED_KAK_SIZE = 72; -// wrapped KAK + 8 byte AES-KW overhead -const WRAPPED_KAK_SIZE = 80; - -const VERSIONS = new Map([ - ['1', { - // parameters for version 1 - iterations: 100000, - // AES-KW is used on a 32 byte key w/8 byte overhead - wrappedKeySize: 40, - // salt size in bytes - saltSize: 16 - }] -]); - -let _storage; - -/** - * Initializes the encrypted secrets database. - * - * @returns {Promise} Settles once the operation completes. - */ -export async function initialize() { - if(_storage) { - // already initialized - return; - } - - _storage = new ConfigStorage({ - assertConfig: assert.secretConfig, - collectionName: 'edv-storage-secret' - }); - - await _storage.initialize(); - - // queries by hmac ID - await _storage.client.createIndex({ - index: { - fields: ['config.hmacId'] - } - }); - - // queries by keyAgreementKeyId (key agreement key ID) - await _storage.client.createIndex({ - index: { - fields: ['config.keyAgreementKeyId'] - } - }); -} - -/** - * Decrypts a secret so its derived keys can be used. - * - * @param {object} options - The options to use. - * @param {object} options.config - The secret configuration. - * @param {object} options.password - The password to use to decrypt. - * - * @returns {Promise} Returns {hmac, keyAgreementKey, cipherVersion} on - * success and `null` if the password was invalid. - */ -export async function decrypt({config, password} = {}) { - assert.secretConfig(config); - assert.string(password, 'password'); - - // use password and version parameters to derive key encryption - const {secret} = config; - const {version, salt, wrappedKey} = secret; - const {wrappedKeySize, saltSize} = VERSIONS.get(version); - const {kek} = await _deriveKek({ - password, - salt: await multihashDecode({ - expectedSize: saltSize, encoded: salt - }), - version - }); - - // unwrap key derivation key - const unwrappedKey = await kek.unwrapKey({ - wrappedKey: await multihashDecode({ - expectedSize: wrappedKeySize, encoded: wrappedKey - }) - }); - if(!unwrappedKey) { - // invalid password - return null; - } - - // import KDK and clear secret value from memory - const kdk = await Hmac.import({secret: unwrappedKey}); - unwrappedKey.fill(0); - - // derive HMAC and key agreement keys - const { - hmac, keyAgreementKey, cipherVersion - } = await _deriveKeys({kdk, kek, secret}); - hmac.id = config.hmacId; - keyAgreementKey.id = config.keyAgreementKeyId; - return {hmac, keyAgreementKey, cipherVersion}; -} - -/** - * Generates a new encrypted secret configuration. - * - * @param {object} options - The options to use. - * @param {string} options.id - The ID for the secret. - * @param {string} options.password - The password to encrypt the secret. - * @param {string} options.version - The secret version. - * @param {string} [options.cipherVersion='recommended'] - Sets the cipher - * version to either "recommended" or "fips". - * - * @returns {Promise} Resolves to `{hmac, keyAgreementKey, config}`. - */ -export async function generate({ - id, version = '1', password, cipherVersion = 'recommended' -} = {}) { - if(version !== '1') { - throw new Error('"version" must be "1".'); - } - assert.string(id, 'id'); - assert.string(password, 'password'); - - // generate an HMAC key for deriving other keys - const kdk = await Hmac.generate(); - - // create encrypted secret from key derivation key - const {kek, secret} = await _createSecret( - {kdk, password, version, cipherVersion}); - - // derive blinded index key (HMAC) and key agreement key - const {hmac, keyAgreementKey} = await _deriveKeys({kdk, kek, secret}); - - const config = { - id, - hmacId: `urn:uuid:${uuid()}`, - keyAgreementKeyId: `urn:uuid:${uuid()}`, - secret, - sequence: 0 - }; - - hmac.id = config.hmacId; - keyAgreementKey.id = config.keyAgreementKeyId; - - return {hmac, keyAgreementKey, config}; -} - -/** - * Establishes a new secret by inserting its config into storage. - * - * @param {object} options - The options to use. - * @param {object} options.config - The secret configuration. - * - * @returns {Promise} Resolves to the database record. - */ -export async function insert({config} = {}) { - return _storage.insert({config}); -} - -/** - * Updates a secret config if its sequence number is next. - * - * @param {object} options - The options to use. - * @param {object} options.config - The secret configuration. - * - * @returns {Promise} Resolves to the database record. - */ -export async function update({config} = {}) { - return _storage.update({config}); -} - -/** - * Gets a secret configuration. - * - * @param {object} options - The options to use. - * @param {object} options.id - The ID of the EDV. - * - * @returns {Promise} Resolves to the database record. - */ -export async function get({id} = {}) { - return _storage.get({id}); -} - -async function _createSecret({kdk, password, version, cipherVersion} = {}) { - // use password and version parameters to derive key encryption - const {kek, algorithm} = await _deriveKek({password, version}); - - // wrap key derivation key for storage and reuse later - const wrappedKey = await kek.wrapKey({unwrappedKey: kdk.key}); - - const secret = { - version, - salt: multihashEncode({data: algorithm.salt}), - wrappedKey: multihashEncode({data: wrappedKey}) - }; - // if `fips` cipher version is used, generate random key agreement key - // instead of deriving it from a secret; this is a requirement for creating - // fips-compliant P-* curve keys - if(cipherVersion === 'fips') { - const kak = await EcdsaMultikey.generate({curve: 'P-256'}); - // export key for wrapping (secret key + public key) - const {secretKey, publicKey} = await kak.export( - {publicKey: true, secretKey: true, raw: true}); - const unwrappedKey = new Uint8Array(UNWRAPPED_KAK_SIZE); - unwrappedKey.set(secretKey); - unwrappedKey.set(publicKey, secretKey.length); - const wrappedKeyAgreementKey = await kek.wrapKey({unwrappedKey}); - secretKey.fill(0); - unwrappedKey.fill(0); - secret.wrappedKeyAgreementKey = multihashEncode( - {data: wrappedKeyAgreementKey}); - wrappedKeyAgreementKey.fill(0); - } - - return {kek, secret}; -} - -async function _deriveKek({password, salt, version} = {}) { - const {iterations, saltSize} = VERSIONS.get(version); - const { - derivedBits: kekSecret, - algorithm - } = await deriveBits({ - bitLength: 256, iterations, password, salt, saltSize - }); - const kek = await Kek.import({secret: kekSecret}); - kekSecret.fill(0); - return {kek, algorithm}; -} - -async function _deriveKeys({kdk, kek, secret} = {}) { - const encoder = new TextEncoder(); - - // generate secret and derive HMAC key - const hmacSecret = await kdk.sign({data: encoder.encode('hmac')}); - const hmac = await Hmac.import({secret: hmacSecret}); - hmacSecret.fill(0); - - // unwrap or generate secret and derive key agreement key - let keyAgreementKey; - let cipherVersion; - if(secret.wrappedKeyAgreementKey) { - cipherVersion = 'fips'; - // unwrap key derivation key (P-256 ) - const unwrappedKey = await kek.unwrapKey({ - wrappedKey: await multihashDecode({ - expectedSize: WRAPPED_KAK_SIZE, encoded: secret.wrappedKeyAgreementKey - }) - }); - if(!unwrappedKey) { - // invalid wrapped key agreement key - throw new Error('Invalid stored key agreement key.'); - } - const secretKey = unwrappedKey.subarray(0, 32); - const publicKey = unwrappedKey.subarray(32, 65); - keyAgreementKey = await P256Kak.import({secretKey, publicKey}); - unwrappedKey.fill(0); - } else { - cipherVersion = 'recommended'; - // generate secrets for an HMAC and a key agreement key - const kakSecret = await kdk.sign({data: encoder.encode('keyAgreementKey')}); - keyAgreementKey = await X25519Kak.import({secret: kakSecret}); - kakSecret.fill(0); - } - - return {hmac, keyAgreementKey, cipherVersion}; -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ef0ce6a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4614 @@ +{ + "name": "@bedrock/vue-optical-scanner", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@bedrock/vue-optical-scanner", + "version": "0.0.1", + "dependencies": { + "@bedrock/web-optical-scanner": "digitalbazaar/bedrock-web-optical-scanner#3ed24c3c5e2865edf0e0be948c6b03e02f187966" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "eslint": "^8.57.1", + "eslint-config-digitalbazaar": "^5.2.0", + "eslint-plugin-jsdoc": "^51.4.1", + "eslint-plugin-quasar": "^1.1.0", + "eslint-plugin-unicorn": "^56.0.1", + "eslint-plugin-vue": "^9.23.0", + "jsdoc": "^4.0.2", + "jsdoc-to-markdown": "^9.0.2", + "vite": "^7.1.5", + "vue": "^3.4.21" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@bedrock/quasar": "^10.0.0", + "@bedrock/web-fontawesome": "^2.1.0", + "vue": "^3.4.21" + } + }, + "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/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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bedrock/quasar": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@bedrock/quasar/-/quasar-10.0.1.tgz", + "integrity": "sha512-Os21koHpiKU/SP8nQKvM7ZSioboqEKwqRdgDqeBJpEzpHJk7DU3O42E2iJi93kY1HQHonSFQYd1+Alm5VEmD6A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "animate.css": "^4.1.1", + "quasar": "~2.15.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@bedrock/vue": "^5.0.1", + "vue": "^3.4.21" + } + }, + "node_modules/@bedrock/vue": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@bedrock/vue/-/vue-5.1.0.tgz", + "integrity": "sha512-rvCEvFvAirQPhTx00fenxe6lVJLj/BIX7Z4OLiQbVe2aSDEPHlGUgLRYgcTmb8XPLs7br8vUR8UE4LjWBnAIhw==", + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "@bedrock/web": "^3.0.0", + "vue": "^3.2.36", + "vue-router": "^4.0.15" + } + }, + "node_modules/@bedrock/web": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@bedrock/web/-/web-3.1.0.tgz", + "integrity": "sha512-1mbJeoLFfDeB9esPPAw49jkaKs5oKWtTonYvzakaQDmuEGFOnMdZaXP4KVwI10naB/mgTD65Pi+aNmCNbOTCJg==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@bedrock/web-fontawesome": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@bedrock/web-fontawesome/-/web-fontawesome-2.1.0.tgz", + "integrity": "sha512-YUFAtNa8PAKQjL8+xJaju4kDHY72jHTLqV0UkbXsYhdzwaKl72MUmoGhhftzan7qP0zFj8FOYMwYkfvoSpLK0w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-free": "^5.11.1" + } + }, + "node_modules/@bedrock/web-optical-scanner": { + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/digitalbazaar/bedrock-web-optical-scanner.git#3ed24c3c5e2865edf0e0be948c6b03e02f187966", + "integrity": "sha512-wcNpIcOl9ZjRcZdTJu73DBp35irc05/9n7CqlcKKdhM+ESVy2qoi5TkbYf17akKan3pZMFm8tg8RYFW55OP+vA==", + "dependencies": { + "barcode-detector": "^3.0.5", + "dynamsoft-javascript-barcode": "=9.6.1", + "dynamsoft-mrz-scanner": "^3.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.52.0.tgz", + "integrity": "sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.34.1", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", + "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", + "hasInstallScript": true, + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "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==", + "license": "MIT" + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/emscripten": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.2.tgz", + "integrity": "sha512-0EVXosRnffZuF+rsMM1ZVbfpwpvL2/hWycYQ/0GaH/VaoSJvcSmMl6fiPel9TZXHL3EhANxzqKOVFC6NFXyn8A==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", + "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/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.19", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT", + "peer": true + }, + "node_modules/@vue/reactivity": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "vue": "3.5.22" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==", + "license": "MIT", + "peer": true + }, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/barcode-detector": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-3.0.6.tgz", + "integrity": "sha512-v4xTr6B+FINl/p1RDl38qzIwF+Repfo+k/a/HlKTJKAJpNvACD6v7AH7LSPvfR4AdzXXuwai04huA4TWn02Znw==", + "license": "MIT", + "dependencies": { + "zxing-wasm": "2.2.2" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.14.tgz", + "integrity": "sha512-GM9c0cWWR8Ga7//Ves/9KRgTS8nLausCkP3CGiFLrnwA2CDUluXgaQqvrULoR2Ujrd/mz/lkX87F5BHFsNr5sQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cache-point": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-3.0.1.tgz", + "integrity": "sha512-itTIMLEKbh6Dw5DruXbxAgcyLnh/oPGVLBfTPqBOftASxHe8bAeXy7JkO4F0LvHqht7XqP5O/09h5UcHS2w0FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001749", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz", + "integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "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/command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" + }, + "engines": { + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/common-sequence": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-3.0.0.tgz", + "integrity": "sha512-g/CgSYk93y+a1IKm50tKl7kaT/OjjTYVQlEbUlt/49ZLV1mcKpUU7iyDiqTAeLdb4QDtQfq3ako8y8v//fzrWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "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/config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-back": "^2.0.1" + } + }, + "node_modules/config-master/node_modules/walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/current-module-paths": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/current-module-paths/-/current-module-paths-1.1.2.tgz", + "integrity": "sha512-H4s4arcLx/ugbu1XkkgSvcUZax0L6tXUqnppGniQb8l5VjUKGHoayXE5RiriiPhYDd+kjZnaok1Uig13PKtKYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dm-fabric": { + "version": "5.1.17", + "resolved": "https://registry.npmjs.org/dm-fabric/-/dm-fabric-5.1.17.tgz", + "integrity": "sha512-YouI4F+svfqTi3FM9fKG8ZqtdygzLbAzaA53QfekreLvJRqaHhCplk+2EF+cgMmuO5kY62m2Pm0pBqqV/sc5Vg==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/dm-howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/dm-howler/-/dm-howler-2.2.4.tgz", + "integrity": "sha512-h+iDEP/cyALeqWNtGdZZRwm3buSwaG26wzF6fCUGhvkF/yxYoWb8F/v7qN+urqdZw3wotZGO/01rDlzOxJUUGw==", + "license": "MIT" + }, + "node_modules/dmd": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-7.1.1.tgz", + "integrity": "sha512-Ap2HP6iuOek7eShReDLr9jluNJm9RMZESlt29H/Xs1qrVMkcS9X6m5h1mBC56WMxNiSo0wvjGICmZlYUSFjwZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^3.0.0", + "common-sequence": "^3.0.0", + "file-set": "^5.2.2", + "handlebars": "^4.7.8", + "marked": "^4.3.0", + "walk-back": "^5.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dynamsoft-camera-enhancer": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/dynamsoft-camera-enhancer/-/dynamsoft-camera-enhancer-3.3.10.tgz", + "integrity": "sha512-end8agwVRIpPF+Ixz4Kb5wY8Nn7Qq7XIa+mtysB/X1GubXm/2Vjh1YFiqA3XSPDhy65yYtqX0OA1G9vg7phfOg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "dm-fabric": "^5.1.17" + } + }, + "node_modules/dynamsoft-capture-vision-bundle": { + "version": "3.0.6001", + "resolved": "https://registry.npmjs.org/dynamsoft-capture-vision-bundle/-/dynamsoft-capture-vision-bundle-3.0.6001.tgz", + "integrity": "sha512-UZnhaiiLY1ZU/40LCVUfyDMN5nBqZXQOjzCpWjaqwt+WlkZBx6kO1uTtDlc5XMLM3bKXUFxNCsz7asv5dDjfTw==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/dynamsoft-capture-vision-data": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dynamsoft-capture-vision-data/-/dynamsoft-capture-vision-data-1.0.1.tgz", + "integrity": "sha512-qZDhSVD9r1XSrDl6alDZ+ocn+gmO3hCjMtzYnwEB9O/Y2TgpthHsDA7tuD1Gcfk+fycfSoI8eW3w0LcelzJFRw==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/dynamsoft-javascript-barcode": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/dynamsoft-javascript-barcode/-/dynamsoft-javascript-barcode-9.6.1.tgz", + "integrity": "sha512-wAaCrihkiX7YudisJNUMy21/jo5BJUqIc+mW54iNeJ9I4qDowfYA07uQEfw3XKCNUVRE50rbg4zGQIaH4Cb06g==", + "deprecated": "This package is deprecated. Please use 'dynamsoft-barcode-reader-bundle' instead: https://www.npmjs.com/package/dynamsoft-barcode-reader-bundle", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "dm-howler": "^2.2.4", + "dynamsoft-camera-enhancer": "^3.2.0" + }, + "peerDependencies": { + "node-fetch": "^2.6.5", + "node-localstorage": "^2.2.1" + }, + "peerDependenciesMeta": { + "node-fetch": { + "optional": true + }, + "node-localstorage": { + "optional": true + } + } + }, + "node_modules/dynamsoft-mrz-scanner": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dynamsoft-mrz-scanner/-/dynamsoft-mrz-scanner-3.0.3.tgz", + "integrity": "sha512-nBqLbh3rFUeurH2d50gmDzW3etg1Zf41w+suQaez97IEavlGpNYVfjmN68dATZgutupl7og+NgjK48gW65scEg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "dynamsoft-capture-vision-bundle": "3.0.6001", + "dynamsoft-capture-vision-data": "1.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.233", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.233.tgz", + "integrity": "sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "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", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-digitalbazaar": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-digitalbazaar/-/eslint-config-digitalbazaar-5.2.0.tgz", + "integrity": "sha512-b401QVIkHpN60kNEix7gAosVFX0TOQCOPw6RPp4rlGN4zIVL5E6oMPcuXeoE5AxAuJrVmt+nQQTKNuQ5gmyeOg==", + "deprecated": "Upgrade to eslint@9 and @digitalbazaar/eslint-config@6.", + "dev": true, + "license": "BSD-3-Clause", + "peerDependencies": { + "eslint": "^8.14.0", + "eslint-plugin-jsdoc": ">=42.0.0" + }, + "peerDependenciesMeta": { + "eslint-plugin-jsdoc": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "51.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-51.4.1.tgz", + "integrity": "sha512-y4CA9OkachG8v5nAtrwvcvjIbdcKgSyS6U//IfQr4FZFFyeBFwZFf/tfSsMr46mWDJgidZjBTqoCRlXywfFBMg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.52.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.4.1", + "escape-string-regexp": "^4.0.0", + "espree": "^10.4.0", + "esquery": "^1.6.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=20.11.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-quasar": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-quasar/-/eslint-plugin-quasar-1.1.0.tgz", + "integrity": "sha512-lVOfr6kTRPu91pAVYisiziMwU+bW33Z+AMnmnj3hM1xjzqeo0KBHovcX5J+YDyna8GWwiL8kAzrDvy0eG52aIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "requireindex": "~1.2.0", + "semver-compare": "^1.0.0" + }, + "engines": { + "node": ">= 8.9.0", + "npm": ">= 5.6.0", + "yarn": ">= 1.6.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/hawkeye64" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "56.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz", + "integrity": "sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "@eslint-community/eslint-utils": "^4.4.0", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.38.1", + "esquery": "^1.6.0", + "globals": "^15.9.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.6.3", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=18.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.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", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-set": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-5.3.0.tgz", + "integrity": "sha512-FKCxdjLX0J6zqTWdT0RXIxNF/n7MyXXnsSUp0syLEOCKdexvPZ02lNNv2a+gpK9E3hzUYF3+eFZe32ci7goNUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "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/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/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", + "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-api": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-9.3.5.tgz", + "integrity": "sha512-TQwh1jA8xtCkIbVwm/XA3vDRAa5JjydyKx1cC413Sh3WohDFxcMdwKSvn4LOsq2xWyAmOU/VnSChTQf6EF0R8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^3.0.1", + "current-module-paths": "^1.1.2", + "file-set": "^5.3.0", + "jsdoc": "^4.0.4", + "object-to-spawn-args": "^2.0.1", + "walk-back": "^5.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/jsdoc-parse": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.5.tgz", + "integrity": "sha512-8JaSNjPLr2IuEY4Das1KM6Z4oLHZYUnjRrr27hKSa78Cj0i5Lur3DzNnCkz+DfrKBDoljGMoWOiBVQbtUZJBPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.1", + "sort-array": "^5.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdoc-to-markdown": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-9.1.3.tgz", + "integrity": "sha512-i9wi+6WHX0WKziv0ar88T8h7OmxA0LWdQaV23nY6uQyKvdUPzVt0o6YAaOceFuKRF5Rvlju5w/KnZBfdpDAlnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "command-line-args": "^6.0.1", + "command-line-usage": "^7.0.3", + "config-master": "^3.1.0", + "dmd": "^7.1.1", + "jsdoc-api": "^9.3.5", + "jsdoc-parse": "^6.2.5", + "walk-back": "^5.1.1" + }, + "bin": { + "jsdoc2md": "bin/cli.js" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "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/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "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/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-to-spawn-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", + "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "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/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quasar": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.15.4.tgz", + "integrity": "sha512-6Rtj0KrsVA0IV9zMZ6R7U7hOpwLS/6E06hsISVHRPn21KEm3XAwHdvy9xWz5kwqWraHRlcisFSDu/KPL4VQK1w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.18.1", + "npm": ">= 6.13.4", + "yarn": ">= 1.21.1" + }, + "funding": { + "type": "github", + "url": "https://donate.quasar.dev" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "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": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "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/sort-array": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-5.1.1.tgz", + "integrity": "sha512-EltS7AIsNlAFIM9cayrgKrM6XP94ATWwXP4LCL4IQbvbYhELSt2hZTrixg+AaQwnWFs/JGJgqU3rxMcNNWxGAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.1" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "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/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/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/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/vite": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/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/vue": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/walk-back": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.1.tgz", + "integrity": "sha512-e/FRLDVdZQWFrAzU6Hdvpm7D7m2ina833gIKLptQykRK49mmCYHLHq7UqjPDbxbKLZkTkW1rFqbengdE3sLfdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wordwrapjs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", + "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zxing-wasm": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-2.2.2.tgz", + "integrity": "sha512-Q9/B9whEwAUABvr7ScHl36wVZTBWVHAaumx45uGQLl2GGRp5ZRtDtwbz5scOwl/xzL07fximIqoQqqmzf9eJJA==", + "license": "MIT", + "dependencies": { + "@types/emscripten": "^1.41.2", + "type-fest": "^5.0.1" + }, + "peerDependencies": { + "@types/emscripten": ">=1.39.6" + } + }, + "node_modules/zxing-wasm/node_modules/type-fest": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.0.1.tgz", + "integrity": "sha512-9MpwAI52m8H6ssA542UxSLnSiSD2dsC3/L85g6hVubLSXd82wdI80eZwTWhdOfN67NlA+D+oipAs1MlcTcu3KA==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index e8c583f..d7410cd 100644 --- a/package.json +++ b/package.json @@ -1,43 +1,60 @@ { - "name": "@bedrock/web-pouch-edv", - "version": "8.2.1-0", + "name": "@bedrock/vue-optical-scanner", + "version": "0.0.1", "type": "module", - "description": "PouchDB EDV Storage", - "exports": "./lib/index.js", + "description": "Vue-specific UI component library consumes the bedrock-web-optical-scanner for all scanning logic, provides reusable Vue components for optical scanning UIs, replaces the combined scanning+UI approach in existing libraries", + "exports": { + ".": "./lib/index.js", + "./components/": "./components/" + }, "files": [ - "lib/**/*.js" + "components/*", + "lib/*" ], "scripts": { - "lint": "eslint ." + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint --ext .cjs,.js,.vue .", + "lint:fix": "eslint --ext .cjs,.js,.vue . --fix" }, "dependencies": { - "@digitalbazaar/ecdsa-multikey": "^1.8.0", - "@digitalbazaar/edv-client": "^16.1.0", - "base58-universal": "^2.0.0", - "pouchdb": "^9.0.0", - "pouchdb-adapter-indexeddb": "^9.0.0", - "pouchdb-find": "^9.0.0" + "@bedrock/web-optical-scanner": "digitalbazaar/bedrock-web-optical-scanner#3ed24c3c5e2865edf0e0be948c6b03e02f187966" + }, + "peerDependencies": { + "@bedrock/quasar": "^10.0.0", + "@bedrock/web-fontawesome": "^2.1.0", + "vue": "^3.4.21" }, "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", "eslint": "^8.57.1", "eslint-config-digitalbazaar": "^5.2.0", "eslint-plugin-jsdoc": "^51.4.1", + "eslint-plugin-quasar": "^1.1.0", "eslint-plugin-unicorn": "^56.0.1", - "pouchdb-debug": "^7.2.1" + "eslint-plugin-vue": "^9.23.0", + "jsdoc": "^4.0.2", + "jsdoc-to-markdown": "^9.0.2", + "vite": "^7.1.5", + "vue": "^3.4.21" }, "engines": { "node": ">=20" }, "repository": { "type": "git", - "url": "https://github.com/digitalbazaar/bedrock-web-pouch-edv" + "url": "https://github.com/digitalbazaar/bedrock-vue-optical-scanner" }, "keywords": [ "bedrock", - "pouchdb", - "edv", - "encrypted data vault", - "web" + "vue", + "optical", + "scanner", + "barcode", + "qr", + "pdf417", + "mrz" ], "author": { "name": "Digital Bazaar, Inc.", @@ -45,7 +62,7 @@ "url": "https://digitalbazaar.com" }, "bugs": { - "url": "https://github.com/digitalbazaar/bedrock-pouch-edv/issues" + "url": "https://github.com/digitalbazaar/bedrock-vue-optical-scanner/issues" }, - "homepage": "https://github.com/digitalbazaar/bedrock-pouch-edv" + "homepage": "https://github.com/digitalbazaar/bedrock-vue-optical-scanner" } diff --git a/test/package.json b/test/package.json index 1735756..d319142 100644 --- a/test/package.json +++ b/test/package.json @@ -1,5 +1,5 @@ { - "name": "bedrock-web-pouch-edv-test", + "name": "bedrock-vue-optical-scanner-test", "version": "0.0.1-0", "type": "module", "private": true, @@ -14,7 +14,6 @@ "@bedrock/core": "^6.0.1", "@bedrock/karma": "^6.1.0", "@bedrock/test": "^8.0.5", - "@bedrock/web-pouch-edv": "file:..", "events": "^3.3.0" } } diff --git a/test/test.config.js b/test/test.config.js index 70d8230..b3064eb 100644 --- a/test/test.config.js +++ b/test/test.config.js @@ -3,10 +3,11 @@ */ import {config} from '@bedrock/core'; import {createRequire} from 'node:module'; -import path from 'node:path'; +// import path from 'node:path'; const require = createRequire(import.meta.url); -config.karma.suites['bedrock-web-pouch-edv'] = path.join('web', '**', '*.js'); +// config.karma.suites['bedrock-web-pouch-edv'] = +// path.join('web', '**', '*.js'); config.karma.config.webpack.resolve.fallback.events = require.resolve('events/'); diff --git a/test/web/10-edvs.js b/test/web/10-edvs.js deleted file mode 100644 index f11ec36..0000000 --- a/test/web/10-edvs.js +++ /dev/null @@ -1,221 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import {edvs, generateLocalId, initialize} from '@bedrock/web-pouch-edv'; -import {mock} from './mock.js'; - -describe('edvs API', function() { - before(async () => { - await initialize(); - }); - - describe('insert', () => { - it('should fail "config.id" assertion', async () => { - const config = { - ...mock.config, - id: false - }; - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.id" must be a string.'); - }); - it('should fail "config.controller" assertion', async () => { - const config = { - ...mock.config, - controller: false - }; - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.controller" must be a string.'); - }); - it('should fail "config.sequence" assertion', async () => { - const config = { - ...mock.config, - sequence: false - }; - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"config.sequence" must be a non-negative safe integer.'); - }); - it('should fail "config.hmac" assertion', async () => { - const config = { - ...mock.config, - hmac: false - }; - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.hmac" must be an object.'); - }); - it('should fail "config.keyAgreementKey" assertion', async () => { - const config = { - ...mock.config, - keyAgreementKey: false - }; - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.keyAgreementKey" must be an object.'); - }); - it('should pass', async () => { - const config = { - ...mock.config, - id: await generateLocalId() - }; - const result = await edvs.insert({config}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'controller', 'sequence', 'hmac', 'keyAgreementKey']); - }); - it('should fail due to duplicate config', async () => { - const config = { - ...mock.config, - id: await generateLocalId() - }; - await edvs.insert({config}); - - // insert same config again - let error; - try { - await edvs.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not insert document; uniqueness constraint violation.'); - }); - }); - - describe('update', () => { - it('should fail due to bad sequence', async () => { - // first insert config - const config = { - ...mock.config, - id: await generateLocalId() - }; - const record = await edvs.insert({config}); - - // then update config w/o updating sequence - let error; - try { - await edvs.update({config: record.config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update configuration. Sequence does not match or ' + - 'configuration does not exist.'); - }); - it('should fail due to not found', async () => { - // update non-existent config - const config = { - ...mock.config, - id: await generateLocalId() - }; - let error; - try { - await edvs.update({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update configuration. Sequence does not match or ' + - 'configuration does not exist.'); - }); - it('should pass', async () => { - // first insert config - const config = { - ...mock.config, - id: await generateLocalId() - }; - const record = await edvs.insert({config}); - - // then update config - const newConfig = {...record.config}; - newConfig.sequence++; - const result = await edvs.update({config: newConfig}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'controller', 'sequence', 'hmac', 'keyAgreementKey']); - }); - }); - - describe('get', () => { - it('should fail "id" assertion', async () => { - let error; - try { - await edvs.get({id: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"id" must be a string.'); - }); - it('should fail due to not found error', async () => { - // get non-existent config - let error; - try { - await edvs.get({id: 'not found'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Configuration not found.'); - }); - it('should pass', async () => { - // first insert config - const config = { - ...mock.config, - id: await generateLocalId() - }; - const inserted = await edvs.insert({config}); - - // then get config - const record = await edvs.get({id: config.id}); - record.should.eql(inserted); - }); - }); -}); diff --git a/test/web/20-docs.js b/test/web/20-docs.js deleted file mode 100644 index 83e6b76..0000000 --- a/test/web/20-docs.js +++ /dev/null @@ -1,498 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import {docs, edvs, generateLocalId, initialize} from '@bedrock/web-pouch-edv'; -import {mock} from './mock.js'; - -describe('docs API', function() { - let edvId; - before(async () => { - await initialize(); - }); - beforeEach(async () => { - const config = { - ...mock.config, - id: await generateLocalId() - }; - const record = await edvs.insert({config}); - edvId = record.config.id; - }); - - describe('insert', () => { - it('should fail "edvId" assertion', async () => { - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - let error; - try { - await docs.insert({edvId: false, doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"edvId" must be a string.'); - }); - it('should fail "doc.id" assertion', async () => { - const doc = { - ...mock.doc, - id: false - }; - let error; - try { - await docs.insert({edvId, doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"doc.id" must be a string.'); - }); - it('should fail "doc.sequence" assertion', async () => { - const doc = { - ...mock.doc, - sequence: false - }; - let error; - try { - await docs.insert({edvId, doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"doc.sequence" must be a non-negative safe integer.'); - }); - it('should pass', async () => { - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const result = await docs.insert({edvId, doc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'doc', 'localEdvId']); - should.exist(result.doc); - result.doc.should.have.keys(['id', 'sequence', 'jwe']); - }); - it('should fail due to duplicate doc ID', async () => { - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - await docs.insert({edvId, doc}); - - // insert same doc again - let error; - try { - await docs.insert({edvId, doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not insert document; uniqueness constraint violation.'); - }); - it('should fail due to duplicate unique attribute', async () => { - const doc = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - await docs.insert({edvId, doc}); - - // insert different doc with same attribute - const doc2 = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - let error; - try { - await docs.insert({edvId, doc: doc2}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not insert document; uniqueness constraint violation.'); - }); - it('should pass with non-conflicting attribute', async () => { - const doc = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - const inserted1 = await docs.insert({edvId, doc}); - - // insert different doc with different value for unique attributes - // but same value for non-unique attribute (should be legal) - const doc2 = { - ...mock.docWithUniqueAttributes2, - id: await generateLocalId() - }; - const inserted2 = await docs.insert({edvId, doc: doc2}); - - const record1 = await docs.get({edvId, id: doc.id}); - record1.should.eql(inserted1); - const record2 = await docs.get({edvId, id: doc2.id}); - record2.should.eql(inserted2); - }); - }); - - describe('upsert', () => { - it('should fail due to bad sequence', async () => { - // first insert doc - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const record = await docs.insert({edvId, doc}); - - // then update doc w/o updating sequence - let error; - try { - await docs.upsert({edvId, doc: record.doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update document. Sequence does not match.'); - }); - it('should pass with a new doc', async () => { - // upsert doc - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const result = await docs.upsert({edvId, doc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'doc', 'localEdvId']); - should.exist(result.doc); - result.doc.should.have.keys(['id', 'sequence', 'jwe']); - }); - it('should pass with an existing doc', async () => { - // first insert doc - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const record = await docs.insert({edvId, doc}); - - // then update doc - const newDoc = {...record.doc}; - newDoc.sequence++; - const result = await docs.upsert({edvId, doc: newDoc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'doc', 'localEdvId']); - should.exist(result.doc); - result.doc.should.have.keys(['id', 'sequence', 'jwe']); - }); - it('should fail due to duplicate unique attribute', async () => { - const doc = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - await docs.upsert({edvId, doc}); - - // upsert different doc with same attribute - const doc2 = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - let error; - try { - await docs.upsert({edvId, doc: doc2}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not insert document; uniqueness constraint violation.'); - }); - it('should pass with non-conflicting attribute', async () => { - const doc = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - const inserted1 = await docs.upsert({edvId, doc}); - - // upsert different doc with different value for unique attributes - // but same value for non-unique attribute (should be legal) - const doc2 = { - ...mock.docWithUniqueAttributes2, - id: await generateLocalId() - }; - const inserted2 = await docs.upsert({edvId, doc: doc2}); - - const record1 = await docs.get({edvId, id: doc.id}); - record1.should.eql(inserted1); - const record2 = await docs.get({edvId, id: doc2.id}); - record2.should.eql(inserted2); - }); - it('should fail due to change to duplicate unique attribute', async () => { - const doc = { - ...mock.docWithUniqueAttributes, - id: await generateLocalId() - }; - await docs.upsert({edvId, doc}); - - // upsert different doc with different value for unique attributes - // but same value for non-unique attribute (should be legal) - let doc2 = { - ...mock.docWithUniqueAttributes2, - id: await generateLocalId() - }; - await docs.upsert({edvId, doc: doc2}); - - // now should fail when trying to change `doc2` to have the same - // unique attribute as `doc1` - doc2 = { - ...mock.docWithUniqueAttributes, - id: doc2.id, - sequence: 1 - }; - let error; - try { - await docs.upsert({edvId, doc: doc2}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not update document; uniqueness constraint violation.'); - }); - }); - - describe('get', () => { - it('should fail "edvId" assertion', async () => { - let error; - try { - await docs.get({edvId: false, id: 'z19pjdSMQMkBqqJ5zsbbgbbbb'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"edvId" must be a string.'); - }); - it('should fail "id" assertion', async () => { - let error; - try { - await docs.get({edvId, id: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"id" must be a string.'); - }); - it('should fail "id" assertion w/invalid formatted id', async () => { - let error; - try { - await docs.get({edvId, id: 'not valid format'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Identifier "not valid format" must be base58-encoded multibase, ' + - 'multihash array of 16 random bytes.'); - }); - it('should fail due to not found error', async () => { - // get non-existent doc - let error; - try { - await docs.get({edvId, id: 'z19pjdSMQMkBqqJ5zsbbgbbbb'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document not found.'); - }); - it('should fail due to not found error (wrong EDV)', async () => { - // first insert doc - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - await docs.insert({edvId, doc}); - - // try to get doc with wrong EDV ID - let error; - try { - const wrongEdvId = 'z19pjdSMQMkBqqJ5zsbbgbbbb'; - await docs.get({edvId: wrongEdvId, id: doc.id}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document not found.'); - }); - it('should pass', async () => { - // first insert doc - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const inserted = await docs.insert({edvId, doc}); - - // then get doc - const record = await docs.get({edvId, id: doc.id}); - record.should.eql(inserted); - }); - }); - - describe('find', () => { - it('should get a document by attribute', async () => { - // first insert doc - const doc = { - ...mock.docWithAttributes, - id: await generateLocalId() - }; - const inserted = await docs.insert({edvId, doc}); - - // then find doc - const entry = doc.indexed[0]; - const [attribute] = entry.attributes; - const query = docs.createQuery({ - edvId, - edvQuery: { - index: entry.hmac.id, - has: [attribute.name] - } - }); - const result = await docs.find({edvId, query}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['records']); - should.exist(result.records); - result.records.should.be.an('array'); - result.records.length.should.equal(1); - should.exist(result.records[0]); - result.records[0].should.eql(inserted); - }); - it('should get a document by attribute and value', async () => { - // first insert doc - const doc = { - ...mock.docWithAttributes, - id: await generateLocalId() - }; - const inserted = await docs.insert({edvId, doc}); - - // then find doc - const entry = doc.indexed[0]; - const [attribute] = entry.attributes; - const query = docs.createQuery({ - edvId, - edvQuery: { - index: entry.hmac.id, - equals: [{[attribute.name]: attribute.value}] - } - }); - const result = await docs.find({edvId, query}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['records']); - should.exist(result.records); - result.records.should.be.an('array'); - result.records.length.should.equal(1); - should.exist(result.records[0]); - result.records[0].should.eql(inserted); - }); - it('should get documents by attribute and value', async () => { - // insert 3 docs, each with different attribute values, find only 2 - const doc1 = { - ...mock.docWithAttributes, - id: await generateLocalId() - }; - await docs.insert({edvId, doc: doc1}); - - // must deep copy to change attributes - const doc2 = JSON.parse(JSON.stringify(mock.docWithAttributes)); - doc2.id = await generateLocalId(); - doc2.indexed[0].attributes[0].value = 'match'; - await docs.insert({edvId, doc: doc2}); - - // must deep copy to change attributes - const doc3 = JSON.parse(JSON.stringify(mock.docWithAttributes)); - doc3.id = await generateLocalId(); - doc3.indexed[0].attributes[0].value = 'different'; - await docs.insert({edvId, doc: doc3}); - - // then find 2 docs out of 3 - const entry = doc1.indexed[0]; - const [attribute] = entry.attributes; - const query = docs.createQuery({ - edvId, - edvQuery: { - index: entry.hmac.id, - equals: [ - {[attribute.name]: attribute.value}, - {[attribute.name]: doc2.indexed[0].attributes[0].value} - ] - } - }); - const result = await docs.find({edvId, query}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['records']); - should.exist(result.records); - result.records.should.be.an('array'); - result.records.length.should.equal(2); - result.records.map(({doc: {id}}) => id).should.include.members([ - doc1.id, doc2.id - ]); - }); - it('should get no documents', async () => { - // insert 3 docs, each with different attribute values, find none - const doc1 = { - ...mock.docWithAttributes, - id: await generateLocalId() - }; - await docs.insert({edvId, doc: doc1}); - - // must deep copy to change attributes - const doc2 = JSON.parse(JSON.stringify(mock.docWithAttributes)); - doc2.id = await generateLocalId(); - doc2.indexed[0].attributes[0].value = 'match'; - await docs.insert({edvId, doc: doc2}); - - // must deep copy to change attributes - const doc3 = JSON.parse(JSON.stringify(mock.docWithAttributes)); - doc3.id = await generateLocalId(); - doc3.indexed[0].attributes[0].value = 'different'; - await docs.insert({edvId, doc: doc3}); - - // then find no matching docs - const entry = doc1.indexed[0]; - const [attribute] = entry.attributes; - const query = docs.createQuery({ - edvId, - edvQuery: { - index: entry.hmac.id, - equals: [ - {[attribute.name]: 'nomatches'} - ] - } - }); - const result = await docs.find({edvId, query}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['records']); - should.exist(result.records); - result.records.should.be.an('array'); - result.records.length.should.equal(0); - }); - }); -}); diff --git a/test/web/30-chunks.js b/test/web/30-chunks.js deleted file mode 100644 index acbe54f..0000000 --- a/test/web/30-chunks.js +++ /dev/null @@ -1,230 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -import { - chunks, docs, edvs, generateLocalId, initialize -} from '@bedrock/web-pouch-edv'; -import {mock} from './mock.js'; - -describe('chunks API', function() { - let edvId; - let docId; - before(async () => { - await initialize(); - }); - beforeEach(async () => { - const config = { - ...mock.config, - id: await generateLocalId() - }; - const configRecord = await edvs.insert({config}); - edvId = configRecord.config.id; - - const doc = { - ...mock.doc, - id: await generateLocalId() - }; - const docRecord = await docs.insert({edvId, doc}); - docId = docRecord.doc.id; - }); - - describe('upsert', () => { - it('should fail "edvId" assertion', async () => { - const chunk = { - ...mock.chunk - }; - let error; - try { - await chunks.upsert({edvId: false, docId, chunk}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"edvId" must be a string.'); - }); - it('should fail "docId" assertion', async () => { - const chunk = { - ...mock.chunk - }; - let error; - try { - await chunks.upsert({edvId, docId: false, chunk}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"docId" must be a string.'); - }); - it('should fail "chunk.sequence" assertion', async () => { - const chunk = { - ...mock.chunk, - sequence: false - }; - let error; - try { - await chunks.upsert({edvId, docId, chunk}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"chunk.sequence" must be a non-negative safe integer.'); - }); - it('should fail due to bad sequence', async () => { - // upsert chunk w/bad sequence - const chunk = { - ...mock.chunk, - sequence: 1000 - }; - let error; - try { - await chunks.upsert({edvId, docId, chunk}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update document chunk. Sequence does not match the ' + - 'associated document.'); - }); - it('should pass with a new chunk', async () => { - // upsert chunk - const chunk = { - ...mock.chunk - }; - const result = await chunks.upsert({edvId, docId, chunk}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'localEdvId', 'docId', 'chunk']); - should.exist(result.chunk); - result.chunk.should.have.keys(['sequence', 'index', 'offset', 'jwe']); - }); - it('should pass with an existing chunk', async () => { - // first insert chunk - const chunk = { - ...mock.chunk - }; - const record = await chunks.upsert({edvId, docId, chunk}); - - // then update document - const doc = { - ...mock.doc, - id: docId, - sequence: 1 - }; - await docs.upsert({edvId, doc}); - - // then update chunk - const newChunk = {...record.chunk}; - newChunk.sequence++; - const result = await chunks.upsert({edvId, docId, chunk: newChunk}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'localEdvId', 'docId', 'chunk']); - should.exist(result.chunk); - result.chunk.should.have.keys(['sequence', 'index', 'offset', 'jwe']); - }); - }); - - describe('get', () => { - it('should fail "edvId" assertion', async () => { - let error; - try { - await chunks.get({edvId: false, docId, index: 0}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"edvId" must be a string.'); - }); - it('should fail "docId" assertion', async () => { - let error; - try { - await chunks.get({edvId, docId: false, index: 0}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"docId" must be a string.'); - }); - it('should fail "index" assertion', async () => { - let error; - try { - await chunks.get({edvId, docId, index: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"index" must be a non-negative safe integer.'); - }); - it('should fail due to not found error', async () => { - // get non-existent chunk - let error; - try { - await chunks.get({edvId, docId, index: 0}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document chunk not found.'); - }); - it('should fail due to not found error (wrong EDV)', async () => { - // first insert chunk - const chunk = { - ...mock.chunk - }; - await chunks.upsert({edvId, docId, chunk}); - - // try to get chunk with wrong EDV ID - let error; - try { - const wrongEdvId = 'z19pjdSMQMkBqqJ5zsbbgbbbb'; - await chunks.get({edvId: wrongEdvId, docId, index: chunk.index}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document chunk not found.'); - }); - it('should fail due to not found error (wrong doc)', async () => { - // first insert chunk - const chunk = { - ...mock.chunk - }; - await chunks.upsert({edvId, docId, chunk}); - - // try to get chunk with wrong doc ID - let error; - try { - const wrongDocId = 'z19pjdSMQMkBqqJ5zsbbgbbbb'; - await chunks.get({edvId, docId: wrongDocId, index: chunk.index}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document chunk not found.'); - }); - it('should pass', async () => { - // first insert chunk - const chunk = { - ...mock.chunk - }; - const inserted = await chunks.upsert({edvId, docId, chunk}); - - // then get chunk - const record = await chunks.get({edvId, docId, index: chunk.index}); - record.should.eql(inserted); - }); - }); -}); diff --git a/test/web/40-secrets.js b/test/web/40-secrets.js deleted file mode 100644 index 7b0a0f7..0000000 --- a/test/web/40-secrets.js +++ /dev/null @@ -1,364 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -import {generateLocalId, initialize, secrets} from '@bedrock/web-pouch-edv'; - -describe('secrets API', function() { - before(async () => { - await initialize(); - }); - - describe('generate', () => { - it('should fail with wrong version', async () => { - let error; - try { - await secrets.generate({id: '', version: '0', password: 'pw'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('Error'); - error.message.should.equal('"version" must be "1".'); - }); - it('should fail with bad "id"', async () => { - let error; - try { - await secrets.generate({id: false, version: '1', password: 'pw'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"id" must be a string.'); - }); - it('should fail with bad "password"', async () => { - let error; - try { - await secrets.generate({id: '', version: '1', password: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"password" must be a string.'); - }); - it('should pass', async () => { - const result = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['hmac', 'keyAgreementKey', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'hmacId', 'keyAgreementKeyId', 'secret', 'sequence']); - should.exist(result.config.secret); - result.config.secret.should.have.keys(['version', 'salt', 'wrappedKey']); - }); - it('should pass using "fips" cipher version', async () => { - const result = await secrets.generate({ - id: await generateLocalId(), password: 'pw', cipherVersion: 'fips' - }); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['hmac', 'keyAgreementKey', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'hmacId', 'keyAgreementKeyId', 'secret', 'sequence']); - should.exist(result.config.secret); - result.config.secret.should.have.keys( - ['version', 'salt', 'wrappedKey', 'wrappedKeyAgreementKey']); - }); - }); - - describe('decrypt', () => { - it('should fail "config.id" assertion', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - config.id = false; - let error; - try { - await secrets.decrypt({config, password}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.id" must be a string.'); - }); - it('should fail "config.sequence" assertion', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - config.sequence = false; - let error; - try { - await secrets.decrypt({config, password}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"config.sequence" must be a non-negative safe integer.'); - }); - it('should fail "config.hmacId" assertion', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - config.hmacId = false; - let error; - try { - await secrets.decrypt({config, password}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.hmacId" must be a string.'); - }); - it('should fail "config.keyAgreementKeyId" assertion', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - config.keyAgreementKeyId = false; - let error; - try { - await secrets.decrypt({config, password}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"config.keyAgreementKeyId" must be a string.'); - }); - it('should fail due to invalid password', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - const result = await secrets.decrypt({config, password: 'invalid'}); - should.equal(result, null); - }); - it('should pass', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password - }); - const result = await secrets.decrypt({config, password}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['hmac', 'keyAgreementKey', 'cipherVersion']); - result.cipherVersion.should.equal('recommended'); - }); - it('should pass using "fips" cipher version', async () => { - const password = 'pw'; - const {config} = await secrets.generate({ - id: await generateLocalId(), password, cipherVersion: 'fips' - }); - const result = await secrets.decrypt({config, password}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['hmac', 'keyAgreementKey', 'cipherVersion']); - result.cipherVersion.should.equal('fips'); - }); - }); - - describe('insert', () => { - it('should fail "config.id" assertion', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - config.id = false; - let error; - try { - await secrets.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.id" must be a string.'); - }); - it('should fail "config.sequence" assertion', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - config.sequence = false; - let error; - try { - await secrets.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"config.sequence" must be a non-negative safe integer.'); - }); - it('should fail "config.hmacId" assertion', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - config.hmacId = false; - let error; - try { - await secrets.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"config.hmacId" must be a string.'); - }); - it('should fail "config.keyAgreementKeyId" assertion', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - config.keyAgreementKeyId = false; - let error; - try { - await secrets.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal( - '"config.keyAgreementKeyId" must be a string.'); - }); - it('should pass', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - const result = await secrets.insert({config}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'hmacId', 'keyAgreementKeyId', 'secret', 'sequence']); - should.exist(result.config.secret); - result.config.secret.should.have.keys(['version', 'salt', 'wrappedKey']); - }); - it('should fail due to duplicate config', async () => { - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - await secrets.insert({config}); - - // insert same config again - let error; - try { - await secrets.insert({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('ConstraintError'); - error.message.should.equal( - 'Could not insert document; uniqueness constraint violation.'); - }); - }); - - describe('update', () => { - it('should fail due to bad sequence', async () => { - // first insert config - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - const record = await secrets.insert({config}); - - // then update config w/o updating sequence - let error; - try { - await secrets.update({config: record.config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update configuration. Sequence does not match or ' + - 'configuration does not exist.'); - }); - it('should fail due to not found', async () => { - // update non-existent config - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - let error; - try { - await secrets.update({config}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - error.message.should.equal( - 'Could not update configuration. Sequence does not match or ' + - 'configuration does not exist.'); - }); - it('should pass', async () => { - // first insert config - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - const record = await secrets.insert({config}); - - // then update config - const newConfig = {...record.config}; - newConfig.sequence++; - const result = await secrets.update({config: newConfig}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['_id', '_rev', 'config']); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'hmacId', 'keyAgreementKeyId', 'secret', 'sequence']); - }); - }); - - describe('get', () => { - it('should fail "id" assertion', async () => { - let error; - try { - await secrets.get({id: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"id" must be a string.'); - }); - it('should fail due to not found error', async () => { - // get non-existent config - let error; - try { - await secrets.get({id: 'not found'}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Configuration not found.'); - }); - it('should pass', async () => { - // first insert config - const {config} = await secrets.generate({ - id: await generateLocalId(), password: 'pw' - }); - const inserted = await secrets.insert({config}); - - // then get config - const record = await secrets.get({id: config.id}); - record.should.eql(inserted); - }); - }); -}); diff --git a/test/web/50-PouchEdvClient.js b/test/web/50-PouchEdvClient.js deleted file mode 100644 index 5ae93cd..0000000 --- a/test/web/50-PouchEdvClient.js +++ /dev/null @@ -1,689 +0,0 @@ -/*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. - */ -import { - generateLocalId, initialize, PouchEdvClient, secrets -} from '@bedrock/web-pouch-edv'; -import {mock} from './mock.js'; - -const cipherVersions = ['recommended', 'fips']; - -describe('PouchEdvClient API', function() { - before(async () => { - await initialize(); - }); - - cipherVersions.forEach(cipherVersion => { - describe(`"${cipherVersion}" cipher version`, () => { - describe('createEdv', () => { - it('should fail with both password and keys specified', async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - let error; - try { - await PouchEdvClient.createEdv({config, password, cipherVersion}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('Error'); - error.message.should.equal( - '"config" must not have "hmac" or "keyAgreementKey" if these are ' + - 'to be populated using locally generated secrets.'); - }); - it('should pass with no password', async () => { - const config = { - ...mock.config, - id: await generateLocalId() - }; - const result = await PouchEdvClient.createEdv( - {config, cipherVersion}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['config', 'edvClient']); - should.not.exist(result.edvClient); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'controller', 'sequence', 'hmac', 'keyAgreementKey']); - }); - it('should pass with password', async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['config', 'edvClient']); - should.exist(result.edvClient); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'controller', 'sequence', 'hmac', 'keyAgreementKey']); - }); - it('should pass with pregenerated secret', async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - - // generate secret to simulate creating an EDV where secret - // generation is successful but a failure occurs before the EDV - // config is inserted allowing continuation of this process provided - // the password matches - const {config: secretConfig} = await secrets.generate({ - id: config.id, password - }); - await secrets.insert({config: secretConfig}); - - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['config', 'edvClient']); - should.exist(result.edvClient); - should.exist(result.config); - result.config.should.have.keys( - ['id', 'controller', 'sequence', 'hmac', 'keyAgreementKey']); - }); - it('should fail with pregenerated secret + invalid password', - async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - - // generate secret to simulate creating an EDV where secret - // generation is successful but a failure occurs before the EDV - // config is inserted allowing continuation of this process - // provided the password matches - const {config: secretConfig} = await secrets.generate({ - id: config.id, password, cipherVersion - }); - await secrets.insert({config: secretConfig}); - - let error; - try { - await PouchEdvClient.createEdv( - {config, password: 'invalid', cipherVersion}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('Error'); - error.message.should.equal( - `Secret already exists for EDV ID (${config.id}) but password ` + - 'to unlock it is invalid.'); - }); - it('should fail with duplicate EDV', async () => { - // first insert EDV - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - await PouchEdvClient.createEdv({config, password, cipherVersion}); - - // now try again - let error; - try { - await PouchEdvClient.createEdv({config, password, cipherVersion}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('DuplicateError'); - error.message.should.equal('Duplicate EDV configuration.'); - }); - }); - - describe('fromLocalSecrets', () => { - it('should fail due to not found', async () => { - const password = 'pw'; - let error; - try { - const edvId = await generateLocalId(); - await PouchEdvClient.fromLocalSecrets({edvId, password}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Configuration not found.'); - }); - it('should fail due to invalid password', async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - await PouchEdvClient.createEdv({config, password, cipherVersion}); - - let error; - try { - await PouchEdvClient.fromLocalSecrets({ - edvId: config.id, password: 'invalid' - }); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('Error'); - error.message.should.equal('Invalid password.'); - }); - it('should pass', async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - await PouchEdvClient.createEdv({config, password, cipherVersion}); - - const edvClient = await PouchEdvClient.fromLocalSecrets({ - edvId: config.id, password - }); - should.exist(edvClient); - edvClient.should.be.an('object'); - }); - }); - - describe('insert', () => { - let edvClient; - beforeEach(async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - edvClient = result.edvClient; - }); - it('should fail "doc.content" assertion', async () => { - const doc = {}; - let error; - try { - await edvClient.insert({doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"content" must be an object.'); - }); - it('should pass', async () => { - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {} - }; - const result = await edvClient.insert({doc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys([ - 'id', 'sequence', 'indexed', 'jwe', 'content', 'meta' - ]); - result.id.should.equal(doc.id); - result.sequence.should.equal(0); - }); - }); - - describe('update', () => { - let edvClient; - beforeEach(async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - edvClient = result.edvClient; - }); - it('should fail "doc.content" assertion', async () => { - const doc = {}; - let error; - try { - await edvClient.update({doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"content" must be an object.'); - }); - it('should pass', async () => { - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {} - }; - const result = await edvClient.update({doc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys([ - 'id', 'sequence', 'indexed', 'jwe', 'content', 'meta' - ]); - result.id.should.equal(doc.id); - result.sequence.should.equal(0); - }); - it('should fail due to bad sequence', async () => { - // first insert - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {} - }; - await edvClient.insert({doc}); - - // then update w/o updating sequence - let error; - try { - await edvClient.update({doc}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('InvalidStateError'); - }); - it('should pass with sequence change', async () => { - // first insert - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {}, - sequence: 0 - }; - await edvClient.insert({doc}); - - // then update - const result = await edvClient.update({doc}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys([ - 'id', 'sequence', 'indexed', 'jwe', 'content', 'meta' - ]); - result.id.should.equal(doc.id); - result.sequence.should.equal(1); - }); - it('should fail due to duplicate unique attribute', async () => { - await edvClient.ensureIndex({attribute: 'content.id', unique: true}); - - const doc = { - id: await generateLocalId(), - content: {id: 'foo'} - }; - await edvClient.update({doc}); - - // upsert different doc with same attribute - const doc2 = { - id: await generateLocalId(), - content: {id: 'foo'} - }; - let error; - try { - await edvClient.update({doc: doc2}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('DuplicateError'); - }); - it('should pass with non-conflicting attribute', async () => { - await edvClient.ensureIndex({attribute: 'content.id', unique: true}); - - const doc = { - id: await generateLocalId(), - content: {id: 'foo', bar: 'baz'} - }; - const inserted1 = await edvClient.update({doc}); - - // upsert different doc with different value for unique attributes - // but same value for non-unique attribute (should be legal) - const doc2 = { - id: await generateLocalId(), - content: {id: 'different', bar: 'baz'} - }; - const inserted2 = await edvClient.update({doc: doc2}); - - const getDoc1 = await edvClient.get({id: doc.id}); - getDoc1.should.eql(inserted1); - const getDoc2 = await edvClient.get({id: doc2.id}); - getDoc2.should.eql(inserted2); - }); - it('should fail due to change to duplicate unique attribute', - async () => { - await edvClient.ensureIndex( - {attribute: 'content.id', unique: true}); - - const doc = { - id: await generateLocalId(), - content: {id: 'foo', bar: 'baz'} - }; - await edvClient.update({doc}); - - // upsert different doc with different value for unique attributes - // but same value for non-unique attribute (should be legal) - let doc2 = { - id: await generateLocalId(), - content: {id: 'different', bar: 'baz'} - }; - await edvClient.update({doc: doc2}); - - // now should fail when trying to change `doc2` to have the same - // unique attribute as `doc1` - doc2 = { - id: doc2.id, - content: {...doc.content}, - sequence: 1 - }; - let error; - try { - await edvClient.update({doc: doc2}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('DuplicateError'); - }); - }); - - describe('find', () => { - let edvClient; - beforeEach(async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - edvClient = result.edvClient; - }); - it('should get a document by attribute', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - // first insert doc - const doc = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - const inserted = await edvClient.insert({doc}); - - // then find doc - const result = await edvClient.find({has: 'content.foo'}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(1); - should.exist(result.documents[0]); - result.documents[0].should.eql(inserted); - }); - it('should get a document by attribute and value', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - // first insert doc - const doc = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - const inserted = await edvClient.insert({doc}); - - // then find doc - const result = await edvClient.find({equals: {'content.foo': 'bar'}}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(1); - should.exist(result.documents[0]); - result.documents[0].should.eql(inserted); - }); - it('should get documents by attribute and value', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - // insert 3 docs, each with different attribute values, find only 2 - const doc1 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc1}); - - const doc2 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc2}); - - const doc3 = { - id: await generateLocalId(), - content: {foo: 'different'} - }; - await edvClient.insert({doc: doc3}); - - // then find 2 docs out of 3 - const result = await edvClient.find({equals: {'content.foo': 'bar'}}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(2); - result.documents.map(({id}) => id).should.include.members([ - doc1.id, doc2.id - ]); - }); - it('should get documents w/limit', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - const doc1 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc1}); - - const doc2 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc2}); - - // limit results to just 1 of the 2 docs - const result = await edvClient.find({ - equals: {'content.foo': 'bar'}, - limit: 1 - }); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents', 'hasMore']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(1); - should.exist(result.hasMore); - result.hasMore.should.equal(true); - }); - it('should get documents w/limit and hasMore=false', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - const doc1 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc1}); - - const doc2 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc2}); - - // limit results to just 1 of the 2 docs - const result = await edvClient.find({ - equals: {'content.foo': 'bar'}, - limit: 2 - }); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents', 'hasMore']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(2); - result.documents.map(({id}) => id).should.include.members([ - doc1.id, doc2.id - ]); - should.exist(result.hasMore); - result.hasMore.should.equal(false); - }); - it('should get no documents', async () => { - await edvClient.ensureIndex({attribute: 'content.foo'}); - - // insert 3 docs, each with different attribute values, find none - const doc1 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc1}); - - const doc2 = { - id: await generateLocalId(), - content: {foo: 'bar'} - }; - await edvClient.insert({doc: doc2}); - - const doc3 = { - id: await generateLocalId(), - content: {foo: 'different'} - }; - await edvClient.insert({doc: doc3}); - - // then find no matching docs - const result = await edvClient.find({ - equals: {'content.foo': 'nomatches'} - }); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys(['documents']); - should.exist(result.documents); - result.documents.should.be.an('array'); - result.documents.length.should.equal(0); - }); - }); - - describe('get', () => { - let edvClient; - beforeEach(async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - edvClient = result.edvClient; - }); - it('should fail "id" assertion', async () => { - let error; - try { - await edvClient.get({id: false}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('TypeError'); - error.message.should.equal('"id" must be a string.'); - }); - it('should fail due to not found error', async () => { - // get non-existent doc - let error; - try { - await edvClient.get({id: await PouchEdvClient.generateId()}); - } catch(e) { - error = e; - } - should.exist(error); - error.name.should.equal('NotFoundError'); - error.message.should.equal('Document not found.'); - }); - it('should pass', async () => { - // first insert - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {} - }; - await edvClient.insert({doc}); - - // then get document - const result = await edvClient.get({id: doc.id}); - should.exist(result); - result.should.be.an('object'); - result.should.have.keys([ - 'id', 'sequence', 'indexed', 'jwe', 'content', 'meta' - ]); - result.id.should.equal(doc.id); - result.sequence.should.equal(0); - }); - }); - - describe('delete', () => { - let edvClient; - beforeEach(async () => { - const password = 'pw'; - const config = { - ...mock.config, - id: await generateLocalId() - }; - delete config.hmac; - delete config.keyAgreementKey; - const result = await PouchEdvClient.createEdv( - {config, password, cipherVersion}); - edvClient = result.edvClient; - }); - it('should pass', async () => { - // first insert - const doc = { - id: await PouchEdvClient.generateId(), - content: {}, - meta: {}, - sequence: 0 - }; - await edvClient.insert({doc}); - - // then delete document - const result = await edvClient.delete({doc}); - should.exist(result); - result.should.equal(true); - }); - }); - }); - }); -}); diff --git a/test/web/mock.js b/test/web/mock.js deleted file mode 100644 index bd2b19b..0000000 --- a/test/web/mock.js +++ /dev/null @@ -1,197 +0,0 @@ -/*! - * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved. - */ -export const mock = {}; - -mock.config = { - id: 'z1ABL7T6jyuXvoGSUfdxCAnio', - controller: 'urn:controller', - sequence: 0, - hmac: { - id: 'urn:hmac', - type: 'urn:hmac-type' - }, - keyAgreementKey: { - id: 'urn:keyAgreement', - type: 'urn:keyAgreement-type' - } -}; - -mock.doc = { - id: 'z1ABxUcbcnSyMtnenFmeARhUn', - sequence: 0, - jwe: { - protected: 'eyJlbmMiOiJDMjBQIn0', - recipients: [{ - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey' - }, - encrypted_key: - 'HM00migkUSdZjvqmq4b7ixiXnfeLieA7QX2ew6OF4oPUA3HovaMnOw' - }], - iv: 'S-bNe9DayHcXWhBH', - ciphertext: 'bcZnPyreRmcLCngVbMHJTNeIIxkSJno', - tag: 'R2xDL9AJo7IhZ7y_sebgJw' - } -}; - -mock.docWithEmptyAttributes = { - id: 'z19pjdSMQMkBqqJ5zsaagncfU', - sequence: 0, - indexed: [{ - hmac: { - id: 'urn:hmac', - type: 'Sha256HmacKey2019' - }, - sequence: 0, - attributes: [] - }], - jwe: { - protected: 'eyJlbmMiOiJDMjBQIn0', - recipients: [{ - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey' - }, - encrypted_key: - 'HM00migkUSdZjvqmq4b7ixiXnfeLieA7QX2ew6OF4oPUA3HovaMnOw' - }], - iv: 'S-bNe9DayHcXWhBH', - ciphertext: 'bcZnPyreRmcLCngVbMHJTNeIIxkSJno', - tag: 'R2xDL9AJo7IhZ7y_sebgJw' - } -}; - -mock.docWithAttributes = { - id: 'z19pjdSMQMkBqqJ5zsbbgbbbb', - sequence: 0, - indexed: [{ - hmac: { - id: 'urn:hmac', - type: 'Sha256HmacKey2019' - }, - sequence: 0, - attributes: [{ - name: 'CUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloY', - value: 'QV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUBro' - }, { - name: 'CUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloY', - value: 'QV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUSis' - }] - }], - jwe: { - protected: 'eyJlbmMiOiJDMjBQIn0', - recipients: [{ - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey' - }, - encrypted_key: - 'OR1vdCNvf_B68mfUxFQVT-vyXVrBembuiM40mAAjDC1-Qu5iArDbug' - }], - iv: 'i8Nins2vTI3PlrYW', - ciphertext: 'Cb-963UCXblINT8F6MDHzMJN9EAhK3I', - tag: 'pfZO0JulJcrc3trOZy8rjA' - } -}; - -mock.docWithUniqueAttributes = { - id: 'z19pjdSMQMkBqqJ5zsbbgcccc', - sequence: 0, - indexed: [{ - hmac: { - id: 'urn:hmac', - type: 'Sha256HmacKey2019' - }, - sequence: 0, - attributes: [{ - name: 'CUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloZ', - value: 'QV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUBro', - unique: true - }, { - name: 'DUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloZ', - value: 'QV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUBro' - }] - }], - jwe: { - protected: 'eyJlbmMiOiJDMjBQIn0', - recipients: [{ - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey' - }, - encrypted_key: - 'OR1vdCNvf_B68mfUxFQVT-vyXVrBembuiM40mAAjDC1-Qu5iArDbug' - }], - iv: 'i8Nins2vTI3PlrYW', - ciphertext: 'Cb-963UCXblINT8F6MDHzMJN9EAhK3I', - tag: 'pfZO0JulJcrc3trOZy8rjA' - } -}; - -mock.docWithUniqueAttributes2 = { - id: 'z19pjdSMQMkBqqJ5zsbbggggg', - sequence: 0, - indexed: [{ - hmac: { - id: 'urn:hmac', - type: 'Sha256HmacKey2019' - }, - sequence: 0, - attributes: [{ - name: 'CUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloZ', - // different from `mock.docWithAttributes`, so permitted - value: 'RV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUBro', - unique: true - }, { - name: 'DUQaxPtSLtd8L3WBAIkJ4DiVJeqoF6bdnhR7lSaPloZ', - // same as `mock.docWithUniqueAttributes` but not unique, so permitted - value: 'QV58Va4904K-18_L5g_vfARXRWEB00knFSGPpukUBro' - }] - }], - jwe: { - protected: 'eyJlbmMiOiJDMjBQIn0', - recipients: [{ - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey' - }, - encrypted_key: - 'OR1vdCNvf_B68mfUxFQVT-vyXVrBembuiM40mAAjDC1-Qu5iArDbug' - }], - iv: 'i8Nins2vTI3PlrYW', - ciphertext: 'Cb-963UCXblINT8F6MDHzMJN9EAhK3I', - tag: 'pfZO0JulJcrc3trOZy8rjA' - } -}; - -mock.chunk = { - sequence: 0, - index: 0, - offset: 50, - jwe: { - protected: 'eyJlbmMiOiJYQzIwUCJ9', - recipients: [ - { - header: { - alg: 'ECDH-ES+A256KW', - kid: 'urn:keyAgreementKey', - epk: { - kty: 'OKP', - crv: 'X25519', - x: 'ev3MBTNmIQwWsILAREvOMkGo9RZ-Bp0NZmkno9dno0Q' - }, - apu: 'ev3MBTNmIQwWsILAREvOMkGo9RZ-Bp0NZmkno9dno0Q', - // eslint-disable-next-line max-len - apv: 'ZGlkOmtleTp6Nk1rajVFS1BNVzZpYmJoeVA1ZHE2TmplbVF3NnJXM1gyeGFyRHhyWVYyZHppQjkjejZMU2VyWmVXN1V5cmFzbktLOTdldER5Mnp4ZTRtZkFRRlRrQ1RTcXFWc1FEUjdK' - }, - encrypted_key: 'nty5cWPhIlHJUbdbHN7IAlaVzUo9-pxlf3lq4OE9jqzf7lA0VByltg' - } - ], - iv: 'pu_NyyiQ0ZQG-RCpwrgZgHL6P4CUSuYN', - // eslint-disable-next-line max-len - ciphertext: '6rUTYWGaUyg1dp32NR0MwaikovIjNIaUy511IqAGEl4_xunzZDTy_IPzpenf2j3_OJg', - tag: 'vnKPGqBKs4wx_Mlfkzz7EA' - } -}; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..3af7b8d --- /dev/null +++ b/vite.config.js @@ -0,0 +1,17 @@ +import {defineConfig} from 'vite'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import vue from '@vitejs/plugin-vue'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': path.resolve(__dirname, './demo'), + }, + }, +}); +