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
+
+
+ Scan Barcode
+
+
+
+
+
+
+
+
+```
+
+### 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ There was an error loading your camera. Please upload a photo instead
+ or refresh the page.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Upload Image
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
Optical Scanner Demo
+
+
+
+
+
+
+ Scan Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Architecture Flow:
+
+
+ Vue (UI Only) -> CameraScanner (Business Logic)
+ -> OpticalScanner (Core Engine)
+ -> Plugins (Format-Specific)
+
+
+
+
+
+
+
+
+
+ UI Props Testing
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Current Configuration:
+
+
+ Scan Type: {{scanType}}
+ Scan Mode: {{scanMode}}
+ Tip Text: "{{tipText}}"
+ Expected Behavior: {{expectedBehavior}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scan Result
+
+
+
+
+
+
+
+ Architecture Test Passed!
+ Vue -> CameraScanner -> OpticalScanner ->
+ Plugins delegation worked successfully
+
+
+
+
+
+ Scan Type: {{scanResult.scanType || 'Unknown'}}
+ Format Detected:
+ {{scanResult.format || scanResult.type}}
+ Timestamp:
+ {{scanResult.timestamp || 'Not provided'}}
+ Success: {{scanResult.success ? 'Yes' : 'No'}}
+
+
+
+
+
+
+ MRZ Document Data:
+
+
+
+ MRZ Validation: Valid
+
+
+ MRZ Validation: Invalid
+
+
+
+
+
+ {{formatFieldName(key)}}: {{value || 'N/A'}}
+
+
+
+
+
+
+
+
+
+ Validation Details
+
+
+
+
+
+
+
+ Overall Status:
+ {{scanResult.validation.overallStatus}}
+
+
+ Completeness:
+ {{scanResult.validation.statistics.overallCompleteness}}
+ %
+
+
+ Invalid Fields:
+ {{scanResult.invalidFields.join(', ')}}
+
+
+
+
+
+
+
+
+
+
+
+
+ Driver License Data:
+
+
+
+
+
+ {{key}}:
+ {{formatDriverLicenseField(fieldData)}}
+
+
+
+
+
+
+
+
+
+ Barcode Content:
+
+
+
+ {{scanResult.text || 'No text content'}}
+
+
+
+
+
+
+
+
+ 🛠️ Raw Scan Data (Debug)
+
+
+
+
+
+
{{JSON.stringify(scanResult, null, 2)}}
+
+
+
+
+
+
+
+
+
+
+
+
+ Scan Error
+
+
+ Message: {{scanError.message || scanError}}
+ Code: {{scanError.code || 'Unknown'}}
+
+
+
+
+
+
+
+
+
+
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'),
+ },
+ },
+});
+