diff --git a/CLAUDE.md b/CLAUDE.md
index 6963ee1f..6169c932 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -154,6 +154,48 @@ Existing data needs the one-off `scripts/migrate-is-owned-to-owned-versions.js`
then a `scripts/mongodb-create-index.js` re-run to replace the old `is_owned` index definitions.
Covered by the resource tests' owned-filter cases (including a full decimal/date round-trip in `MovieResourceTest`/`AlbumResourceTest`) and `OwnershipSmokeTest` (Playwright, end-to-end through the editor).
+`VideoGamePlatformModel.ProductName` (free text) is the store's own specific product/edition text for that copy (e.g. "Grand Theft Auto V : Édition Premium"), distinct from the game's own `Title` -
+added for the generic video game transaction import below, but it's a plain field on the shared model/entity/DTO, editable on `VideoGameDetail.razor` like any other copy detail regardless of how the platform entry was created.
+It renders via a new `ExtraFields` render-fragment slot on the shared `OwnedVersionFields` component (`Components/Inventory/Shared/`), not by adding it to `IOwnedCopyDto`:
+Movie/TvShow/Book/Album's `OwnedVersionDto` has no equivalent concept, so the interface every `OwnedVersionFields` caller shares stays free of a field only one of them needs.
+The component's own Price/Acquired/Vendor/Reference columns switched from a fixed `col-md-3` to an unnumbered `col-md` (Bootstrap's equal-width auto layout) for this -
+with no `ExtraFields` supplied they still fill one row identically to the old fixed split,
+but a 5th `ExtraFields` column (VideoGame's Product field) joins the same row and every column re-shares the width automatically instead of wrapping to a second row on desktop,
+while `col-6` still stacks two-per-row on mobile like the others.
+
+### Bulk store/retailer transaction imports (Amazon, generic video game transactions)
+
+`AmazonImportController`/`AmazonOrderPreviewService` (preview an uploaded order-history CSV, let the user pick a type and edit fields per row, then commit) was the first of this shape,
+followed by `GenericVideoGameImportController`/`GenericVideoGameImportService` for a video-game-only transaction-history CSV (PSN's own GDPR export today; the format isn't PlayStation-specific -
+`Vendor` is a per-row column, not hardcoded - so any store exporting the same shape would work).
+Both follow `WatchNextController`/`WishlistController`'s split (controller in `Controllers/`, pure parsing/computation in `Domain/Services/`), not the older `WebApi/Import/` feature-folder shape.
+The create/merge/dedup engine behind both (`ComputeCommitPlan`/`FindImportedReferences`, matching by normalized title via `TitleNormalizer`,
+merging within the same commit batch too) started out Amazon-only (`AmazonImportMergeService`) but was already fully generic - it moved to `Domain/Services/OwnedItemImportMergeService.cs`
+(and its return type to `Domain/Models/ImportCommitPlan.cs`, renamed off `AmazonImportPlan`) once the video game importer needed the exact same engine, rather than duplicating it.
+`AmazonImportMergeService` kept only what's genuinely Amazon-specific: `FormatOrderReference` (ASIN + order id) and `BuildAmazonProvenanceNotes`.
+
+**Gotcha, confirmed against a real PSN export:** a transaction/order id pair is *not* reliably unique per line the way Amazon's ASIN is.
+A single PSN transaction can bundle several different products under one shared Transaction Id/Order Id (confirmed with a real export:
+one transaction contained three separate "Far Cry 4" DLC packs, each its own CSV line, distinguishable only by Product Name).
+`GenericVideoGameImportService.FormatReference` originally built the owned-copy `Reference` (and dedup key) from transaction id + order id alone,
+the same class of bug `AmazonImportMergeService.FormatOrderReference` already avoids by including the ASIN -
+without a per-product disambiguator, the second and third bundled lines collided with the first's reference and were silently skipped as "already imported" duplicates,
+so only 1 of the 3 platform entries actually got created from a 3-line selection.
+Fixed by appending the row's own Product Name (falling back to the title when blank) to the reference text, mirroring the ASIN's role for Amazon.
+`GenericVideoGameImportPreviewRow.RowId` had the identical problem (plain `TransactionId`, colliding across the same bundled lines) and got the same fix.
+Covered by `GenericVideoGameImportServiceTest.FormatReference_Disambiguates_WhenTwoLinesShareTheSameTransactionAndOrder`
+and `GenericVideoGameImportResourceTest.PreviewThenCommit_ImportsAllThreeLines_WhenTheyShareOneTransactionAndOrderButDifferentProducts`.
+
+**`VideoGamesCreated`/`VideoGamesMergedInto` count distinct video games, not selected rows - this undercounts rows on purpose, not a bug.**
+Several selected rows sharing a normalized title consolidate into one item within a single commit batch (`ComputeCommitPlan`'s existing same-batch-merge behavior),
+which is correct but means `Created + MergedInto` can come out lower than the number of rows the user selected,
+which read as a discrepancy on first use of the real 104-row PSN export (98 selected, "1 created, 0 merged, 2 already imported" summed to only 3 - actually the *bundling* bug above,
+but the created/merged-vs-selected gap is real and independent of it).
+`ImportCommitPlan.OwnedCopiesAdded`/`GenericVideoGameImportCommitResultDto.RowsImported` is the true per-row count (`RowsImported + VideoGamesSkipped` always equals the number of rows submitted),
+and `SkippedRowTitles` lists exactly which selected rows were skipped as already-imported duplicates -
+together they let `GenericVideoGameImportPage.razor` show a reconciling "X of Y selected rows imported" line plus a named list of anything actually skipped,
+so the user can trust nothing was silently dropped instead of having to guess from the per-item counts alone.
+
### Child entities (1-to-many owned by another entity)
`CarHistory` (owned by `Car`) and `Episode` (owned by `TvShow`) are separate top-level collections referencing their parent by id (`car_id`, `tv_show_id`), not embedded arrays.
diff --git a/Directory.Build.props b/Directory.Build.props
index d4cacdfb..0a5f0828 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
- 2.3.0
+ 2.4.0
diff --git a/docs/prerender-flash-fix.md b/docs/prerender-flash-fix.md
index 63137a6d..1c3901fe 100644
--- a/docs/prerender-flash-fix.md
+++ b/docs/prerender-flash-fix.md
@@ -113,6 +113,14 @@ Two real options, and I have a clear preference:
The detail pages persist a single bounded DTO and are fine to keep as-is.
2. Raise the limit via .AddHubOptions(o => o.MaximumReceiveMessageSize = 256 * 1024) — one line, keeps the no-refetch behavior, but the payload still grows with your collection and the cliff just moves further out.
+**Update**: "the detail pages persist a single bounded DTO" turned out not to hold for TvShowDetail.
+`TvShowReferenceModel.Episodes` is embedded (see CLAUDE.md's "Reference data" section) and unbounded per show,
+so `[PersistentState]`-serializing `Reference` plus the raw `Episodes` list hit the exact same 32 KB SignalR ceiling for shows with many seasons/episodes.
+`[PersistentState]` was reverted from `TvShowDetail.razor`
+(back to plain private fields `_show`/`_reference`/`_episodes`, `OnParametersSetAsync` always calls `LoadAsync`, no restore-skip path) for the same reason it was dropped from Watch Next/Wishlist above.
+The `_loaded`/`_loading` flash fix from "Fix loading flash" stays — it isn't part of the persisted-state mechanism and never caused this issue.
+Movie/Book/Album/VideoGame/Car/House/HealthProfile/Playlist detail pages keep `[PersistentState]` since their persisted DTOs stay genuinely bounded (no embedded unbounded list like `Episodes`).
+
Sources:
[dotnet/aspnetcore#65101](https://github.com/dotnet/aspnetcore/issues/65101),
[Blazor SignalR guidance — message size](https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/signalr),
diff --git a/samples/angular/.editorconfig b/samples/angular/.editorconfig
deleted file mode 100644
index d0dcd4a4..00000000
--- a/samples/angular/.editorconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-# Editor configuration, see http://editorconfig.org
-root = true
-
-[*]
-charset = utf-8
-indent_style = space
-indent_size = 2
-insert_final_newline = true
-trim_trailing_whitespace = true
-end_of_line = lf
-
-[*.md]
-max_line_length = off
-trim_trailing_whitespace = false
diff --git a/samples/angular/.eslintrc.json b/samples/angular/.eslintrc.json
deleted file mode 100644
index 0ac5f715..00000000
--- a/samples/angular/.eslintrc.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "root": true,
- "ignorePatterns": [
- "dist",
- "results",
- "coverage"
- ],
- "parserOptions": {
- "ecmaVersion": 2020
- },
- "overrides": [
- {
- "files": [
- "*.ts"
- ],
- "parserOptions": {
- "project": [
- "tsconfig.json",
- "e2e/tsconfig.json"
- ],
- "createDefaultProgram": true
- },
- "extends": [
- "plugin:@angular-eslint/recommended",
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "rules": {
- "@typescript-eslint/consistent-type-definitions": "error",
- "@typescript-eslint/dot-notation": "off",
- "@typescript-eslint/explicit-member-accessibility": [
- "off",
- {
- "accessibility": "explicit"
- }
- ],
- "@typescript-eslint/naming-convention": "warn",
- "brace-style": [
- "error",
- "1tbs"
- ],
- "id-blacklist": "off",
- "id-match": "off",
- "no-underscore-dangle": "off"
- }
- },
- {
- "files": [
- "*.html"
- ],
- "extends": [
- "plugin:@angular-eslint/template/recommended"
- ],
- "rules": {}
- }
- ]
-}
diff --git a/samples/angular/.gitignore b/samples/angular/.gitignore
deleted file mode 100644
index af4a6f0d..00000000
--- a/samples/angular/.gitignore
+++ /dev/null
@@ -1,55 +0,0 @@
-# See http://help.github.com/ignore-files/ for more about ignoring files.
-
-# compiled output
-/dist
-/dist-server
-/tmp
-/out-tsc
-
-# dependencies
-/node_modules
-
-# IDEs and editors
-/.idea
-.project
-.classpath
-.c9/
-*.launch
-.settings/
-*.sublime-workspace
-
-# IDE - VSCode
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-
-# misc
-/.angular/cache
-/.sass-cache
-/connect.lock
-/coverage
-/libpeerconnection.log
-npm-debug.log
-yarn-error.log
-testem.log
-/typings
-
-# System Files
-.DS_Store
-Thumbs.db
-
-# Firebase
-.firebaserc
-
-# Angular development config
-environment.dev.ts
-environment.prod.ts
-
-# Temporary files
-/temp.txt
-
-# Test report files
-/TESTS*.xml
-/coverage
diff --git a/samples/angular/README.md b/samples/angular/README.md
deleted file mode 100644
index 520fe3fb..00000000
--- a/samples/angular/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Keeptrack Angular frontend application
-
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli).
-
-## Local setup
-
-### Requirements
-
-* [NPM (Node.js)](https://nodejs.org/en/)
-* [Angular CLI](https://cli.angular.io/): `npm install -g @angular/cli`
-* [Firebase CLI](https://www.npmjs.com/package/firebase-tools): `npm install -g firebase-tools`
-
-### Configuration
-
-* Create a file `src/environments/environment.dev.ts` and update it (this file is not purposed to node save secret values)
- * Open your project in the [Firebase console](https://console.firebase.google.com/), go in the properties pages (General tab), inside "Your apps" part you'll have all Firebase information
- * Get the value from your local API URL (`https://localhost:5011`)
-
-### Installation of package
-
-Run `npm install` to load all dependencies.
-
-### Development server
-
-Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
-
-_Warning_: you may encounter CORS issues while testing with Firefox, use Chrome for local debug
-
-### Code scaffolding
-
-Run `ng generate component / --module=` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
-
-### Build
-
-Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
-
-### Running unit tests
-
-Run `npm run test` to execute the unit tests via [Karma](https://karma-runner.github.io).
-
-### Running end-to-end tests
-
-Run `npm run e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
-
-### Running linter
-
-Run `npm run lint` to validate source code standards.
-
-### Further help
-
-To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
-
-### Update procedure
-
-* Update NPM: `npm install --global npm`
-* Follow procedure described by [update.angular.io](https://update.angular.io/): `ng update `
-
-### Firebase guide
-
-* [Getting started with Firebase Authentication](https://github.com/angular/angularfire/blob/master/docs/auth/getting-started.md)
-* [angularfire/samples/compat](https://github.com/angular/angularfire/tree/master/samples/compat)
-
-### Graphic design
-
-* [Bootstrap v4.6](https://getbootstrap.com/docs/4.6/getting-started/introduction/)
-* [Font Awesome v4.7.0](https://fontawesome.bootstrapcheatsheets.com/)
diff --git a/samples/angular/angular.json b/samples/angular/angular.json
deleted file mode 100644
index 52a102ca..00000000
--- a/samples/angular/angular.json
+++ /dev/null
@@ -1,191 +0,0 @@
-{
- "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
- "version": 1,
- "newProjectRoot": "projects",
- "projects": {
- "AngularWebApp": {
- "projectType": "application",
- "schematics": {
- "@schematics/angular:component": {
- "standalone": true
- },
- "@schematics/angular:directive": {
- "standalone": true
- },
- "@schematics/angular:pipe": {
- "standalone": true
- }
- },
- "root": "",
- "sourceRoot": "src",
- "prefix": "app",
- "architect": {
- "build": {
- "builder": "@angular/build:application",
- "options": {
- "progress": false,
- "outputPath": {
- "base": "dist"
- },
- "index": "src/index.html",
- "polyfills": [
- "@angular/localize/init",
- "zone.js"
- ],
- "tsConfig": "tsconfig.app.json",
- "assets": [
- "src/assets",
- "src/web.config"
- ],
- "styles": [
- "node_modules/bootstrap/dist/css/bootstrap.min.css",
- "node_modules/font-awesome/css/font-awesome.css",
- "src/styles.css"
- ],
- "scripts": [],
- "extractLicenses": false,
- "sourceMap": true,
- "optimization": false,
- "namedChunks": true,
- "browser": "src/main.ts"
- },
- "configurations": {
- "dev": {
- "budgets": [
- {
- "type": "anyComponentStyle",
- "maximumWarning": "6kb"
- }
- ],
- "fileReplacements": [
- {
- "replace": "src/environments/environment.ts",
- "with": "src/environments/environment.dev.ts"
- }
- ]
- },
- "production": {
- "budgets": [
- {
- "type": "anyComponentStyle",
- "maximumWarning": "6kb"
- }
- ],
- "fileReplacements": [
- {
- "replace": "src/environments/environment.ts",
- "with": "src/environments/environment.prod.ts"
- }
- ],
- "optimization": true,
- "outputHashing": "all",
- "sourceMap": false,
- "namedChunks": false,
- "extractLicenses": true
- }
- },
- "defaultConfiguration": ""
- },
- "serve": {
- "builder": "@angular/build:dev-server",
- "options": {
- "buildTarget": "AngularWebApp:build"
- },
- "configurations": {
- "dev": {
- "buildTarget": "AngularWebApp:build:dev"
- },
- "production": {
- "buildTarget": "AngularWebApp:build:production"
- }
- }
- },
- "extract-i18n": {
- "builder": "@angular/build:extract-i18n",
- "options": {
- "buildTarget": "AngularWebApp:build"
- }
- },
- "test": {
- "builder": "@angular/build:karma",
- "options": {
- "polyfills": [
- "@angular/localize/init",
- "zone.js",
- "zone.js/testing"
- ],
- "tsConfig": "tsconfig.spec.json",
- "karmaConfig": "karma.conf.js",
- "codeCoverageExclude": [
- "**/models/*.ts",
- "**/app.module.ts",
- "**/app.routes.ts"
- ],
- "assets": [
- "src/favicon.ico",
- "src/assets"
- ],
- "styles": [
- "src/styles.css"
- ],
- "scripts": []
- },
- "configurations": {
- "dev": {
- "fileReplacements": [
- {
- "replace": "src/environments/environment.ts",
- "with": "src/environments/environment.dev.ts"
- }
- ]
- }
- }
- },
- "lint": {
- "builder": "@angular-eslint/builder:lint",
- "options": {
- "lintFilePatterns": [
- "src/**/*.ts",
- "src/**/*.html"
- ]
- }
- }
- }
- }
- },
- "schematics": {
- "@angular-eslint/schematics:application": {
- "setParserOptionsProject": true
- },
- "@angular-eslint/schematics:library": {
- "setParserOptionsProject": true
- },
- "@schematics/angular:component": {
- "type": "component"
- },
- "@schematics/angular:directive": {
- "type": "directive"
- },
- "@schematics/angular:service": {
- "type": "service"
- },
- "@schematics/angular:guard": {
- "typeSeparator": "."
- },
- "@schematics/angular:interceptor": {
- "typeSeparator": "."
- },
- "@schematics/angular:module": {
- "typeSeparator": "."
- },
- "@schematics/angular:pipe": {
- "typeSeparator": "."
- },
- "@schematics/angular:resolver": {
- "typeSeparator": "."
- }
- },
- "cli": {
- "analytics": false
- }
-}
diff --git a/samples/angular/firebase.json b/samples/angular/firebase.json
deleted file mode 100644
index 189585aa..00000000
--- a/samples/angular/firebase.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "hosting": [
- {
- "target": "AngularWebApp",
- "public": "dist",
- "ignore": [
- "firebase.json",
- "**/.*",
- "**/node_modules/**"
- ],
- "rewrites": [
- {
- "source": "**",
- "destination": "/index.html"
- }
- ]
- }
- ]
-}
diff --git a/samples/angular/karma.conf.js b/samples/angular/karma.conf.js
deleted file mode 100644
index fc4f986d..00000000
--- a/samples/angular/karma.conf.js
+++ /dev/null
@@ -1,37 +0,0 @@
-module.exports = function (config) {
- config.set({
- basePath: '',
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
- plugins: [
- require('karma-jasmine'),
- require('karma-coverage'),
- require('karma-chrome-launcher'),
- require('karma-jasmine-html-reporter'),
- require('karma-junit-reporter')
- ],
- client: {
- jasmine: {},
- clearContext: false // leave Jasmine Spec Runner output visible in browser
- },
- preprocessors: {
- 'src/**/*.js': ['coverage']
- },
- coverageReporter: {
- dir: require('path').join(__dirname, '/coverage'),
- reporters: [
- { type: 'html', subdir: 'html' },
- { type: 'lcovonly', subdir: '.', file: 'lcov.info' }
- ]
- },
- junitReporter: {
- outputDir: require('path').join(__dirname, '.'),
- },
- reporters: ['progress', 'kjhtml', 'junit', 'coverage'],
- port: 9876,
- colors: true,
- logLevel: config.LOG_INFO,
- autoWatch: true,
- browsers: ['Chrome'], // 'Chrome', 'ChromeHeadless'
- singleRun: false
- });
-};
diff --git a/samples/angular/package-lock.json b/samples/angular/package-lock.json
deleted file mode 100644
index b6a9c239..00000000
--- a/samples/angular/package-lock.json
+++ /dev/null
@@ -1,12309 +0,0 @@
-{
- "name": "keeptrack-angularwebapp",
- "version": "0.1.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "keeptrack-angularwebapp",
- "version": "0.1.0",
- "dependencies": {
- "@angular/animations": "^21.2.1",
- "@angular/common": "^21.2.1",
- "@angular/compiler": "^21.2.1",
- "@angular/core": "^21.2.1",
- "@angular/fire": "^21.0.0-rc.0",
- "@angular/forms": "^21.2.1",
- "@angular/localize": "^21.2.1",
- "@angular/platform-browser": "^21.2.1",
- "@angular/platform-browser-dynamic": "^21.2.1",
- "@angular/platform-server": "^21.2.1",
- "@angular/router": "^21.2.1",
- "@popperjs/core": "~2.11.8",
- "bootstrap": "~5.3.2",
- "core-js": "^3.48.0",
- "font-awesome": "^4.7.0",
- "rxjs": "~7.8.2",
- "tslib": "^2.8.1",
- "zone.js": "~0.16.1"
- },
- "devDependencies": {
- "@angular-devkit/architect": "^0.2102.1",
- "@angular-eslint/builder": "^21.3.0",
- "@angular-eslint/eslint-plugin": "^21.3.0",
- "@angular-eslint/eslint-plugin-template": "^21.3.0",
- "@angular-eslint/schematics": "^21.3.0",
- "@angular-eslint/template-parser": "^21.3.0",
- "@angular/build": "^21.2.1",
- "@angular/cli": "^21.2.1",
- "@angular/compiler-cli": "^21.2.1",
- "@angular/language-service": "^21.2.1",
- "@types/jasmine": "~6.0.0",
- "@types/node": "^22.13.14",
- "acorn": "~8.16.0",
- "eslint": "^10.0.3",
- "fuzzy": "~0.1.3",
- "glob": "^13.0.6",
- "jasmine": "~6.1.0",
- "jasmine-core": "~6.1.0",
- "jasmine-spec-reporter": "~7.0.0",
- "karma": "~6.4.4",
- "karma-chrome-launcher": "~3.2.0",
- "karma-coverage": "~2.2.1",
- "karma-jasmine": "~5.1.0",
- "karma-jasmine-html-reporter": "~2.2.0",
- "karma-junit-reporter": "~2.0.1",
- "typescript": "~5.9.3"
- },
- "optionalDependencies": {
- "ts-node": "~10.7.0"
- }
- },
- "node_modules/@algolia/abtesting": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.1.tgz",
- "integrity": "sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-abtesting": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.48.1.tgz",
- "integrity": "sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-analytics": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.48.1.tgz",
- "integrity": "sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-common": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.48.1.tgz",
- "integrity": "sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-insights": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.48.1.tgz",
- "integrity": "sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-personalization": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.48.1.tgz",
- "integrity": "sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-query-suggestions": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.48.1.tgz",
- "integrity": "sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/client-search": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.48.1.tgz",
- "integrity": "sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/ingestion": {
- "version": "1.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.48.1.tgz",
- "integrity": "sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/monitoring": {
- "version": "1.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.48.1.tgz",
- "integrity": "sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/recommend": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.48.1.tgz",
- "integrity": "sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-browser-xhr": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.48.1.tgz",
- "integrity": "sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-fetch": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.48.1.tgz",
- "integrity": "sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@algolia/requester-node-http": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.48.1.tgz",
- "integrity": "sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/client-common": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@angular-devkit/architect": {
- "version": "0.2102.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.1.tgz",
- "integrity": "sha512-x2Qqz6oLYvEh9UBUG0AP1A4zROO/VP+k+zM9+4c2uZw1uqoBQFmutqgzncjVU7cR9R0RApgx9JRZHDFtQru68w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/core": "21.2.1",
- "rxjs": "7.8.2"
- },
- "bin": {
- "architect": "bin/cli.js"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- }
- },
- "node_modules/@angular-devkit/core": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.1.tgz",
- "integrity": "sha512-TpXGjERqVPN8EPt7LdmWAwh0oNQ/6uWFutzGZiXhJy81n1zb1O1XrqhRAmvP1cAo5O+na6IV2JkkCmxL6F8GUg==",
- "license": "MIT",
- "dependencies": {
- "ajv": "8.18.0",
- "ajv-formats": "3.0.1",
- "jsonc-parser": "3.3.1",
- "picomatch": "4.0.3",
- "rxjs": "7.8.2",
- "source-map": "0.7.6"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- },
- "peerDependencies": {
- "chokidar": "^5.0.0"
- },
- "peerDependenciesMeta": {
- "chokidar": {
- "optional": true
- }
- }
- },
- "node_modules/@angular-devkit/schematics": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.1.tgz",
- "integrity": "sha512-CWoamHaasAHMjHcYqxbj0tMnoXxdGotcAz2SpiuWtH28Lnf5xfbTaJn/lwdMP8Wdh4tgA+uYh2l45A5auCwmkw==",
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/core": "21.2.1",
- "jsonc-parser": "3.3.1",
- "magic-string": "0.30.21",
- "ora": "9.3.0",
- "rxjs": "7.8.2"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- }
- },
- "node_modules/@angular-eslint/builder": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-21.3.0.tgz",
- "integrity": "sha512-26QUUouei52biUFAlJSrWNAU9tuF2miKwd8uHdxWwCF31xz+OxC5+NfudWvt1AFaYow7gWueX1QX3rNNtSPDrg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/architect": ">= 0.2100.0 < 0.2200.0",
- "@angular-devkit/core": ">= 21.0.0 < 22.0.0"
- },
- "peerDependencies": {
- "@angular/cli": ">= 21.0.0 < 22.0.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": "*"
- }
- },
- "node_modules/@angular-eslint/bundled-angular-compiler": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-21.3.0.tgz",
- "integrity": "sha512-l521I24J9gJxyMbRkrM24Tc7W8J8BP+TDAmVs2nT8+lXbS3kg8QpWBRtd+hNUgq6o+vt+lKBkytnEfu8OiqeRg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@angular-eslint/eslint-plugin": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-21.3.0.tgz",
- "integrity": "sha512-Whf/AUUBekOlfSJRS78m76YGrBQAZ3waXE7oOdlW5xEQvn8jBDN9EGuNnjg/syZzvzjK4ZpYC4g1XYXrc+fQIg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-eslint/bundled-angular-compiler": "21.3.0",
- "@angular-eslint/utils": "21.3.0",
- "ts-api-utils": "^2.1.0"
- },
- "peerDependencies": {
- "@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": "*"
- }
- },
- "node_modules/@angular-eslint/eslint-plugin-template": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-21.3.0.tgz",
- "integrity": "sha512-lVixd/KypPWgA/5/pUOhJV9MTcaHjYZEqyOi+IiLk+h+maGxn6/s6Ot+20n+XGS85zAgOY+qUw6EEQ11hoojIQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-eslint/bundled-angular-compiler": "21.3.0",
- "@angular-eslint/utils": "21.3.0",
- "aria-query": "5.3.2",
- "axobject-query": "4.1.0"
- },
- "peerDependencies": {
- "@angular-eslint/template-parser": "21.3.0",
- "@typescript-eslint/types": "^7.11.0 || ^8.0.0",
- "@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": "*"
- }
- },
- "node_modules/@angular-eslint/schematics": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-21.3.0.tgz",
- "integrity": "sha512-8deU/zVY9f8k8kAQQ9PL130ox2VlrZw3fMxgsPNAY5tjQ0xk0J2YVSszYHhcqdMGG1J01IsxIjvQaJ4pFfEmMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/core": ">= 21.0.0 < 22.0.0",
- "@angular-devkit/schematics": ">= 21.0.0 < 22.0.0",
- "@angular-eslint/eslint-plugin": "21.3.0",
- "@angular-eslint/eslint-plugin-template": "21.3.0",
- "ignore": "7.0.5",
- "semver": "7.7.4",
- "strip-json-comments": "3.1.1"
- },
- "peerDependencies": {
- "@angular/cli": ">= 21.0.0 < 22.0.0"
- }
- },
- "node_modules/@angular-eslint/template-parser": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-21.3.0.tgz",
- "integrity": "sha512-ysyou1zAY6M6rSZNdIcYKGd4nk6TCapamyFNB3ivmTlVZ0O35TS9o/rJ0aUttuHgDp+Ysgs3ql+LA746PXgCyQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-eslint/bundled-angular-compiler": "21.3.0",
- "eslint-scope": "^9.1.1"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": "*"
- }
- },
- "node_modules/@angular-eslint/utils": {
- "version": "21.3.0",
- "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-21.3.0.tgz",
- "integrity": "sha512-oNigH6w3l+owTMboj/uFG0tHOy43uH8BpQRtBOQL1/s2+5in/BJ2Fjobv3SyizxTgeJ1FhRefbkT8GmVjK7jAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-eslint/bundled-angular-compiler": "21.3.0"
- },
- "peerDependencies": {
- "@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": "*"
- }
- },
- "node_modules/@angular/animations": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.2.1.tgz",
- "integrity": "sha512-zT/S29pUTbziCLvZ2itBdNWd5i8tsXexofH7KA4n2yvYmK1EhNpE7TlHRjghmsHgtDt4VnGiMW4zXEyrl05Dwg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/core": "21.2.1"
- }
- },
- "node_modules/@angular/build": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.1.tgz",
- "integrity": "sha512-cUpLNHJp9taII/FOcJHHfQYlMcZSRaf6eIxgSNS6Xfx1CeGoJNDN+J8+GFk+H1CPJt1EvbfyZ+dE5DbsgTD/QQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@ampproject/remapping": "2.3.0",
- "@angular-devkit/architect": "0.2102.1",
- "@babel/core": "7.29.0",
- "@babel/helper-annotate-as-pure": "7.27.3",
- "@babel/helper-split-export-declaration": "7.24.7",
- "@inquirer/confirm": "5.1.21",
- "@vitejs/plugin-basic-ssl": "2.1.4",
- "beasties": "0.4.1",
- "browserslist": "^4.26.0",
- "esbuild": "0.27.3",
- "https-proxy-agent": "7.0.6",
- "istanbul-lib-instrument": "6.0.3",
- "jsonc-parser": "3.3.1",
- "listr2": "9.0.5",
- "magic-string": "0.30.21",
- "mrmime": "2.0.1",
- "parse5-html-rewriting-stream": "8.0.0",
- "picomatch": "4.0.3",
- "piscina": "5.1.4",
- "rolldown": "1.0.0-rc.4",
- "sass": "1.97.3",
- "semver": "7.7.4",
- "source-map-support": "0.5.21",
- "tinyglobby": "0.2.15",
- "undici": "7.22.0",
- "vite": "7.3.1",
- "watchpack": "2.5.1"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- },
- "optionalDependencies": {
- "lmdb": "3.5.1"
- },
- "peerDependencies": {
- "@angular/compiler": "^21.0.0",
- "@angular/compiler-cli": "^21.0.0",
- "@angular/core": "^21.0.0",
- "@angular/localize": "^21.0.0",
- "@angular/platform-browser": "^21.0.0",
- "@angular/platform-server": "^21.0.0",
- "@angular/service-worker": "^21.0.0",
- "@angular/ssr": "^21.2.1",
- "karma": "^6.4.0",
- "less": "^4.2.0",
- "ng-packagr": "^21.0.0",
- "postcss": "^8.4.0",
- "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
- "tslib": "^2.3.0",
- "typescript": ">=5.9 <6.0",
- "vitest": "^4.0.8"
- },
- "peerDependenciesMeta": {
- "@angular/core": {
- "optional": true
- },
- "@angular/localize": {
- "optional": true
- },
- "@angular/platform-browser": {
- "optional": true
- },
- "@angular/platform-server": {
- "optional": true
- },
- "@angular/service-worker": {
- "optional": true
- },
- "@angular/ssr": {
- "optional": true
- },
- "karma": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "ng-packagr": {
- "optional": true
- },
- "postcss": {
- "optional": true
- },
- "tailwindcss": {
- "optional": true
- },
- "vitest": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/cli": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.1.tgz",
- "integrity": "sha512-5SRfMTgwFj1zXOpfeZWHsxZBni0J4Xz7/CbewG47D6DmbstOrSdgt6eNzJ62R650t0G9dpri2YvToZgImtbjOQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/architect": "0.2102.1",
- "@angular-devkit/core": "21.2.1",
- "@angular-devkit/schematics": "21.2.1",
- "@inquirer/prompts": "7.10.1",
- "@listr2/prompt-adapter-inquirer": "3.0.5",
- "@modelcontextprotocol/sdk": "1.26.0",
- "@schematics/angular": "21.2.1",
- "@yarnpkg/lockfile": "1.1.0",
- "algoliasearch": "5.48.1",
- "ini": "6.0.0",
- "jsonc-parser": "3.3.1",
- "listr2": "9.0.5",
- "npm-package-arg": "13.0.2",
- "pacote": "21.3.1",
- "parse5-html-rewriting-stream": "8.0.0",
- "semver": "7.7.4",
- "yargs": "18.0.0",
- "zod": "4.3.6"
- },
- "bin": {
- "ng": "bin/ng.js"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- }
- },
- "node_modules/@angular/common": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.1.tgz",
- "integrity": "sha512-xhv2i1Q9s1kpGbGsfj+o36+XUC/TQLcZyRuRxn3GwaN7Rv34FabC88ycpvoE+sW/txj4JRx9yPA0dRSZjwZ+Gg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/core": "21.2.1",
- "rxjs": "^6.5.3 || ^7.4.0"
- }
- },
- "node_modules/@angular/compiler": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.1.tgz",
- "integrity": "sha512-FxWaSaii1vfHIFA+JksqQ8NGB2frfqCrs7Ju50a44kbwR4fmanfn/VsiS/CbwBp9vcyT/Br9X/jAG4RuK/U2nw==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- }
- },
- "node_modules/@angular/compiler-cli": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.1.tgz",
- "integrity": "sha512-qYCWLGtEju4cDtYLi4ZzbwKoF0lcGs+Lc31kuESvAzYvWNgk2EUOtwWo8kbgpAzAwSYodtxW6Q90iWEwfU6elw==",
- "license": "MIT",
- "dependencies": {
- "@babel/core": "7.29.0",
- "@jridgewell/sourcemap-codec": "^1.4.14",
- "chokidar": "^5.0.0",
- "convert-source-map": "^1.5.1",
- "reflect-metadata": "^0.2.0",
- "semver": "^7.0.0",
- "tslib": "^2.3.0",
- "yargs": "^18.0.0"
- },
- "bin": {
- "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
- "ngc": "bundles/src/bin/ngc.js"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/compiler": "21.2.1",
- "typescript": ">=5.9 <6.1"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/core": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.1.tgz",
- "integrity": "sha512-pFTbg03s2ZI5cHNT+eWsGjwIIKiYkeAnodFbCAHjwFi9KCEYlTykFLjr9lcpGrBddfmAH7GE08Q73vgmsdcNHw==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/compiler": "21.2.1",
- "rxjs": "^6.5.3 || ^7.4.0",
- "zone.js": "~0.15.0 || ~0.16.0"
- },
- "peerDependenciesMeta": {
- "@angular/compiler": {
- "optional": true
- },
- "zone.js": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/fire": {
- "version": "21.0.0-rc.0-canary.ac3dd7c",
- "resolved": "https://registry.npmjs.org/@angular/fire/-/fire-21.0.0-rc.0-canary.ac3dd7c.tgz",
- "integrity": "sha512-Z1T9FrA8pd4JxUdQt1kDpsPGuXmqJSofsyiIZEJWboG4K1rezqN2JsB3cVGucIJ9uLljaFdvAtXYc+unBmAADQ==",
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/schematics": "^21.0.0",
- "@schematics/angular": "^21.0.0",
- "firebase": "^12.4.0",
- "rxfire": "^6.1.0",
- "tslib": "^2.3.0"
- },
- "peerDependencies": {
- "@angular/common": "^21.0.0",
- "@angular/core": "^21.0.0",
- "@angular/platform-browser": "^21.0.0",
- "@angular/platform-browser-dynamic": "^21.0.0",
- "@angular/platform-server": "^21.0.0",
- "firebase-tools": "^14.0.0",
- "rxjs": "~7.8.0"
- },
- "peerDependenciesMeta": {
- "@angular/platform-server": {
- "optional": true
- },
- "firebase-tools": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/ai": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-2.9.0.tgz",
- "integrity": "sha512-NPvBBuvdGo9x3esnABAucFYmqbBmXvyTMimBq2PCuLZbdANZoHzGlx7vfzbwNDaEtCBq4RGGNMliLIv6bZ+PtA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x",
- "@firebase/app-types": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/analytics": {
- "version": "0.10.20",
- "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.20.tgz",
- "integrity": "sha512-adGTNVUWH5q66tI/OQuKLSN6mamPpfYhj0radlH2xt+3eL6NFPtXoOs+ulvs+UsmK27vNFx5FjRDfWk+TyduHg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/installations": "0.6.20",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/analytics-compat": {
- "version": "0.2.26",
- "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.26.tgz",
- "integrity": "sha512-0j2ruLOoVSwwcXAF53AMoniJKnkwiTjGVfic5LDzqiRkR13vb5j6TXMeix787zbLeQtN/m1883Yv1TxI0gItbA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/analytics": "0.10.20",
- "@firebase/analytics-types": "0.8.3",
- "@firebase/component": "0.7.1",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/app": {
- "version": "0.14.9",
- "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.9.tgz",
- "integrity": "sha512-3gtUX0e584MYkKBQMgSECMvE1Dwzg+eONefDQ0wxVSe5YMBsZwdN5pL7UapwWBlV8+i8QCztF9TP947tEjZAGA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/app-check": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.11.1.tgz",
- "integrity": "sha512-gmKfwQ2k8aUQlOyRshc+fOQLq0OwUmibIZvpuY1RDNu2ho0aTMlwxOuEiJeYOs7AxzhSx7gnXPFNsXCFbnvXUQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/app-check-compat": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.4.1.tgz",
- "integrity": "sha512-yjSvSl5B1u4CirnxhzirN1uiTRCRfx+/qtfbyeyI+8Cx8Cw1RWAIO/OqytPSVwLYbJJ1vEC3EHfxazRaMoWKaA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-check": "0.11.1",
- "@firebase/app-check-types": "0.5.3",
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/app-compat": {
- "version": "0.5.9",
- "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.9.tgz",
- "integrity": "sha512-e5LzqjO69/N2z7XcJeuMzIp4wWnW696dQeaHAUpQvGk89gIWHAIvG6W+mA3UotGW6jBoqdppEJ9DnuwbcBByug==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app": "0.14.9",
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/auth": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.12.1.tgz",
- "integrity": "sha512-nXKj7d5bMBlnq6XpcQQpmnSVwEeHBkoVbY/+Wk0P1ebLSICoH4XPtvKOFlXKfIHmcS84mLQ99fk3njlDGKSDtw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x",
- "@react-native-async-storage/async-storage": "^2.2.0"
- },
- "peerDependenciesMeta": {
- "@react-native-async-storage/async-storage": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/auth-compat": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.6.3.tgz",
- "integrity": "sha512-nHOkupcYuGVxI1AJJ/OBhLPaRokbP14Gq4nkkoVvf1yvuREEWqdnrYB/CdsSnPxHMAnn5wJIKngxBF9jNX7s/Q==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/auth": "1.12.1",
- "@firebase/auth-types": "0.13.0",
- "@firebase/component": "0.7.1",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/component": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.1.tgz",
- "integrity": "sha512-mFzsm7CLHR60o08S23iLUY8m/i6kLpOK87wdEFPLhdlCahaxKmWOwSVGiWoENYSmFJJoDhrR3gKSCxz7ENdIww==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/data-connect": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.4.0.tgz",
- "integrity": "sha512-vLXM6WHNIR3VtEeYNUb/5GTsUOyl3Of4iWNZHBe1i9f88sYFnxybJNWVBjvJ7flhCyF8UdxGpzWcUnv6F5vGfg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/database": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.1.tgz",
- "integrity": "sha512-LwIXe8+mVHY5LBPulWECOOIEXDiatyECp/BOlu0gOhe+WOcKjWHROaCbLlkFTgHMY7RHr5MOxkLP/tltWAH3dA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "faye-websocket": "0.11.4",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/database-compat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.1.tgz",
- "integrity": "sha512-heAEVZ9Z8c8PnBUcmGh91JHX0cXcVa1yESW/xkLuwaX7idRFyLiN8sl73KXpR8ZArGoPXVQDanBnk6SQiekRCQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/database": "1.1.1",
- "@firebase/database-types": "1.0.17",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/database-types": {
- "version": "1.0.17",
- "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.17.tgz",
- "integrity": "sha512-4eWaM5fW3qEIHjGzfi3cf0Jpqi1xQsAdT6rSDE1RZPrWu8oGjgrq6ybMjobtyHQFgwGCykBm4YM89qDzc+uG/w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-types": "0.9.3",
- "@firebase/util": "1.14.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/firestore": {
- "version": "4.12.0",
- "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.12.0.tgz",
- "integrity": "sha512-PM47OyiiAAoAMB8kkq4Je14mTciaRoAPDd3ng3Ckqz9i2TX9D9LfxIRcNzP/OxzNV4uBKRq6lXoOggkJBQR3Gw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "@firebase/webchannel-wrapper": "1.0.5",
- "@grpc/grpc-js": "~1.9.0",
- "@grpc/proto-loader": "^0.7.8",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/firestore-compat": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.4.6.tgz",
- "integrity": "sha512-NgVyR4hHHN2FvSNQOtbgBOuVsEdD/in30d9FKbEvvITiAChrBN2nBstmhfjI4EOTnHaP8zigwvkNYFI9yKGAkQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/firestore": "4.12.0",
- "@firebase/firestore-types": "3.0.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/functions": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.13.2.tgz",
- "integrity": "sha512-tHduUD+DeokM3NB1QbHCvEMoL16e8Z8JSkmuVA4ROoJKPxHn8ibnecHPO2e3nVCJR1D9OjuKvxz4gksfq92/ZQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.7.1",
- "@firebase/messaging-interop-types": "0.2.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/functions-compat": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.4.2.tgz",
- "integrity": "sha512-YNxgnezvZDkqxqXa6cT7/oTeD4WXbxgIP7qZp4LFnathQv5o2omM6EoIhXiT9Ie5AoQDcIhG9Y3/dj+DFJGaGQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/functions": "0.13.2",
- "@firebase/functions-types": "0.6.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/installations": {
- "version": "0.6.20",
- "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.20.tgz",
- "integrity": "sha512-LOzvR7XHPbhS0YB5ANXhqXB5qZlntPpwU/4KFwhSNpXNsGk/sBQ9g5hepi0y0/MfenJLe2v7t644iGOOElQaHQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/util": "1.14.0",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/installations-compat": {
- "version": "0.2.20",
- "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.20.tgz",
- "integrity": "sha512-9C9pL/DIEGucmoPj8PlZTnztbX3nhNj5RTYVpUM7wQq/UlHywaYv99969JU/WHLvi9ptzIogXYS9d1eZ6XFe9g==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/installations": "0.6.20",
- "@firebase/installations-types": "0.5.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/logger": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz",
- "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/messaging": {
- "version": "0.12.24",
- "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.24.tgz",
- "integrity": "sha512-UtKoubegAhHyehcB7iQjvQ8OVITThPbbWk3g2/2ze42PrQr6oe6OmCElYQkBrE5RDCeMTNucXejbdulrQ2XwVg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/installations": "0.6.20",
- "@firebase/messaging-interop-types": "0.2.3",
- "@firebase/util": "1.14.0",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/messaging-compat": {
- "version": "0.2.24",
- "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.24.tgz",
- "integrity": "sha512-wXH8FrKbJvFuFe6v98TBhAtvgknxKIZtGM/wCVsfpOGmaAE80bD8tBxztl+uochjnFb9plihkd6mC4y7sZXSpA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/messaging": "0.12.24",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/performance": {
- "version": "0.7.10",
- "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.10.tgz",
- "integrity": "sha512-8nRFld+Ntzp5cLKzZuG9g+kBaSn8Ks9dmn87UQGNFDygbmR6ebd8WawauEXiJjMj1n70ypkvAOdE+lzeyfXtGA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/installations": "0.6.20",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0",
- "web-vitals": "^4.2.4"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/performance-compat": {
- "version": "0.2.23",
- "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.23.tgz",
- "integrity": "sha512-c7qOAGBUAOpIuUlHu1axWcrCVtIYKPMhH0lMnoCDWnPwn1HcPuPUBVTWETbC7UWw71RMJF8DpirfWXzMWJQfgA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/performance": "0.7.10",
- "@firebase/performance-types": "0.2.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/remote-config": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.8.1.tgz",
- "integrity": "sha512-L86TReBnPiiJOWd7k9iaiE9f7rHtMpjAoYN0fH2ey2ZRzsOChHV0s5sYf1+IIUYzplzsE46pjlmAUNkRRKwHSQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/installations": "0.6.20",
- "@firebase/logger": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/remote-config-compat": {
- "version": "0.2.22",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.22.tgz",
- "integrity": "sha512-uW/eNKKtRBot2gnCC5mnoy5Voo2wMzZuQ7dwqqGHU176fO9zFgMwKiRzk+aaC99NLrFk1KOmr0ZVheD+zdJmjQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/logger": "0.5.0",
- "@firebase/remote-config": "0.8.1",
- "@firebase/remote-config-types": "0.5.0",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/remote-config-types": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.5.0.tgz",
- "integrity": "sha512-vI3bqLoF14L/GchtgayMiFpZJF+Ao3uR8WCde0XpYNkSokDpAKca2DxvcfeZv7lZUqkUwQPL2wD83d3vQ4vvrg==",
- "license": "Apache-2.0"
- },
- "node_modules/@angular/fire/node_modules/@firebase/storage": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.14.1.tgz",
- "integrity": "sha512-uIpYgBBsv1vIET+5xV20XT7wwqV+H4GFp6PBzfmLUcEgguS4SWNFof56Z3uOC2lNDh0KDda1UflYq2VwD9Nefw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/storage-compat": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.4.1.tgz",
- "integrity": "sha512-bgl3FHHfXAmBgzIK/Fps6Xyv2HiAQlSTov07CBL+RGGhrC5YIk4lruS8JVIC+UkujRdYvnf8cpQFGn2RCilJ/A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/component": "0.7.1",
- "@firebase/storage": "0.14.1",
- "@firebase/storage-types": "0.8.3",
- "@firebase/util": "1.14.0",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/util": {
- "version": "1.14.0",
- "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.14.0.tgz",
- "integrity": "sha512-/gnejm7MKkVIXnSJGpc9L2CvvvzJvtDPeAEq5jAwgVlf/PeNxot+THx/bpD20wQ8uL5sz0xqgXy1nisOYMU+mw==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/@angular/fire/node_modules/@firebase/webchannel-wrapper": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.5.tgz",
- "integrity": "sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw==",
- "license": "Apache-2.0"
- },
- "node_modules/@angular/fire/node_modules/firebase": {
- "version": "12.10.0",
- "resolved": "https://registry.npmjs.org/firebase/-/firebase-12.10.0.tgz",
- "integrity": "sha512-tAjHnEirksqWpa+NKDUSUMjulOnsTcsPC1X1rQ+gwPtjlhJS572na91CwaBXQJHXharIrfj7sw/okDkXOsphjA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@firebase/ai": "2.9.0",
- "@firebase/analytics": "0.10.20",
- "@firebase/analytics-compat": "0.2.26",
- "@firebase/app": "0.14.9",
- "@firebase/app-check": "0.11.1",
- "@firebase/app-check-compat": "0.4.1",
- "@firebase/app-compat": "0.5.9",
- "@firebase/app-types": "0.9.3",
- "@firebase/auth": "1.12.1",
- "@firebase/auth-compat": "0.6.3",
- "@firebase/data-connect": "0.4.0",
- "@firebase/database": "1.1.1",
- "@firebase/database-compat": "2.1.1",
- "@firebase/firestore": "4.12.0",
- "@firebase/firestore-compat": "0.4.6",
- "@firebase/functions": "0.13.2",
- "@firebase/functions-compat": "0.4.2",
- "@firebase/installations": "0.6.20",
- "@firebase/installations-compat": "0.2.20",
- "@firebase/messaging": "0.12.24",
- "@firebase/messaging-compat": "0.2.24",
- "@firebase/performance": "0.7.10",
- "@firebase/performance-compat": "0.2.23",
- "@firebase/remote-config": "0.8.1",
- "@firebase/remote-config-compat": "0.2.22",
- "@firebase/storage": "0.14.1",
- "@firebase/storage-compat": "0.4.1",
- "@firebase/util": "1.14.0"
- }
- },
- "node_modules/@angular/forms": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.2.1.tgz",
- "integrity": "sha512-6aqOPk9xoa0dfeUDeEbhaiPhmt6MQrdn59qbGAomn9RMXA925TrHbJhSIkp9tXc2Fr4aJRi8zkD/cdXEc1IYeA==",
- "license": "MIT",
- "dependencies": {
- "@standard-schema/spec": "^1.0.0",
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/common": "21.2.1",
- "@angular/core": "21.2.1",
- "@angular/platform-browser": "21.2.1",
- "rxjs": "^6.5.3 || ^7.4.0"
- }
- },
- "node_modules/@angular/language-service": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.2.1.tgz",
- "integrity": "sha512-L8EaNhWDKMny18RURg/Ju2Dix2e7qLL/s2yDQrawgjQRmXAMnjimz10w/EiiG7FMK/Hj5fLycS5X8VITq1f2rg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- }
- },
- "node_modules/@angular/localize": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-21.2.1.tgz",
- "integrity": "sha512-2QsN33fLO3N/RRFfUxDKHMX/Y/2TH90Tx51Wi6hi1do9IJdlfEe1qBw+5F0g1F1CuFEYgZWMJdZIK7LPHpuDzw==",
- "license": "MIT",
- "dependencies": {
- "@babel/core": "7.29.0",
- "@types/babel__core": "7.20.5",
- "tinyglobby": "^0.2.12",
- "yargs": "^18.0.0"
- },
- "bin": {
- "localize-extract": "tools/bundles/src/extract/cli.js",
- "localize-migrate": "tools/bundles/src/migrate/cli.js",
- "localize-translate": "tools/bundles/src/translate/cli.js"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/compiler": "21.2.1",
- "@angular/compiler-cli": "21.2.1"
- }
- },
- "node_modules/@angular/platform-browser": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.1.tgz",
- "integrity": "sha512-k4SJLxIaLT26vLjLuFL+ho0BiG5PrdxEsjsXFC7w5iUhomeouzkHVTZ4t7gaLNKrdRD7QNtU4Faw0nL0yx0ZPQ==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/animations": "21.2.1",
- "@angular/common": "21.2.1",
- "@angular/core": "21.2.1"
- },
- "peerDependenciesMeta": {
- "@angular/animations": {
- "optional": true
- }
- }
- },
- "node_modules/@angular/platform-browser-dynamic": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.2.1.tgz",
- "integrity": "sha512-J4KnrXjgSuk7KjEm79/RK1yyzR867sIyT5mcG6jx2KmkjspFJd4OeOux7Oj7lSBM7+nDEsKC9F6s0x3dC0hCPQ==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/common": "21.2.1",
- "@angular/compiler": "21.2.1",
- "@angular/core": "21.2.1",
- "@angular/platform-browser": "21.2.1"
- }
- },
- "node_modules/@angular/platform-server": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-21.2.1.tgz",
- "integrity": "sha512-ezIY3hKl98JIjQcy+wq+38w9ln2bplQ8HFpEsaYySjPPu5TYCQ/z8HFxpvgqK80SVfvE7ijbgY+ALzKCE30Fjg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0",
- "xhr2": "^0.2.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/common": "21.2.1",
- "@angular/compiler": "21.2.1",
- "@angular/core": "21.2.1",
- "@angular/platform-browser": "21.2.1",
- "rxjs": "^6.5.3 || ^7.4.0"
- }
- },
- "node_modules/@angular/router": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.1.tgz",
- "integrity": "sha512-FUKG+8ImQYxmlDUdAs7+VeS/VrBNrbo0zGiKkzVNU/bbcCyroKXJLXFtkFI3qmROiJNyIta2IMBCHJvIjLIMig==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.3.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
- },
- "peerDependencies": {
- "@angular/common": "21.2.1",
- "@angular/core": "21.2.1",
- "@angular/platform-browser": "21.2.1",
- "rxjs": "^6.5.3 || ^7.4.0"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
- "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
- "license": "MIT",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.28.5",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.1.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/compat-data": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
- "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
- "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-module-transforms": "^7.28.6",
- "@babel/helpers": "^7.28.6",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/traverse": "^7.29.0",
- "@babel/types": "^7.29.0",
- "@jridgewell/remapping": "^2.3.5",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/core/node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "license": "MIT"
- },
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.29.1",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
- "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.29.0",
- "@babel/types": "^7.29.0",
- "@jridgewell/gen-mapping": "^0.3.12",
- "@jridgewell/trace-mapping": "^0.3.28",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.27.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
- "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.27.3"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
- "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.28.6",
- "@babel/helper-validator-option": "^7.27.1",
- "browserslist": "^4.24.0",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
- "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
- "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.28.6",
- "@babel/helper-validator-identifier": "^7.28.5",
- "@babel/traverse": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-split-export-declaration": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
- "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.24.7"
- },
- "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.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
- "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
- "license": "MIT",
- "dependencies": {
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/parser": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
- "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.29.0"
- },
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
- "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.28.6",
- "@babel/parser": "^7.28.6",
- "@babel/types": "^7.28.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
- "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.29.0",
- "debug": "^4.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
- "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
- "license": "MIT",
- "dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@colors/colors": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
- "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
- "node_modules/@cspotcode/source-map-consumer": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
- "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
- "license": "BSD-3-Clause",
- "optional": true,
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/@cspotcode/source-map-support": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
- "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@cspotcode/source-map-consumer": "0.8.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@emnapi/core": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
- "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.1.0",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@emnapi/runtime": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
- "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@emnapi/wasi-threads": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
- "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
- "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
- "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
- "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
- "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
- "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
- "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
- "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
- "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
- "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
- "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
- "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
- "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
- "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
- "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
- "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
- "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
- "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
- "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
- "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
- "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
- "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
- "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
- "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
- "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
- "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.9.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
- "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
- "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.2",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
- "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
- "node_modules/@eslint/config-array": {
- "version": "0.23.3",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz",
- "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/object-schema": "^3.0.3",
- "debug": "^4.3.1",
- "minimatch": "^10.2.4"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- }
- },
- "node_modules/@eslint/config-helpers": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz",
- "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^1.1.1"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- }
- },
- "node_modules/@eslint/core": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz",
- "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz",
- "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz",
- "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^1.1.1",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- }
- },
- "node_modules/@firebase/ai": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz",
- "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x",
- "@firebase/app-types": "0.x"
- }
- },
- "node_modules/@firebase/analytics": {
- "version": "0.10.17",
- "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz",
- "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/installations": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/analytics-compat": {
- "version": "0.2.23",
- "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz",
- "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/analytics": "0.10.17",
- "@firebase/analytics-types": "0.8.3",
- "@firebase/component": "0.6.18",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/analytics-types": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz",
- "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/app": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz",
- "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/app-check": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz",
- "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/app-check-compat": {
- "version": "0.3.26",
- "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz",
- "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app-check": "0.10.1",
- "@firebase/app-check-types": "0.5.3",
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/app-check-interop-types": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz",
- "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/app-check-types": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz",
- "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/app-compat": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz",
- "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app": "0.13.2",
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/app-types": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
- "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/auth": {
- "version": "1.10.8",
- "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz",
- "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x",
- "@react-native-async-storage/async-storage": "^1.18.1"
- },
- "peerDependenciesMeta": {
- "@react-native-async-storage/async-storage": {
- "optional": true
- }
- }
- },
- "node_modules/@firebase/auth-compat": {
- "version": "0.5.28",
- "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz",
- "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/auth": "1.10.8",
- "@firebase/auth-types": "0.13.0",
- "@firebase/component": "0.6.18",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/auth-interop-types": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz",
- "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/auth-types": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz",
- "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "@firebase/app-types": "0.x",
- "@firebase/util": "1.x"
- }
- },
- "node_modules/@firebase/component": {
- "version": "0.6.18",
- "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz",
- "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/data-connect": {
- "version": "0.3.10",
- "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz",
- "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/database": {
- "version": "1.0.20",
- "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz",
- "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "faye-websocket": "0.11.4",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/database-compat": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz",
- "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/database": "1.0.20",
- "@firebase/database-types": "1.0.15",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/database-types": {
- "version": "1.0.15",
- "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz",
- "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app-types": "0.9.3",
- "@firebase/util": "1.12.1"
- }
- },
- "node_modules/@firebase/firestore": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz",
- "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "@firebase/webchannel-wrapper": "1.0.3",
- "@grpc/grpc-js": "~1.9.0",
- "@grpc/proto-loader": "^0.7.8",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/firestore-compat": {
- "version": "0.3.53",
- "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz",
- "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/firestore": "4.8.0",
- "@firebase/firestore-types": "3.0.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/firestore-types": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz",
- "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "@firebase/app-types": "0.x",
- "@firebase/util": "1.x"
- }
- },
- "node_modules/@firebase/functions": {
- "version": "0.12.9",
- "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz",
- "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/app-check-interop-types": "0.3.3",
- "@firebase/auth-interop-types": "0.2.4",
- "@firebase/component": "0.6.18",
- "@firebase/messaging-interop-types": "0.2.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/functions-compat": {
- "version": "0.3.26",
- "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz",
- "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/functions": "0.12.9",
- "@firebase/functions-types": "0.6.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/functions-types": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz",
- "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/installations": {
- "version": "0.6.18",
- "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz",
- "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/util": "1.12.1",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/installations-compat": {
- "version": "0.2.18",
- "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz",
- "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/installations": "0.6.18",
- "@firebase/installations-types": "0.5.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/installations-types": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz",
- "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "@firebase/app-types": "0.x"
- }
- },
- "node_modules/@firebase/logger": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz",
- "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/messaging": {
- "version": "0.12.22",
- "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz",
- "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/installations": "0.6.18",
- "@firebase/messaging-interop-types": "0.2.3",
- "@firebase/util": "1.12.1",
- "idb": "7.1.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/messaging-compat": {
- "version": "0.2.22",
- "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz",
- "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/messaging": "0.12.22",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/messaging-interop-types": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz",
- "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/performance": {
- "version": "0.7.7",
- "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz",
- "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/installations": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0",
- "web-vitals": "^4.2.4"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/performance-compat": {
- "version": "0.2.20",
- "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz",
- "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/performance": "0.7.7",
- "@firebase/performance-types": "0.2.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/performance-types": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz",
- "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==",
- "license": "Apache-2.0"
- },
- "node_modules/@firebase/remote-config": {
- "version": "0.6.5",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz",
- "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/installations": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/remote-config-compat": {
- "version": "0.2.18",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz",
- "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/logger": "0.4.4",
- "@firebase/remote-config": "0.6.5",
- "@firebase/remote-config-types": "0.4.0",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/remote-config-types": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz",
- "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==",
- "license": "Apache-2.0",
- "peer": true
- },
- "node_modules/@firebase/storage": {
- "version": "0.13.14",
- "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz",
- "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app": "0.x"
- }
- },
- "node_modules/@firebase/storage-compat": {
- "version": "0.3.24",
- "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz",
- "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/component": "0.6.18",
- "@firebase/storage": "0.13.14",
- "@firebase/storage-types": "0.8.3",
- "@firebase/util": "1.12.1",
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "@firebase/app-compat": "0.x"
- }
- },
- "node_modules/@firebase/storage-types": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz",
- "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "@firebase/app-types": "0.x",
- "@firebase/util": "1.x"
- }
- },
- "node_modules/@firebase/util": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz",
- "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "tslib": "^2.1.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@firebase/webchannel-wrapper": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz",
- "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==",
- "license": "Apache-2.0",
- "peer": true
- },
- "node_modules/@gar/promise-retry": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.2.tgz",
- "integrity": "sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "retry": "^0.13.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@grpc/grpc-js": {
- "version": "1.9.15",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
- "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@grpc/proto-loader": "^0.7.8",
- "@types/node": ">=12.12.47"
- },
- "engines": {
- "node": "^8.13.0 || >=10.10.0"
- }
- },
- "node_modules/@grpc/proto-loader": {
- "version": "0.7.15",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
- "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "lodash.camelcase": "^4.3.0",
- "long": "^5.0.0",
- "protobufjs": "^7.2.5",
- "yargs": "^17.7.2"
- },
- "bin": {
- "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@grpc/proto-loader/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==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@grpc/proto-loader/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==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@grpc/proto-loader/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==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "license": "MIT",
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@grpc/proto-loader/node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@harperfast/extended-iterable": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz",
- "integrity": "sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==",
- "dev": true,
- "license": "Apache-2.0",
- "optional": true
- },
- "node_modules/@hono/node-server": {
- "version": "1.19.11",
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz",
- "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.14.1"
- },
- "peerDependencies": {
- "hono": "^4"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.7",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
- "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.4.0"
- },
- "engines": {
- "node": ">=18.18.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/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@inquirer/ansi": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
- "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@inquirer/checkbox": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
- "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/ansi": "^1.0.2",
- "@inquirer/core": "^10.3.2",
- "@inquirer/figures": "^1.0.15",
- "@inquirer/type": "^3.0.10",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/confirm": {
- "version": "5.1.21",
- "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz",
- "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/core": {
- "version": "10.3.2",
- "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz",
- "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/ansi": "^1.0.2",
- "@inquirer/figures": "^1.0.15",
- "@inquirer/type": "^3.0.10",
- "cli-width": "^4.1.0",
- "mute-stream": "^2.0.0",
- "signal-exit": "^4.1.0",
- "wrap-ansi": "^6.2.0",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/editor": {
- "version": "4.2.23",
- "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz",
- "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/external-editor": "^1.0.3",
- "@inquirer/type": "^3.0.10"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/expand": {
- "version": "4.0.23",
- "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz",
- "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/external-editor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
- "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chardet": "^2.1.1",
- "iconv-lite": "^0.7.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/figures": {
- "version": "1.0.15",
- "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz",
- "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@inquirer/input": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz",
- "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/number": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz",
- "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/password": {
- "version": "4.0.23",
- "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz",
- "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/ansi": "^1.0.2",
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/prompts": {
- "version": "7.10.1",
- "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz",
- "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/checkbox": "^4.3.2",
- "@inquirer/confirm": "^5.1.21",
- "@inquirer/editor": "^4.2.23",
- "@inquirer/expand": "^4.0.23",
- "@inquirer/input": "^4.3.1",
- "@inquirer/number": "^3.0.23",
- "@inquirer/password": "^4.0.23",
- "@inquirer/rawlist": "^4.1.11",
- "@inquirer/search": "^3.2.2",
- "@inquirer/select": "^4.4.2"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/rawlist": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
- "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/type": "^3.0.10",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/search": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz",
- "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^10.3.2",
- "@inquirer/figures": "^1.0.15",
- "@inquirer/type": "^3.0.10",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/select": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz",
- "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/ansi": "^1.0.2",
- "@inquirer/core": "^10.3.2",
- "@inquirer/figures": "^1.0.15",
- "@inquirer/type": "^3.0.10",
- "yoctocolors-cjs": "^2.1.3"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/type": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz",
- "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@isaacs/fs-minipass": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
- "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.4"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jasminejs/reporters": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@jasminejs/reporters/-/reporters-1.0.0.tgz",
- "integrity": "sha512-rM3GG4vx2H1Gp5kYCTr9aKlOEJFd43pzpiMAiy5b1+FUc2ub4e6bS6yCi/WQNDzAa5MVp9++dwcoEtcIfoEnhA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/remapping": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
- "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.31",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
- "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@listr2/prompt-adapter-inquirer": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz",
- "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/type": "^3.0.8"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@inquirer/prompts": ">= 3 < 8",
- "listr2": "9.0.5"
- }
- },
- "node_modules/@lmdb/lmdb-darwin-arm64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.1.tgz",
- "integrity": "sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@lmdb/lmdb-darwin-x64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.1.tgz",
- "integrity": "sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@lmdb/lmdb-linux-arm": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.1.tgz",
- "integrity": "sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@lmdb/lmdb-linux-arm64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.1.tgz",
- "integrity": "sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@lmdb/lmdb-linux-x64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.1.tgz",
- "integrity": "sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@lmdb/lmdb-win32-arm64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.1.tgz",
- "integrity": "sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@lmdb/lmdb-win32-x64": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.1.tgz",
- "integrity": "sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@modelcontextprotocol/sdk": {
- "version": "1.26.0",
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz",
- "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@hono/node-server": "^1.19.9",
- "ajv": "^8.17.1",
- "ajv-formats": "^3.0.1",
- "content-type": "^1.0.5",
- "cors": "^2.8.5",
- "cross-spawn": "^7.0.5",
- "eventsource": "^3.0.2",
- "eventsource-parser": "^3.0.0",
- "express": "^5.2.1",
- "express-rate-limit": "^8.2.1",
- "hono": "^4.11.4",
- "jose": "^6.1.3",
- "json-schema-typed": "^8.0.2",
- "pkce-challenge": "^5.0.0",
- "raw-body": "^3.0.0",
- "zod": "^3.25 || ^4.0",
- "zod-to-json-schema": "^3.25.1"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@cfworker/json-schema": "^4.1.1",
- "zod": "^3.25 || ^4.0"
- },
- "peerDependenciesMeta": {
- "@cfworker/json-schema": {
- "optional": true
- },
- "zod": {
- "optional": false
- }
- }
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
- "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
- "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
- "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
- "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
- "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
- "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@napi-rs/nice": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz",
- "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 10"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Brooooooklyn"
- },
- "optionalDependencies": {
- "@napi-rs/nice-android-arm-eabi": "1.1.1",
- "@napi-rs/nice-android-arm64": "1.1.1",
- "@napi-rs/nice-darwin-arm64": "1.1.1",
- "@napi-rs/nice-darwin-x64": "1.1.1",
- "@napi-rs/nice-freebsd-x64": "1.1.1",
- "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1",
- "@napi-rs/nice-linux-arm64-gnu": "1.1.1",
- "@napi-rs/nice-linux-arm64-musl": "1.1.1",
- "@napi-rs/nice-linux-ppc64-gnu": "1.1.1",
- "@napi-rs/nice-linux-riscv64-gnu": "1.1.1",
- "@napi-rs/nice-linux-s390x-gnu": "1.1.1",
- "@napi-rs/nice-linux-x64-gnu": "1.1.1",
- "@napi-rs/nice-linux-x64-musl": "1.1.1",
- "@napi-rs/nice-openharmony-arm64": "1.1.1",
- "@napi-rs/nice-win32-arm64-msvc": "1.1.1",
- "@napi-rs/nice-win32-ia32-msvc": "1.1.1",
- "@napi-rs/nice-win32-x64-msvc": "1.1.1"
- }
- },
- "node_modules/@napi-rs/nice-android-arm-eabi": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz",
- "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-android-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz",
- "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-darwin-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz",
- "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-darwin-x64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz",
- "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-freebsd-x64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz",
- "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-arm-gnueabihf": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz",
- "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-arm64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz",
- "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-arm64-musl": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz",
- "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-ppc64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz",
- "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-riscv64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz",
- "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-s390x-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz",
- "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-x64-gnu": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz",
- "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-linux-x64-musl": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz",
- "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-openharmony-arm64": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz",
- "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-win32-arm64-msvc": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz",
- "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-win32-ia32-msvc": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz",
- "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/nice-win32-x64-msvc": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz",
- "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@napi-rs/wasm-runtime": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
- "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/core": "^1.7.1",
- "@emnapi/runtime": "^1.7.1",
- "@tybys/wasm-util": "^0.10.1"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Brooooooklyn"
- }
- },
- "node_modules/@npmcli/agent": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz",
- "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "agent-base": "^7.1.0",
- "http-proxy-agent": "^7.0.0",
- "https-proxy-agent": "^7.0.1",
- "lru-cache": "^11.2.1",
- "socks-proxy-agent": "^8.0.3"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/agent/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/@npmcli/fs": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz",
- "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "semver": "^7.3.5"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/git": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz",
- "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@gar/promise-retry": "^1.0.0",
- "@npmcli/promise-spawn": "^9.0.0",
- "ini": "^6.0.0",
- "lru-cache": "^11.2.1",
- "npm-pick-manifest": "^11.0.1",
- "proc-log": "^6.0.0",
- "semver": "^7.3.5",
- "which": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/git/node_modules/isexe": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
- "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/@npmcli/git/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/@npmcli/git/node_modules/which": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
- "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^4.0.0"
- },
- "bin": {
- "node-which": "bin/which.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/installed-package-contents": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz",
- "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "npm-bundled": "^5.0.0",
- "npm-normalize-package-bin": "^5.0.0"
- },
- "bin": {
- "installed-package-contents": "bin/index.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/node-gyp": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz",
- "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/package-json": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz",
- "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/git": "^7.0.0",
- "glob": "^13.0.0",
- "hosted-git-info": "^9.0.0",
- "json-parse-even-better-errors": "^5.0.0",
- "proc-log": "^6.0.0",
- "semver": "^7.5.3",
- "spdx-expression-parse": "^4.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/promise-spawn": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz",
- "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "which": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/promise-spawn/node_modules/isexe": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
- "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/@npmcli/promise-spawn/node_modules/which": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
- "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^4.0.0"
- },
- "bin": {
- "node-which": "bin/which.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/redact": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz",
- "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@npmcli/run-script": {
- "version": "10.0.4",
- "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz",
- "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/node-gyp": "^5.0.0",
- "@npmcli/package-json": "^7.0.0",
- "@npmcli/promise-spawn": "^9.0.0",
- "node-gyp": "^12.1.0",
- "proc-log": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@oxc-project/types": {
- "version": "0.113.0",
- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.113.0.tgz",
- "integrity": "sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
- }
- },
- "node_modules/@parcel/watcher": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
- "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "detect-libc": "^2.0.3",
- "is-glob": "^4.0.3",
- "node-addon-api": "^7.0.0",
- "picomatch": "^4.0.3"
- },
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- },
- "optionalDependencies": {
- "@parcel/watcher-android-arm64": "2.5.6",
- "@parcel/watcher-darwin-arm64": "2.5.6",
- "@parcel/watcher-darwin-x64": "2.5.6",
- "@parcel/watcher-freebsd-x64": "2.5.6",
- "@parcel/watcher-linux-arm-glibc": "2.5.6",
- "@parcel/watcher-linux-arm-musl": "2.5.6",
- "@parcel/watcher-linux-arm64-glibc": "2.5.6",
- "@parcel/watcher-linux-arm64-musl": "2.5.6",
- "@parcel/watcher-linux-x64-glibc": "2.5.6",
- "@parcel/watcher-linux-x64-musl": "2.5.6",
- "@parcel/watcher-win32-arm64": "2.5.6",
- "@parcel/watcher-win32-ia32": "2.5.6",
- "@parcel/watcher-win32-x64": "2.5.6"
- }
- },
- "node_modules/@parcel/watcher-android-arm64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
- "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-darwin-arm64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
- "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-darwin-x64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
- "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-freebsd-x64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
- "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-arm-glibc": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
- "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-arm-musl": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
- "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-arm64-glibc": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
- "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-arm64-musl": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
- "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-x64-glibc": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
- "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-linux-x64-musl": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
- "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-win32-arm64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
- "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-win32-ia32": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
- "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher-win32-x64": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
- "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/@parcel/watcher/node_modules/node-addon-api": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
- "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
- "node_modules/@protobufjs/aspromise": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
- "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/base64": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
- "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/codegen": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
- "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/eventemitter": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
- "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/fetch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
- "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.1",
- "@protobufjs/inquire": "^1.1.0"
- }
- },
- "node_modules/@protobufjs/float": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
- "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/inquire": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
- "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/path": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
- "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/pool": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
- "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@protobufjs/utf8": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
- "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
- "license": "BSD-3-Clause"
- },
- "node_modules/@rolldown/binding-android-arm64": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz",
- "integrity": "sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-darwin-arm64": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.4.tgz",
- "integrity": "sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-darwin-x64": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.4.tgz",
- "integrity": "sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-freebsd-x64": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.4.tgz",
- "integrity": "sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.4.tgz",
- "integrity": "sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-linux-arm64-gnu": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.4.tgz",
- "integrity": "sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-linux-arm64-musl": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.4.tgz",
- "integrity": "sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-linux-x64-gnu": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.4.tgz",
- "integrity": "sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-linux-x64-musl": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.4.tgz",
- "integrity": "sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-openharmony-arm64": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.4.tgz",
- "integrity": "sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-wasm32-wasi": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.4.tgz",
- "integrity": "sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==",
- "cpu": [
- "wasm32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@napi-rs/wasm-runtime": "^1.1.1"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@rolldown/binding-win32-arm64-msvc": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.4.tgz",
- "integrity": "sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/binding-win32-x64-msvc": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.4.tgz",
- "integrity": "sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
- },
- "node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz",
- "integrity": "sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
- "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
- "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
- "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
- "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
- "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
- "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
- "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
- "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
- "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
- "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
- "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
- "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
- "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
- "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
- "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
- "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
- "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
- "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
- "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ]
- },
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
- "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
- "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
- "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
- "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
- "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@schematics/angular": {
- "version": "21.2.1",
- "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.1.tgz",
- "integrity": "sha512-DjrHRMoILhbZ6tc7aNZWuHA1wCm1iU/JN1TxAwNEyIBgyU3Fx8Z5baK4w0TCpOIPt0RLWVgP2L7kka9aXWCUFA==",
- "license": "MIT",
- "dependencies": {
- "@angular-devkit/core": "21.2.1",
- "@angular-devkit/schematics": "21.2.1",
- "jsonc-parser": "3.3.1"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
- "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
- "yarn": ">= 1.13.0"
- }
- },
- "node_modules/@sigstore/bundle": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz",
- "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@sigstore/protobuf-specs": "^0.5.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@sigstore/core": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.1.0.tgz",
- "integrity": "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@sigstore/protobuf-specs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz",
- "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@sigstore/sign": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.0.tgz",
- "integrity": "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@sigstore/bundle": "^4.0.0",
- "@sigstore/core": "^3.1.0",
- "@sigstore/protobuf-specs": "^0.5.0",
- "make-fetch-happen": "^15.0.3",
- "proc-log": "^6.1.0",
- "promise-retry": "^2.0.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@sigstore/tuf": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.1.tgz",
- "integrity": "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@sigstore/protobuf-specs": "^0.5.0",
- "tuf-js": "^4.1.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@sigstore/verify": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz",
- "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@sigstore/bundle": "^4.0.0",
- "@sigstore/core": "^3.1.0",
- "@sigstore/protobuf-specs": "^0.5.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@socket.io/component-emitter": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
- "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@standard-schema/spec": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
- "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
- "license": "MIT"
- },
- "node_modules/@tsconfig/node10": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
- "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@tsconfig/node12": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
- "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@tsconfig/node14": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
- "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@tsconfig/node16": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
- "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/@tufjs/canonical-json": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
- "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^16.14.0 || >=18.0.0"
- }
- },
- "node_modules/@tufjs/models": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz",
- "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@tufjs/canonical-json": "2.0.0",
- "minimatch": "^10.1.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/@tybys/wasm-util": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
- "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@types/babel__core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
- "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "node_modules/@types/babel__generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
- "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__template": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
- "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
- "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.28.2"
- }
- },
- "node_modules/@types/cors": {
- "version": "2.8.19",
- "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
- "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/esrecurse": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
- "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
- "dev": true,
- "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/jasmine": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-6.0.0.tgz",
- "integrity": "sha512-18lgGsLmEh3VJk9eZ5wAjTISxdqzl6YOwu8UdMpolajN57QOCNbl+AbHUd+Yu9ItrsFdB+c8LSZSGNg8nHaguw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/node": {
- "version": "22.19.15",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",
- "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==",
- "license": "MIT",
- "dependencies": {
- "undici-types": "~6.21.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
- "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.56.1",
- "@typescript-eslint/types": "^8.56.1",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
- "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
- "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
- "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
- "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@typescript-eslint/project-service": "8.56.1",
- "@typescript-eslint/tsconfig-utils": "8.56.1",
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1",
- "debug": "^4.4.3",
- "minimatch": "^10.2.2",
- "semver": "^7.7.3",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.4.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
- "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.56.1",
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/typescript-estree": "8.56.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
- "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "eslint-visitor-keys": "^5.0.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
- "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
- "license": "Apache-2.0",
- "peer": true,
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@vitejs/plugin-basic-ssl": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz",
- "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
- },
- "peerDependencies": {
- "vite": "^6.0.0 || ^7.0.0"
- }
- },
- "node_modules/@yarnpkg/lockfile": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
- "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/abbrev": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
- "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/accepts": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
- "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-types": "^3.0.0",
- "negotiator": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/acorn": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
- "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
- "devOptional": 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/acorn-walk": {
- "version": "8.3.5",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
- "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "acorn": "^8.11.0"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ajv": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
- "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
- "json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ajv-formats": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
- "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
- "license": "MIT",
- "dependencies": {
- "ajv": "^8.0.0"
- },
- "peerDependencies": {
- "ajv": "^8.0.0"
- },
- "peerDependenciesMeta": {
- "ajv": {
- "optional": true
- }
- }
- },
- "node_modules/algoliasearch": {
- "version": "5.48.1",
- "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.48.1.tgz",
- "integrity": "sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@algolia/abtesting": "1.14.1",
- "@algolia/client-abtesting": "5.48.1",
- "@algolia/client-analytics": "5.48.1",
- "@algolia/client-common": "5.48.1",
- "@algolia/client-insights": "5.48.1",
- "@algolia/client-personalization": "5.48.1",
- "@algolia/client-query-suggestions": "5.48.1",
- "@algolia/client-search": "5.48.1",
- "@algolia/ingestion": "1.48.1",
- "@algolia/monitoring": "1.48.1",
- "@algolia/recommend": "5.48.1",
- "@algolia/requester-browser-xhr": "5.48.1",
- "@algolia/requester-fetch": "5.48.1",
- "@algolia/requester-node-http": "5.48.1"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/ansi-escapes": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
- "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "environment": "^1.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/ansi-styles": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
- "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/anymatch/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/arg": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
- "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/aria-query": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
- "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/axobject-query": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
- "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/balanced-match": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
- "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/base64id": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
- "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^4.5.0 || >= 5.9"
- }
- },
- "node_modules/baseline-browser-mapping": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
- "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
- "license": "Apache-2.0",
- "bin": {
- "baseline-browser-mapping": "dist/cli.cjs"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/beasties": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.1.tgz",
- "integrity": "sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "css-select": "^6.0.0",
- "css-what": "^7.0.0",
- "dom-serializer": "^2.0.0",
- "domhandler": "^5.0.3",
- "htmlparser2": "^10.0.0",
- "picocolors": "^1.1.1",
- "postcss": "^8.4.49",
- "postcss-media-query-parser": "^0.2.3",
- "postcss-safe-parser": "^7.0.1"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/body-parser": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
- "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "^3.1.2",
- "content-type": "^1.0.5",
- "debug": "^4.4.3",
- "http-errors": "^2.0.0",
- "iconv-lite": "^0.7.0",
- "on-finished": "^2.4.1",
- "qs": "^6.14.1",
- "raw-body": "^3.0.1",
- "type-is": "^2.0.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "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/bootstrap": {
- "version": "5.3.8",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
- "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/twbs"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/bootstrap"
- }
- ],
- "license": "MIT",
- "peerDependencies": {
- "@popperjs/core": "^2.11.8"
- }
- },
- "node_modules/brace-expansion": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
- "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "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.28.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
- "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
- "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.9.0",
- "caniuse-lite": "^1.0.30001759",
- "electron-to-chromium": "^1.5.263",
- "node-releases": "^2.0.27",
- "update-browserslist-db": "^1.2.0"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/cacache": {
- "version": "20.0.3",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz",
- "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/fs": "^5.0.0",
- "fs-minipass": "^3.0.0",
- "glob": "^13.0.0",
- "lru-cache": "^11.1.0",
- "minipass": "^7.0.3",
- "minipass-collect": "^2.0.1",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.4",
- "p-map": "^7.0.2",
- "ssri": "^13.0.0",
- "unique-filename": "^5.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/cacache/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/call-bound": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "get-intrinsic": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001777",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz",
- "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==",
- "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/chalk": {
- "version": "5.6.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
- "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/chardet": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
- "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/chokidar": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
- "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
- "license": "MIT",
- "dependencies": {
- "readdirp": "^5.0.0"
- },
- "engines": {
- "node": ">= 20.19.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/cli-cursor": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
- "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-spinners": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
- "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
- "license": "MIT",
- "engines": {
- "node": ">=18.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-truncate": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
- "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "slice-ansi": "^8.0.0",
- "string-width": "^8.2.0"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cli-width": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
- "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/cliui": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
- "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^7.2.0",
- "strip-ansi": "^7.1.0",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/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==",
- "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==",
- "license": "MIT"
- },
- "node_modules/colorette": {
- "version": "2.0.20",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
- "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
- "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/connect": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
- "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "finalhandler": "1.1.2",
- "parseurl": "~1.3.3",
- "utils-merge": "1.0.1"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/connect/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/connect/node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/connect/node_modules/finalhandler": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
- "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "statuses": "~1.5.0",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/connect/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/connect/node_modules/on-finished": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
- "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/connect/node_modules/statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/content-disposition": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
- "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "license": "MIT"
- },
- "node_modules/cookie": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
- "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cookie-signature": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
- "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.6.0"
- }
- },
- "node_modules/core-js": {
- "version": "3.48.0",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
- "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
- "node_modules/cors": {
- "version": "2.8.6",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
- "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "object-assign": "^4",
- "vary": "^1"
- },
- "engines": {
- "node": ">= 0.10"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/create-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
- "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "license": "MIT",
- "optional": true
- },
- "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/css-select": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz",
- "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "boolbase": "^1.0.0",
- "css-what": "^7.0.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.2.2",
- "nth-check": "^2.1.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/css-what": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz",
- "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">= 6"
- },
- "funding": {
- "url": "https://github.com/sponsors/fb55"
- }
- },
- "node_modules/custom-event": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
- "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/date-format": {
- "version": "4.0.14",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
- "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "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/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/detect-libc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/di": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
- "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/diff": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
- "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
- "license": "BSD-3-Clause",
- "optional": true,
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/dom-serialize": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
- "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "custom-event": "~1.0.0",
- "ent": "~2.2.0",
- "extend": "^3.0.0",
- "void-elements": "^2.0.0"
- }
- },
- "node_modules/dom-serializer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
- "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.2",
- "entities": "^4.2.0"
- },
- "funding": {
- "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
- }
- },
- "node_modules/domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "BSD-2-Clause"
- },
- "node_modules/domhandler": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
- "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "domelementtype": "^2.3.0"
- },
- "engines": {
- "node": ">= 4"
- },
- "funding": {
- "url": "https://github.com/fb55/domhandler?sponsor=1"
- }
- },
- "node_modules/domutils": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
- "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "dom-serializer": "^2.0.0",
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3"
- },
- "funding": {
- "url": "https://github.com/fb55/domutils?sponsor=1"
- }
- },
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.307",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
- "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==",
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/engine.io": {
- "version": "6.6.5",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz",
- "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/cors": "^2.8.12",
- "@types/node": ">=10.0.0",
- "accepts": "~1.3.4",
- "base64id": "2.0.0",
- "cookie": "~0.7.2",
- "cors": "~2.8.5",
- "debug": "~4.4.1",
- "engine.io-parser": "~5.2.1",
- "ws": "~8.18.3"
- },
- "engines": {
- "node": ">=10.2.0"
- }
- },
- "node_modules/engine.io-parser": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
- "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/engine.io/node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/engine.io/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/engine.io/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/engine.io/node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/ent": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz",
- "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.3",
- "es-errors": "^1.3.0",
- "punycode": "^1.4.1",
- "safe-regex-test": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/env-paths": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
- "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/environment": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
- "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/err-code": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
- "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/esbuild": {
- "version": "0.27.3",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
- "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.3",
- "@esbuild/android-arm": "0.27.3",
- "@esbuild/android-arm64": "0.27.3",
- "@esbuild/android-x64": "0.27.3",
- "@esbuild/darwin-arm64": "0.27.3",
- "@esbuild/darwin-x64": "0.27.3",
- "@esbuild/freebsd-arm64": "0.27.3",
- "@esbuild/freebsd-x64": "0.27.3",
- "@esbuild/linux-arm": "0.27.3",
- "@esbuild/linux-arm64": "0.27.3",
- "@esbuild/linux-ia32": "0.27.3",
- "@esbuild/linux-loong64": "0.27.3",
- "@esbuild/linux-mips64el": "0.27.3",
- "@esbuild/linux-ppc64": "0.27.3",
- "@esbuild/linux-riscv64": "0.27.3",
- "@esbuild/linux-s390x": "0.27.3",
- "@esbuild/linux-x64": "0.27.3",
- "@esbuild/netbsd-arm64": "0.27.3",
- "@esbuild/netbsd-x64": "0.27.3",
- "@esbuild/openbsd-arm64": "0.27.3",
- "@esbuild/openbsd-x64": "0.27.3",
- "@esbuild/openharmony-arm64": "0.27.3",
- "@esbuild/sunos-x64": "0.27.3",
- "@esbuild/win32-arm64": "0.27.3",
- "@esbuild/win32-ia32": "0.27.3",
- "@esbuild/win32-x64": "0.27.3"
- }
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
- "dev": true,
- "license": "MIT"
- },
- "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": "10.0.3",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz",
- "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.2",
- "@eslint/config-array": "^0.23.3",
- "@eslint/config-helpers": "^0.5.2",
- "@eslint/core": "^1.1.1",
- "@eslint/plugin-kit": "^0.6.1",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "ajv": "^6.14.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^9.1.2",
- "eslint-visitor-keys": "^5.0.1",
- "espree": "^11.1.1",
- "esquery": "^1.7.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "minimatch": "^10.2.4",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-scope": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
- "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "@types/esrecurse": "^4.3.1",
- "@types/estree": "^1.0.8",
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "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/eslint/node_modules/ajv": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
- "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
- "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/eslint/node_modules/eslint-visitor-keys": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
- "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/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/eslint/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/espree": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
- "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.16.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^5.0.1"
- },
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree/node_modules/eslint-visitor-keys": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
- "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
- "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
- "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/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/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/eventemitter3": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eventsource": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
- "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eventsource-parser": "^3.0.1"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/eventsource-parser": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
- "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/exponential-backoff": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
- "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/express": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
- "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "accepts": "^2.0.0",
- "body-parser": "^2.2.1",
- "content-disposition": "^1.0.0",
- "content-type": "^1.0.5",
- "cookie": "^0.7.1",
- "cookie-signature": "^1.2.1",
- "debug": "^4.4.0",
- "depd": "^2.0.0",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "etag": "^1.8.1",
- "finalhandler": "^2.1.0",
- "fresh": "^2.0.0",
- "http-errors": "^2.0.0",
- "merge-descriptors": "^2.0.0",
- "mime-types": "^3.0.0",
- "on-finished": "^2.4.1",
- "once": "^1.4.0",
- "parseurl": "^1.3.3",
- "proxy-addr": "^2.0.7",
- "qs": "^6.14.0",
- "range-parser": "^1.2.1",
- "router": "^2.2.0",
- "send": "^1.1.0",
- "serve-static": "^2.2.0",
- "statuses": "^2.0.1",
- "type-is": "^2.0.1",
- "vary": "^1.1.2"
- },
- "engines": {
- "node": ">= 18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/express-rate-limit": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.0.tgz",
- "integrity": "sha512-KJzBawY6fB9FiZGdE/0aftepZ91YlaGIrV8vgblRM3J8X+dHx/aiowJWwkx6LIGyuqGiANsjSwwrbb8mifOJ4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ip-address": "10.1.0"
- },
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/express-rate-limit"
- },
- "peerDependencies": {
- "express": ">= 4.11"
- }
- },
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "dev": true,
- "license": "MIT"
- },
- "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==",
- "license": "MIT"
- },
- "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/fast-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/faye-websocket": {
- "version": "0.11.4",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
- "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
- "license": "Apache-2.0",
- "dependencies": {
- "websocket-driver": ">=0.5.1"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "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/finalhandler": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
- "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.4.0",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "on-finished": "^2.4.1",
- "parseurl": "^1.3.3",
- "statuses": "^2.0.1"
- },
- "engines": {
- "node": ">= 18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "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/firebase": {
- "version": "11.10.0",
- "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz",
- "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@firebase/ai": "1.4.1",
- "@firebase/analytics": "0.10.17",
- "@firebase/analytics-compat": "0.2.23",
- "@firebase/app": "0.13.2",
- "@firebase/app-check": "0.10.1",
- "@firebase/app-check-compat": "0.3.26",
- "@firebase/app-compat": "0.4.2",
- "@firebase/app-types": "0.9.3",
- "@firebase/auth": "1.10.8",
- "@firebase/auth-compat": "0.5.28",
- "@firebase/data-connect": "0.3.10",
- "@firebase/database": "1.0.20",
- "@firebase/database-compat": "2.0.11",
- "@firebase/firestore": "4.8.0",
- "@firebase/firestore-compat": "0.3.53",
- "@firebase/functions": "0.12.9",
- "@firebase/functions-compat": "0.3.26",
- "@firebase/installations": "0.6.18",
- "@firebase/installations-compat": "0.2.18",
- "@firebase/messaging": "0.12.22",
- "@firebase/messaging-compat": "0.2.22",
- "@firebase/performance": "0.7.7",
- "@firebase/performance-compat": "0.2.20",
- "@firebase/remote-config": "0.6.5",
- "@firebase/remote-config-compat": "0.2.18",
- "@firebase/storage": "0.13.14",
- "@firebase/storage-compat": "0.3.24",
- "@firebase/util": "1.12.1"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
- "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/follow-redirects": {
- "version": "1.15.11",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
- "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
- "node_modules/font-awesome": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
- "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
- "license": "(OFL-1.1 AND MIT)",
- "engines": {
- "node": ">=0.10.3"
- }
- },
- "node_modules/forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/fresh": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
- "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
- "node_modules/fs-minipass": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
- "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "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/fuzzy": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz",
- "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==",
- "dev": true,
- "engines": {
- "node": ">= 0.6.0"
- }
- },
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "license": "ISC",
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/get-east-asian-width": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
- "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/get-intrinsic": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "es-define-property": "^1.0.1",
- "es-errors": "^1.3.0",
- "es-object-atoms": "^1.1.1",
- "function-bind": "^1.1.2",
- "get-proto": "^1.0.1",
- "gopd": "^1.2.0",
- "has-symbols": "^1.1.0",
- "hasown": "^2.0.2",
- "math-intrinsics": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/glob": {
- "version": "13.0.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
- "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "minimatch": "^10.2.2",
- "minipass": "^7.1.3",
- "path-scurry": "^2.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "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/glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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/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/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-tostringtag": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-symbols": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "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/hono": {
- "version": "4.12.5",
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.5.tgz",
- "integrity": "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=16.9.0"
- }
- },
- "node_modules/hosted-git-info": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz",
- "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^11.1.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/hosted-git-info/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/htmlparser2": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
- "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
- "dev": true,
- "funding": [
- "https://github.com/fb55/htmlparser2?sponsor=1",
- {
- "type": "github",
- "url": "https://github.com/sponsors/fb55"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "domelementtype": "^2.3.0",
- "domhandler": "^5.0.3",
- "domutils": "^3.2.2",
- "entities": "^7.0.1"
- }
- },
- "node_modules/htmlparser2/node_modules/entities": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
- "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/http-cache-semantics": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
- "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/http-errors": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
- "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "depd": "~2.0.0",
- "inherits": "~2.0.4",
- "setprototypeof": "~1.2.0",
- "statuses": "~2.0.2",
- "toidentifier": "~1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/http-parser-js": {
- "version": "0.5.10",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
- "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
- "license": "MIT"
- },
- "node_modules/http-proxy": {
- "version": "1.18.1",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
- "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eventemitter3": "^4.0.0",
- "follow-redirects": "^1.0.0",
- "requires-port": "^1.0.0"
- },
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
- "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
- "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/idb": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
- "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
- "license": "ISC"
- },
- "node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/ignore-walk": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz",
- "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minimatch": "^10.0.3"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/immutable": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
- "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
- "dev": true,
- "license": "MIT"
- },
- "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/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/ini": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz",
- "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/ip-address": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
- "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "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-fullwidth-code-point": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
- "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-east-asian-width": "^1.3.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-interactive": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
- "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "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-promise": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
- "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-regex": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
- "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "gopd": "^1.2.0",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-unicode-supported": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
- "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/isbinaryfile": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
- "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/gjtorikian/"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
- "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-instrument": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
- "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@babel/core": "^7.23.9",
- "@babel/parser": "^7.23.9",
- "@istanbuljs/schema": "^0.1.3",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-source-maps/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/istanbul-reports": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
- "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/jasmine": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-6.1.0.tgz",
- "integrity": "sha512-WPphPqEMY0uBRMjuhRHoVoxQNvJuxIMqz0yIcJ3k3oYxBedeGoH60/NXNgasxnx2FvfXrq5/r+2wssJ7WE8ABw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jasminejs/reporters": "^1.0.0",
- "glob": "^10.2.2 || ^11.0.3 || ^12.0.0 || ^13.0.0",
- "jasmine-core": "~6.1.0"
- },
- "bin": {
- "jasmine": "bin/jasmine.js"
- }
- },
- "node_modules/jasmine-core": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.1.0.tgz",
- "integrity": "sha512-p/tjBw58O6vxKIWMlrU+yys8lqR3+l3UrqwNTT7wpj+dQ7N4etQekFM8joI+cWzPDYqZf54kN+hLC1+s5TvZvg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/jasmine-spec-reporter": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz",
- "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "colors": "1.4.0"
- }
- },
- "node_modules/jose": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.0.tgz",
- "integrity": "sha512-xsfE1TcSCbUdo6U07tR0mvhg0flGxU8tPLbF03mirl2ukGQENhUg4ubGYQnhVH0b5stLlPM+WOqDkEl1R1y5sQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/panva"
- }
- },
- "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==",
- "license": "MIT"
- },
- "node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "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": "5.0.0",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz",
- "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/json-schema-traverse": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
- "license": "MIT"
- },
- "node_modules/json-schema-typed": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
- "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "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/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "license": "MIT",
- "bin": {
- "json5": "lib/cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/jsonc-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
- "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
- "license": "MIT"
- },
- "node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "dev": true,
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/jsonparse": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
- "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
- "dev": true,
- "engines": [
- "node >= 0.2.0"
- ],
- "license": "MIT"
- },
- "node_modules/karma": {
- "version": "6.4.4",
- "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
- "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@colors/colors": "1.5.0",
- "body-parser": "^1.19.0",
- "braces": "^3.0.2",
- "chokidar": "^3.5.1",
- "connect": "^3.7.0",
- "di": "^0.0.1",
- "dom-serialize": "^2.2.1",
- "glob": "^7.1.7",
- "graceful-fs": "^4.2.6",
- "http-proxy": "^1.18.1",
- "isbinaryfile": "^4.0.8",
- "lodash": "^4.17.21",
- "log4js": "^6.4.1",
- "mime": "^2.5.2",
- "minimatch": "^3.0.4",
- "mkdirp": "^0.5.5",
- "qjobs": "^1.2.0",
- "range-parser": "^1.2.1",
- "rimraf": "^3.0.2",
- "socket.io": "^4.7.2",
- "source-map": "^0.6.1",
- "tmp": "^0.2.1",
- "ua-parser-js": "^0.7.30",
- "yargs": "^16.1.1"
- },
- "bin": {
- "karma": "bin/karma"
- },
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/karma-chrome-launcher": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz",
- "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "which": "^1.2.1"
- }
- },
- "node_modules/karma-chrome-launcher/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
- "node_modules/karma-coverage": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz",
- "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "istanbul-lib-coverage": "^3.2.0",
- "istanbul-lib-instrument": "^5.1.0",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.1",
- "istanbul-reports": "^3.0.5",
- "minimatch": "^3.0.4"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/karma-coverage/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/karma-coverage/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/karma-coverage/node_modules/istanbul-lib-instrument": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/karma-coverage/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/karma-coverage/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/karma-jasmine": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
- "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "jasmine-core": "^4.1.0"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "karma": "^6.0.0"
- }
- },
- "node_modules/karma-jasmine-html-reporter": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.2.0.tgz",
- "integrity": "sha512-J0laEC43Oy2RdR5V5R3bqmdo7yRIYySq6XHKbA+e5iSAgLjhR1oICLGeSREPlJXpeyNcdJf3J17YcdhD0mRssQ==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "jasmine-core": "^4.0.0 || ^5.0.0 || ^6.0.0",
- "karma": "^6.0.0",
- "karma-jasmine": "^5.0.0"
- }
- },
- "node_modules/karma-jasmine/node_modules/jasmine-core": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz",
- "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/karma-junit-reporter": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz",
- "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-is-absolute": "^1.0.0",
- "xmlbuilder": "12.0.0"
- },
- "engines": {
- "node": ">= 8"
- },
- "peerDependencies": {
- "karma": ">=0.9"
- }
- },
- "node_modules/karma/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/karma/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/karma/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/karma/node_modules/body-parser": {
- "version": "1.20.4",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
- "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "~3.1.2",
- "content-type": "~1.0.5",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "~1.2.0",
- "http-errors": "~2.0.1",
- "iconv-lite": "~0.4.24",
- "on-finished": "~2.4.1",
- "qs": "~6.14.0",
- "raw-body": "~2.5.3",
- "type-is": "~1.6.18",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/karma/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/karma/node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/karma/node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "node_modules/karma/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/karma/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "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/karma/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/karma/node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/karma/node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/karma/node_modules/media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/karma/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/karma/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/karma/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/karma/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/karma/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/karma/node_modules/qs": {
- "version": "6.14.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
- "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/karma/node_modules/raw-body": {
- "version": "2.5.3",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
- "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "~3.1.2",
- "http-errors": "~2.0.1",
- "iconv-lite": "~0.4.24",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/karma/node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
- "node_modules/karma/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/karma/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/karma/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/karma/node_modules/type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/karma/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/karma/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/karma/node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
- "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/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/listr2": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
- "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cli-truncate": "^5.0.0",
- "colorette": "^2.0.20",
- "eventemitter3": "^5.0.1",
- "log-update": "^6.1.0",
- "rfdc": "^1.4.1",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "node_modules/listr2/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/listr2/node_modules/eventemitter3": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
- "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/listr2/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/listr2/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/lmdb": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.1.tgz",
- "integrity": "sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@harperfast/extended-iterable": "^1.0.3",
- "msgpackr": "^1.11.2",
- "node-addon-api": "^6.1.0",
- "node-gyp-build-optional-packages": "5.2.2",
- "ordered-binary": "^1.5.3",
- "weak-lru-cache": "^1.2.2"
- },
- "bin": {
- "download-lmdb-prebuilds": "bin/download-prebuilds.js"
- },
- "optionalDependencies": {
- "@lmdb/lmdb-darwin-arm64": "3.5.1",
- "@lmdb/lmdb-darwin-x64": "3.5.1",
- "@lmdb/lmdb-linux-arm": "3.5.1",
- "@lmdb/lmdb-linux-arm64": "3.5.1",
- "@lmdb/lmdb-linux-x64": "3.5.1",
- "@lmdb/lmdb-win32-arm64": "3.5.1",
- "@lmdb/lmdb-win32-x64": "3.5.1"
- }
- },
- "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.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
- "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==",
- "license": "MIT"
- },
- "node_modules/log-symbols": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
- "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
- "license": "MIT",
- "dependencies": {
- "is-unicode-supported": "^2.0.0",
- "yoctocolors": "^2.1.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
- "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-escapes": "^7.0.0",
- "cli-cursor": "^5.0.0",
- "slice-ansi": "^7.1.0",
- "strip-ansi": "^7.1.0",
- "wrap-ansi": "^9.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/log-update/node_modules/slice-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
- "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "is-fullwidth-code-point": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/slice-ansi?sponsor=1"
- }
- },
- "node_modules/log-update/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/log4js": {
- "version": "6.9.1",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz",
- "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "flatted": "^3.2.7",
- "rfdc": "^1.3.0",
- "streamroller": "^3.1.5"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/long": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
- "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
- "license": "Apache-2.0"
- },
- "node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "license": "ISC",
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "node_modules/magic-string": {
- "version": "0.30.21",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
- }
- },
- "node_modules/make-dir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
- "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "license": "ISC",
- "optional": true
- },
- "node_modules/make-fetch-happen": {
- "version": "15.0.4",
- "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.4.tgz",
- "integrity": "sha512-vM2sG+wbVeVGYcCm16mM3d5fuem9oC28n436HjsGO3LcxoTI8LNVa4rwZDn3f76+cWyT4GGJDxjTYU1I2nr6zw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@gar/promise-retry": "^1.0.0",
- "@npmcli/agent": "^4.0.0",
- "cacache": "^20.0.1",
- "http-cache-semantics": "^4.1.1",
- "minipass": "^7.0.2",
- "minipass-fetch": "^5.0.0",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.4",
- "negotiator": "^1.0.0",
- "proc-log": "^6.0.0",
- "ssri": "^13.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/media-typer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
- "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/merge-descriptors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
- "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/mime": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
- "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/mime-db": {
- "version": "1.54.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
- "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
- "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "^1.54.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/mimic-function": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
- "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/minimatch": {
- "version": "10.2.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
- "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "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/minipass": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
- "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minipass-collect": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
- "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minipass-fetch": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz",
- "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.0.3",
- "minipass-sized": "^2.0.0",
- "minizlib": "^3.0.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- },
- "optionalDependencies": {
- "iconv-lite": "^0.7.2"
- }
- },
- "node_modules/minipass-flush": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
- "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^3.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/minipass-flush/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minipass-flush/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/minipass-pipeline": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
- "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minipass-pipeline/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minipass-pipeline/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/minipass-sized": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz",
- "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minizlib": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
- "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/mrmime": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
- "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "license": "MIT"
- },
- "node_modules/msgpackr": {
- "version": "1.11.8",
- "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.8.tgz",
- "integrity": "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "optionalDependencies": {
- "msgpackr-extract": "^3.0.2"
- }
- },
- "node_modules/msgpackr-extract": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
- "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "node-gyp-build-optional-packages": "5.2.2"
- },
- "bin": {
- "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
- },
- "optionalDependencies": {
- "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
- "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
- }
- },
- "node_modules/mute-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
- "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
- "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/negotiator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
- "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/node-addon-api": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
- "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/node-gyp": {
- "version": "12.2.0",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz",
- "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "env-paths": "^2.2.0",
- "exponential-backoff": "^3.1.1",
- "graceful-fs": "^4.2.6",
- "make-fetch-happen": "^15.0.0",
- "nopt": "^9.0.0",
- "proc-log": "^6.0.0",
- "semver": "^7.3.5",
- "tar": "^7.5.4",
- "tinyglobby": "^0.2.12",
- "which": "^6.0.0"
- },
- "bin": {
- "node-gyp": "bin/node-gyp.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/node-gyp-build-optional-packages": {
- "version": "5.2.2",
- "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
- "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "detect-libc": "^2.0.1"
- },
- "bin": {
- "node-gyp-build-optional-packages": "bin.js",
- "node-gyp-build-optional-packages-optional": "optional.js",
- "node-gyp-build-optional-packages-test": "build-test.js"
- }
- },
- "node_modules/node-gyp/node_modules/isexe": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
- "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/node-gyp/node_modules/which": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
- "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^4.0.0"
- },
- "bin": {
- "node-which": "bin/which.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.36",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
- "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
- "license": "MIT"
- },
- "node_modules/nopt": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
- "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "abbrev": "^4.0.0"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/npm-bundled": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz",
- "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "npm-normalize-package-bin": "^5.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-install-checks": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz",
- "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "semver": "^7.1.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-normalize-package-bin": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz",
- "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-package-arg": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz",
- "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "hosted-git-info": "^9.0.0",
- "proc-log": "^6.0.0",
- "semver": "^7.3.5",
- "validate-npm-package-name": "^7.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-packlist": {
- "version": "10.0.4",
- "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz",
- "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "ignore-walk": "^8.0.0",
- "proc-log": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-pick-manifest": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz",
- "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "npm-install-checks": "^8.0.0",
- "npm-normalize-package-bin": "^5.0.0",
- "npm-package-arg": "^13.0.0",
- "semver": "^7.3.5"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/npm-registry-fetch": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz",
- "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/redact": "^4.0.0",
- "jsonparse": "^1.3.1",
- "make-fetch-happen": "^15.0.0",
- "minipass": "^7.0.2",
- "minipass-fetch": "^5.0.0",
- "minizlib": "^3.0.1",
- "npm-package-arg": "^13.0.0",
- "proc-log": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "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-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "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/onetime": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
- "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
- "license": "MIT",
- "dependencies": {
- "mimic-function": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "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/ora": {
- "version": "9.3.0",
- "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz",
- "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^5.6.2",
- "cli-cursor": "^5.0.0",
- "cli-spinners": "^3.2.0",
- "is-interactive": "^2.0.0",
- "is-unicode-supported": "^2.1.0",
- "log-symbols": "^7.0.1",
- "stdin-discarder": "^0.3.1",
- "string-width": "^8.1.0"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ordered-binary": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz",
- "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "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-map": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
- "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/pacote": {
- "version": "21.3.1",
- "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.3.1.tgz",
- "integrity": "sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/git": "^7.0.0",
- "@npmcli/installed-package-contents": "^4.0.0",
- "@npmcli/package-json": "^7.0.0",
- "@npmcli/promise-spawn": "^9.0.0",
- "@npmcli/run-script": "^10.0.0",
- "cacache": "^20.0.0",
- "fs-minipass": "^3.0.0",
- "minipass": "^7.0.2",
- "npm-package-arg": "^13.0.0",
- "npm-packlist": "^10.0.1",
- "npm-pick-manifest": "^11.0.1",
- "npm-registry-fetch": "^19.0.0",
- "proc-log": "^6.0.0",
- "promise-retry": "^2.0.1",
- "sigstore": "^4.0.0",
- "ssri": "^13.0.0",
- "tar": "^7.4.3"
- },
- "bin": {
- "pacote": "bin/index.js"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/parse5": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
- "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "entities": "^6.0.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parse5-html-rewriting-stream": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz",
- "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "entities": "^6.0.0",
- "parse5": "^8.0.0",
- "parse5-sax-parser": "^8.0.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parse5-html-rewriting-stream/node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/parse5-sax-parser": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz",
- "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parse5": "^8.0.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/parse5/node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "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-scurry": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
- "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^11.0.0",
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-scurry/node_modules/lru-cache": {
- "version": "11.2.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
- "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/path-to-regexp": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
- "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "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": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/piscina": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.4.tgz",
- "integrity": "sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=20.x"
- },
- "optionalDependencies": {
- "@napi-rs/nice": "^1.0.4"
- }
- },
- "node_modules/pkce-challenge": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
- "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/postcss": {
- "version": "8.5.8",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
- "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
- "dev": true,
- "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-media-query-parser": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
- "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/postcss-safe-parser": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
- "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=18.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.31"
- }
- },
- "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/proc-log": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
- "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/promise-retry": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
- "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "err-code": "^2.0.2",
- "retry": "^0.12.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/promise-retry/node_modules/retry": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
- "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/protobufjs": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
- "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
- "hasInstallScript": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@protobufjs/aspromise": "^1.1.2",
- "@protobufjs/base64": "^1.1.2",
- "@protobufjs/codegen": "^2.0.4",
- "@protobufjs/eventemitter": "^1.1.0",
- "@protobufjs/fetch": "^1.1.0",
- "@protobufjs/float": "^1.0.2",
- "@protobufjs/inquire": "^1.1.0",
- "@protobufjs/path": "^1.1.2",
- "@protobufjs/pool": "^1.1.0",
- "@protobufjs/utf8": "^1.1.0",
- "@types/node": ">=13.7.0",
- "long": "^5.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/qjobs": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
- "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.9"
- }
- },
- "node_modules/qs": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
- "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/raw-body": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
- "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bytes": "~3.1.2",
- "http-errors": "~2.0.1",
- "iconv-lite": "~0.7.0",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/readdirp": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
- "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 20.19.0"
- },
- "funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/reflect-metadata": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
- "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
- "license": "Apache-2.0"
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/restore-cursor": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
- "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
- "license": "MIT",
- "dependencies": {
- "onetime": "^7.0.0",
- "signal-exit": "^4.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/retry": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
- "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/rfdc": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
- "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/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/rimraf/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/rimraf/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/rimraf/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "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/rimraf/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/rolldown": {
- "version": "1.0.0-rc.4",
- "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.4.tgz",
- "integrity": "sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@oxc-project/types": "=0.113.0",
- "@rolldown/pluginutils": "1.0.0-rc.4"
- },
- "bin": {
- "rolldown": "bin/cli.mjs"
- },
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- },
- "optionalDependencies": {
- "@rolldown/binding-android-arm64": "1.0.0-rc.4",
- "@rolldown/binding-darwin-arm64": "1.0.0-rc.4",
- "@rolldown/binding-darwin-x64": "1.0.0-rc.4",
- "@rolldown/binding-freebsd-x64": "1.0.0-rc.4",
- "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.4",
- "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.4",
- "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.4",
- "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.4",
- "@rolldown/binding-linux-x64-musl": "1.0.0-rc.4",
- "@rolldown/binding-openharmony-arm64": "1.0.0-rc.4",
- "@rolldown/binding-wasm32-wasi": "1.0.0-rc.4",
- "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.4",
- "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.4"
- }
- },
- "node_modules/rollup": {
- "version": "4.59.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
- "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
- "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.59.0",
- "@rollup/rollup-android-arm64": "4.59.0",
- "@rollup/rollup-darwin-arm64": "4.59.0",
- "@rollup/rollup-darwin-x64": "4.59.0",
- "@rollup/rollup-freebsd-arm64": "4.59.0",
- "@rollup/rollup-freebsd-x64": "4.59.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
- "@rollup/rollup-linux-arm64-gnu": "4.59.0",
- "@rollup/rollup-linux-arm64-musl": "4.59.0",
- "@rollup/rollup-linux-loong64-gnu": "4.59.0",
- "@rollup/rollup-linux-loong64-musl": "4.59.0",
- "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
- "@rollup/rollup-linux-ppc64-musl": "4.59.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
- "@rollup/rollup-linux-riscv64-musl": "4.59.0",
- "@rollup/rollup-linux-s390x-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-gnu": "4.59.0",
- "@rollup/rollup-linux-x64-musl": "4.59.0",
- "@rollup/rollup-openbsd-x64": "4.59.0",
- "@rollup/rollup-openharmony-arm64": "4.59.0",
- "@rollup/rollup-win32-arm64-msvc": "4.59.0",
- "@rollup/rollup-win32-ia32-msvc": "4.59.0",
- "@rollup/rollup-win32-x64-gnu": "4.59.0",
- "@rollup/rollup-win32-x64-msvc": "4.59.0",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/router": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
- "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.4.0",
- "depd": "^2.0.0",
- "is-promise": "^4.0.0",
- "parseurl": "^1.3.3",
- "path-to-regexp": "^8.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/rxfire": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/rxfire/-/rxfire-6.1.0.tgz",
- "integrity": "sha512-NezdjeY32VZcCuGO0bbb8H8seBsJSCaWdUwGsHNzUcAOHR0VGpzgPtzjuuLXr8R/iemkqSzbx/ioS7VwV43ynA==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "firebase": "^9.0.0 || ^10.0.0 || ^11.0.0",
- "rxjs": "^6.0.0 || ^7.0.0"
- }
- },
- "node_modules/rxjs": {
- "version": "7.8.2",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
- "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.1.0"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "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/safe-regex-test": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
- "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "is-regex": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/sass": {
- "version": "1.97.3",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz",
- "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chokidar": "^4.0.0",
- "immutable": "^5.0.2",
- "source-map-js": ">=0.6.2 <2.0.0"
- },
- "bin": {
- "sass": "sass.js"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "optionalDependencies": {
- "@parcel/watcher": "^2.4.1"
- }
- },
- "node_modules/sass/node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "readdirp": "^4.0.1"
- },
- "engines": {
- "node": ">= 14.16.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/sass/node_modules/readdirp": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14.18.0"
- },
- "funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/semver": {
- "version": "7.7.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
- "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/send": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
- "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.4.3",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "etag": "^1.8.1",
- "fresh": "^2.0.0",
- "http-errors": "^2.0.1",
- "mime-types": "^3.0.2",
- "ms": "^2.1.3",
- "on-finished": "^2.4.1",
- "range-parser": "^1.2.1",
- "statuses": "^2.0.2"
- },
- "engines": {
- "node": ">= 18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/serve-static": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
- "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "parseurl": "^1.3.3",
- "send": "^1.2.0"
- },
- "engines": {
- "node": ">= 18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
- "dev": true,
- "license": "ISC"
- },
- "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/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/sigstore": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz",
- "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@sigstore/bundle": "^4.0.0",
- "@sigstore/core": "^3.1.0",
- "@sigstore/protobuf-specs": "^0.5.0",
- "@sigstore/sign": "^4.1.0",
- "@sigstore/tuf": "^4.0.1",
- "@sigstore/verify": "^3.1.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/slice-ansi": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
- "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.3",
- "is-fullwidth-code-point": "^5.1.0"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/chalk/slice-ansi?sponsor=1"
- }
- },
- "node_modules/smart-buffer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
- "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socket.io": {
- "version": "4.8.3",
- "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
- "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "accepts": "~1.3.4",
- "base64id": "~2.0.0",
- "cors": "~2.8.5",
- "debug": "~4.4.1",
- "engine.io": "~6.6.0",
- "socket.io-adapter": "~2.5.2",
- "socket.io-parser": "~4.2.4"
- },
- "engines": {
- "node": ">=10.2.0"
- }
- },
- "node_modules/socket.io-adapter": {
- "version": "2.5.6",
- "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz",
- "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "~4.4.1",
- "ws": "~8.18.3"
- }
- },
- "node_modules/socket.io-parser": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
- "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.4.1"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/socket.io/node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/socket.io/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/socket.io/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/socket.io/node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/socks": {
- "version": "2.8.7",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
- "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ip-address": "^10.0.1",
- "smart-buffer": "^4.2.0"
- },
- "engines": {
- "node": ">= 10.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socks-proxy-agent": {
- "version": "8.0.5",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
- "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/source-map": {
- "version": "0.7.6",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
- "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">= 12"
- }
- },
- "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==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/source-map-support/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/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.23",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
- "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
- "dev": true,
- "license": "CC0-1.0"
- },
- "node_modules/ssri": {
- "version": "13.0.1",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz",
- "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/statuses": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
- "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/stdin-discarder": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.1.tgz",
- "integrity": "sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/streamroller": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
- "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "date-format": "^4.0.14",
- "debug": "^4.3.4",
- "fs-extra": "^8.1.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/string-width": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
- "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
- "license": "MIT",
- "dependencies": {
- "get-east-asian-width": "^1.5.0",
- "strip-ansi": "^7.1.2"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/strip-ansi": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
- "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.2.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "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/tar": {
- "version": "7.5.10",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
- "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.1.0",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/tar/node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
- "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/tmp": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
- "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.14"
- }
- },
- "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/toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
- "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "node_modules/ts-node": {
- "version": "10.7.0",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz",
- "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@cspotcode/source-map-support": "0.7.0",
- "@tsconfig/node10": "^1.0.7",
- "@tsconfig/node12": "^1.0.7",
- "@tsconfig/node14": "^1.0.0",
- "@tsconfig/node16": "^1.0.2",
- "acorn": "^8.4.1",
- "acorn-walk": "^8.1.1",
- "arg": "^4.1.0",
- "create-require": "^1.1.0",
- "diff": "^4.0.1",
- "make-error": "^1.1.1",
- "v8-compile-cache-lib": "^3.0.0",
- "yn": "3.1.1"
- },
- "bin": {
- "ts-node": "dist/bin.js",
- "ts-node-cwd": "dist/bin-cwd.js",
- "ts-node-esm": "dist/bin-esm.js",
- "ts-node-script": "dist/bin-script.js",
- "ts-node-transpile-only": "dist/bin-transpile.js",
- "ts-script": "dist/bin-script-deprecated.js"
- },
- "peerDependencies": {
- "@swc/core": ">=1.2.50",
- "@swc/wasm": ">=1.2.50",
- "@types/node": "*",
- "typescript": ">=2.7"
- },
- "peerDependenciesMeta": {
- "@swc/core": {
- "optional": true
- },
- "@swc/wasm": {
- "optional": true
- }
- }
- },
- "node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
- },
- "node_modules/tuf-js": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz",
- "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@tufjs/models": "4.1.0",
- "debug": "^4.4.3",
- "make-fetch-happen": "^15.0.1"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.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-is": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
- "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "content-type": "^1.0.5",
- "media-typer": "^1.1.0",
- "mime-types": "^3.0.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "devOptional": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/ua-parser-js": {
- "version": "0.7.41",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz",
- "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/ua-parser-js"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/faisalman"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
- }
- ],
- "license": "MIT",
- "bin": {
- "ua-parser-js": "script/cli.js"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/undici": {
- "version": "7.22.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
- "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=20.18.1"
- }
- },
- "node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
- "license": "MIT"
- },
- "node_modules/unique-filename": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz",
- "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "unique-slug": "^6.0.0"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/unique-slug": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz",
- "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "imurmurhash": "^0.1.4"
- },
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
- "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
- "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/uri-js/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/utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/v8-compile-cache-lib": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
- "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/validate-npm-package-name": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz",
- "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^20.17.0 || >=22.9.0"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/vite": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
- "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "^0.27.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/void-elements": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
- "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/watchpack": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
- "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.1.2"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/weak-lru-cache": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
- "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/web-vitals": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz",
- "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
- "license": "Apache-2.0"
- },
- "node_modules/websocket-driver": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
- "license": "Apache-2.0",
- "dependencies": {
- "http-parser-js": ">=0.5.1",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/websocket-extensions": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
- "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "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/wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi/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/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/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/xhr2": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz",
- "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/xmlbuilder": {
- "version": "12.0.0",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz",
- "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0"
- }
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "license": "ISC",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "license": "ISC"
- },
- "node_modules/yargs": {
- "version": "18.0.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
- "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
- "license": "MIT",
- "dependencies": {
- "cliui": "^9.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "string-width": "^7.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^22.0.0"
- },
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=23"
- }
- },
- "node_modules/yargs-parser": {
- "version": "22.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
- "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
- "license": "ISC",
- "engines": {
- "node": "^20.19.0 || ^22.12.0 || >=23"
- }
- },
- "node_modules/yargs/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "license": "MIT"
- },
- "node_modules/yargs/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/yn": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
- "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=6"
- }
- },
- "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/yoctocolors": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
- "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/yoctocolors-cjs": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
- "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/zod": {
- "version": "4.3.6",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
- "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
- },
- "node_modules/zod-to-json-schema": {
- "version": "3.25.1",
- "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
- "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
- "dev": true,
- "license": "ISC",
- "peerDependencies": {
- "zod": "^3.25 || ^4"
- }
- },
- "node_modules/zone.js": {
- "version": "0.16.1",
- "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.1.tgz",
- "integrity": "sha512-dpvY17vxYIW3+bNrP0ClUlaiY0CiIRK3tnoLaGoQsQcY9/I/NpzIWQ7tQNhbV7LacQMpCII6wVzuL3tuWOyfuA==",
- "license": "MIT"
- }
- }
-}
diff --git a/samples/angular/package.json b/samples/angular/package.json
deleted file mode 100644
index cbb28ecc..00000000
--- a/samples/angular/package.json
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- "name": "keeptrack-angularwebapp",
- "version": "0.1.0",
- "scripts": {
- "ng": "ng",
- "start": "ng serve --configuration=dev",
- "build": "ng build",
- "watch": "ng build --watch --configuration dev",
- "test": "ng test --configuration dev",
- "test:ci": "ng test --no-watch --configuration=dev --code-coverage --browsers ChromeHeadless",
- "lint": "ng lint",
- "e2e": "ng e2e"
- },
- "private": true,
- "dependencies": {
- "@angular/animations": "^21.2.1",
- "@angular/common": "^21.2.1",
- "@angular/compiler": "^21.2.1",
- "@angular/core": "^21.2.1",
- "@angular/fire": "^21.0.0-rc.0",
- "@angular/forms": "^21.2.1",
- "@angular/localize": "^21.2.1",
- "@angular/platform-browser": "^21.2.1",
- "@angular/platform-browser-dynamic": "^21.2.1",
- "@angular/platform-server": "^21.2.1",
- "@angular/router": "^21.2.1",
- "@popperjs/core": "~2.11.8",
- "bootstrap": "~5.3.2",
- "core-js": "^3.48.0",
- "font-awesome": "^4.7.0",
- "rxjs": "~7.8.2",
- "tslib": "^2.8.1",
- "zone.js": "~0.16.1"
- },
- "devDependencies": {
- "@angular-devkit/architect": "^0.2102.1",
- "@angular-eslint/builder": "^21.3.0",
- "@angular-eslint/eslint-plugin": "^21.3.0",
- "@angular-eslint/eslint-plugin-template": "^21.3.0",
- "@angular-eslint/schematics": "^21.3.0",
- "@angular-eslint/template-parser": "^21.3.0",
- "@angular/build": "^21.2.1",
- "@angular/cli": "^21.2.1",
- "@angular/compiler-cli": "^21.2.1",
- "@angular/language-service": "^21.2.1",
- "@types/jasmine": "~6.0.0",
- "@types/node": "^22.13.14",
- "acorn": "~8.16.0",
- "eslint": "^10.0.3",
- "fuzzy": "~0.1.3",
- "glob": "^13.0.6",
- "jasmine": "~6.1.0",
- "jasmine-core": "~6.1.0",
- "jasmine-spec-reporter": "~7.0.0",
- "karma": "~6.4.4",
- "karma-chrome-launcher": "~3.2.0",
- "karma-coverage": "~2.2.1",
- "karma-jasmine": "~5.1.0",
- "karma-jasmine-html-reporter": "~2.2.0",
- "karma-junit-reporter": "~2.0.1",
- "typescript": "~5.9.3"
- },
- "optionalDependencies": {
- "ts-node": "~10.7.0"
- }
-}
diff --git a/samples/angular/src/app/app.component.html b/samples/angular/src/app/app.component.html
deleted file mode 100644
index 8a51c496..00000000
--- a/samples/angular/src/app/app.component.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/samples/angular/src/app/app.component.ts b/samples/angular/src/app/app.component.ts
deleted file mode 100644
index dcbdb596..00000000
--- a/samples/angular/src/app/app.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component } from '@angular/core';
-import { RouterOutlet } from '@angular/router';
-import { HeaderComponent } from './layout/header/header.component';
-
-@Component({
- selector: 'app-root',
- imports: [
- RouterOutlet,
- HeaderComponent
- ],
- templateUrl: './app.component.html'
-})
-export class AppComponent {
- title = 'app';
-}
diff --git a/samples/angular/src/app/app.routes.ts b/samples/angular/src/app/app.routes.ts
deleted file mode 100644
index 438e2070..00000000
--- a/samples/angular/src/app/app.routes.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Routes } from '@angular/router';
-import { AuthGuard } from '@angular/fire/auth-guard';
-
-import { HomeComponent } from './home/home.component';
-import { BookComponent } from './inventory/book/book.component';
-import { CarComponent } from './inventory/car/car.component';
-import { MovieComponent } from './inventory/movie/movie.component';
-import { TvShowComponent } from './inventory/tv-show/tv-show.component';
-import { VideoGameComponent } from './inventory/video-game/video-game.component';
-import { LoginComponent } from './user/login/login.component';
-
-export const routes: Routes = [
- { path: '', component: HomeComponent, pathMatch: 'full' },
- { path: 'login', component: LoginComponent },
- { path: 'movies', component: MovieComponent, canActivate: [AuthGuard] },
- { path: 'books', component: BookComponent, canActivate: [AuthGuard] },
- { path: 'cars', component: CarComponent, canActivate: [AuthGuard] },
- { path: 'tv-shows', component: TvShowComponent, canActivate: [AuthGuard] },
- { path: 'video-games', component: VideoGameComponent, canActivate: [AuthGuard] }
-];
diff --git a/samples/angular/src/app/backend/backend.module.ts b/samples/angular/src/app/backend/backend.module.ts
deleted file mode 100644
index caa787d1..00000000
--- a/samples/angular/src/app/backend/backend.module.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { MovieService } from './services/movie.service';
-import { CarHistoryService } from './services/car-history.service';
-
-@NgModule({
- declarations: [],
- imports: [
- CommonModule
- ],
- providers: [
- CarHistoryService,
- MovieService
- ]
-})
-export class BackendModule { }
diff --git a/samples/angular/src/app/backend/services/book.service.spec.ts b/samples/angular/src/app/backend/services/book.service.spec.ts
deleted file mode 100644
index 1312587e..00000000
--- a/samples/angular/src/app/backend/services/book.service.spec.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
-
-import { BookService } from './book.service';
-import { Book } from '../types/book';
-import { environment } from 'src/environments/environment.dev';
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('BookService', () => {
- let bookService: BookService;
- let http: HttpTestingController;
-
- beforeEach(() => TestBed.configureTestingModule({
- imports: [],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-}));
-
- beforeEach(() => {
- http = TestBed.inject(HttpTestingController);
- bookService = TestBed.inject(BookService);
- });
-
- afterAll(() => http.verify());
-
- it('should list', () => {
- // fake response
- const hardcodedBooks = [{ title: 'The fellowship of the Ring' }, { title: 'The two Towers' }] as Array;
-
- let actualBooks: Array = [];
- bookService.list().subscribe((books: Array) => actualBooks = books);
-
- http.expectOne(`${environment.keepTrackApiUrl}/api/books?search=&page=0&pageSize=50`)
- .flush(hardcodedBooks);
-
- expect(actualBooks).toEqual(hardcodedBooks, 'The `list` method should return an array of Book wrapped in an Observable');
- });
-});
diff --git a/samples/angular/src/app/backend/services/book.service.ts b/samples/angular/src/app/backend/services/book.service.ts
deleted file mode 100644
index e4ee8478..00000000
--- a/samples/angular/src/app/backend/services/book.service.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Injectable, inject } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { environment } from 'src/environments/environment';
-import { Book } from '../types/book';
-import { DataService } from './data.interface';
-
-@Injectable({
- providedIn: 'root'
-})
-export class BookService implements DataService {
- private httpClient = inject(HttpClient);
-
- get(id: string): Observable {
- return this.httpClient.get(`${environment.keepTrackApiUrl}/api/books/${id}`);
- }
-
- list(search?: string, currentPage?: number, pageSize?: number): Observable> {
- return this.httpClient.get>(`${environment.keepTrackApiUrl}/api/books?search=${search ?? ''}&page=${currentPage ?? 0}&pageSize=${pageSize ?? 50}`);
- }
-
- create(input: Book): Observable {
- return this.httpClient.post(`${environment.keepTrackApiUrl}/api/books`, input);
- }
-
- update(input: Book): Observable {
- delete input.isEditable;
- if (!input.finishedAt) {
- delete input.finishedAt;
- }
- return this.httpClient.put(`${environment.keepTrackApiUrl}/api/books/${input.id}`, input);
- }
-
- delete(input: Book): Observable {
- return this.httpClient.delete(`${environment.keepTrackApiUrl}/api/books/${input.id}`);
- }
-}
diff --git a/samples/angular/src/app/backend/services/car-history.service.spec.ts b/samples/angular/src/app/backend/services/car-history.service.spec.ts
deleted file mode 100644
index 1f1e53ed..00000000
--- a/samples/angular/src/app/backend/services/car-history.service.spec.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { CarHistoryService } from './car-history.service';
-
-describe('CarHistoryService', () => {
- beforeEach(() => TestBed.configureTestingModule({}));
-
- it('should be created', () => {
- const service: CarHistoryService = TestBed.inject(CarHistoryService);
- expect(service).toBeTruthy();
- });
-});
diff --git a/samples/angular/src/app/backend/services/car-history.service.ts b/samples/angular/src/app/backend/services/car-history.service.ts
deleted file mode 100644
index efb1657c..00000000
--- a/samples/angular/src/app/backend/services/car-history.service.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable({
- providedIn: 'root'
-})
-export class CarHistoryService {
-}
diff --git a/samples/angular/src/app/backend/services/data.interface.ts b/samples/angular/src/app/backend/services/data.interface.ts
deleted file mode 100644
index 399a0589..00000000
--- a/samples/angular/src/app/backend/services/data.interface.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Observable } from "rxjs";
-
-export interface DataService {
- get(id: string): Observable;
- list(search?: string, currentPage?: number, pageSize?: number, filter?: T): Observable>;
- create(input: T): Observable;
- update(input: T): Observable;
- delete(input: T): Observable;
-}
diff --git a/samples/angular/src/app/backend/services/movie.service.spec.ts b/samples/angular/src/app/backend/services/movie.service.spec.ts
deleted file mode 100644
index 73e63c11..00000000
--- a/samples/angular/src/app/backend/services/movie.service.spec.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
-
-import { environment } from 'src/environments/environment.dev';
-import { MovieService } from './movie.service';
-import { Movie } from '../types/movie';
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('MovieService', () => {
- let movieService: MovieService;
- let http: HttpTestingController;
-
- beforeEach(() => TestBed.configureTestingModule({
- imports: [],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-}));
-
- beforeEach(() => {
- http = TestBed.inject(HttpTestingController);
- movieService = TestBed.inject(MovieService);
- });
-
- afterAll(() => http.verify());
-
- it('should list', () => {
- // fake response
- const hardcodedMovies = [{ title: 'Terminator 1' }, { title: 'Terminator 2' }] as Array;
-
- let actualMovies: Array = [];
- movieService.list().subscribe((movies: Array) => actualMovies = movies);
-
- http.expectOne(`${environment.keepTrackApiUrl}/api/movies?search=&page=0&pageSize=50`)
- .flush(hardcodedMovies);
-
- expect(actualMovies).toEqual(hardcodedMovies, 'The `list` method should return an array of Movie wrapped in an Observable');
- });
-});
diff --git a/samples/angular/src/app/backend/services/movie.service.ts b/samples/angular/src/app/backend/services/movie.service.ts
deleted file mode 100644
index e5d07315..00000000
--- a/samples/angular/src/app/backend/services/movie.service.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Injectable, inject } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { environment } from 'src/environments/environment';
-import { DataService } from './data.interface';
-import { Movie } from '../types/movie';
-
-@Injectable({
- providedIn: 'root'
-})
-export class MovieService implements DataService {
- private httpClient = inject(HttpClient);
-
- get(id: string): Observable {
- return this.httpClient.get(`${environment.keepTrackApiUrl}/api/movies/${id}`);
- }
-
- list(search?: string, currentPage?: number, pageSize?: number): Observable> {
- return this.httpClient.get>(`${environment.keepTrackApiUrl}/api/movies?search=${search ?? ''}&page=${currentPage ?? 0}&pageSize=${pageSize ?? 50}`);
- }
-
- create(input: Movie): Observable {
- return this.httpClient.post(`${environment.keepTrackApiUrl}/api/movies`, input);
- }
-
- update(input: Movie): Observable {
- delete input.isEditable;
- if (!input.year) {
- delete input.year;
- }
-
- return this.httpClient.put(`${environment.keepTrackApiUrl}/api/movies/${input.id}`, input);
- }
-
- delete(input: Movie): Observable {
- return this.httpClient.delete(`${environment.keepTrackApiUrl}/api/movies/${input.id}`);
- }
-}
diff --git a/samples/angular/src/app/backend/services/tv-show.service.spec.ts b/samples/angular/src/app/backend/services/tv-show.service.spec.ts
deleted file mode 100644
index ccdec45c..00000000
--- a/samples/angular/src/app/backend/services/tv-show.service.spec.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
-import { environment } from 'src/environments/environment.dev';
-import { TvShow } from '../types/tv-show';
-import { TvShowService } from './tv-show.service';
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('TvShowService', () => {
- let service: TvShowService;
- let http: HttpTestingController;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-});
- http = TestBed.inject(HttpTestingController);
- service = TestBed.inject(TvShowService);
- });
-
- afterAll(() => http.verify());
-
- it('should list', () => {
- // fake response
- const fake = [{ title: 'Friends' }, { title: 'ER' }] as Array;
-
- let actual: Array = [];
- service.list().subscribe((movies: Array) => actual = movies);
-
- http.expectOne(`${environment.keepTrackApiUrl}/api/tv-shows?search=&page=0&pageSize=50`)
- .flush(fake);
-
- expect(actual).toEqual(fake, 'The `list` method should return an array of TV Shows wrapped in an Observable');
- });
-
-});
diff --git a/samples/angular/src/app/backend/services/tv-show.service.ts b/samples/angular/src/app/backend/services/tv-show.service.ts
deleted file mode 100644
index d731edb2..00000000
--- a/samples/angular/src/app/backend/services/tv-show.service.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Injectable, inject } from '@angular/core';
-import { Observable } from 'rxjs';
-import { environment } from 'src/environments/environment';
-import { TvShow } from '../types/tv-show';
-import { DataService } from './data.interface';
-
-@Injectable({
- providedIn: 'root'
-})
-export class TvShowService implements DataService {
- private httpClient = inject(HttpClient);
-
- get(id: string): Observable {
- return this.httpClient.get(`${environment.keepTrackApiUrl}/api/tv-shows/${id}`);
- }
-
- list(search?: string, currentPage?: number, pageSize?: number): Observable> {
- return this.httpClient.get>(`${environment.keepTrackApiUrl}/api/tv-shows?search=${search ?? ''}&page=${currentPage ?? 0}&pageSize=${pageSize ?? 50}`);
- }
-
- create(input: TvShow): Observable {
- return this.httpClient.post(`${environment.keepTrackApiUrl}/api/tv-shows`, input);
- }
-
- update(input: TvShow): Observable {
- delete input.isEditable;
- return this.httpClient.put(`${environment.keepTrackApiUrl}/api/tv-shows/${input.id}`, input);
- }
-
- delete(input: TvShow): Observable {
- return this.httpClient.delete(`${environment.keepTrackApiUrl}/api/tv-shows/${input.id}`);
- }
-}
diff --git a/samples/angular/src/app/backend/services/video-game.service.spec.ts b/samples/angular/src/app/backend/services/video-game.service.spec.ts
deleted file mode 100644
index 7ff50f0c..00000000
--- a/samples/angular/src/app/backend/services/video-game.service.spec.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
-import { VideoGameService } from './video-game.service';
-import { VideoGame } from '../types/video-game';
-import { environment } from 'src/environments/environment.dev';
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('VideoGameService', () => {
- let service: VideoGameService;
- let http: HttpTestingController;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-});
- http = TestBed.inject(HttpTestingController);
- service = TestBed.inject(VideoGameService);
- });
-
- afterAll(() => http.verify());
-
- it('should list', () => {
- // fake response
- const fake = [{ title: 'Final Fantasy VII' }, { title: 'Resident Evil' }] as Array;
-
- let actual: Array = [];
- service.list().subscribe((movies: Array) => actual = movies);
-
- http.expectOne(`${environment.keepTrackApiUrl}/api/video-games?search=&platform=&state=&page=0&pageSize=50`)
- .flush(fake);
-
- expect(actual).toEqual(fake, 'The `list` method should return an array of TV Shows wrapped in an Observable');
- });
-});
diff --git a/samples/angular/src/app/backend/services/video-game.service.ts b/samples/angular/src/app/backend/services/video-game.service.ts
deleted file mode 100644
index 01df6aae..00000000
--- a/samples/angular/src/app/backend/services/video-game.service.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Injectable, inject } from '@angular/core';
-import { Observable } from 'rxjs';
-import { environment } from 'src/environments/environment';
-import { VideoGame } from '../types/video-game';
-import { DataService } from './data.interface';
-
-@Injectable({
- providedIn: 'root'
-})
-export class VideoGameService implements DataService {
- private httpClient = inject(HttpClient);
-
- get(id: string): Observable {
- return this.httpClient.get(`${environment.keepTrackApiUrl}/api/video-games/${id}`);
- }
-
- list(search?: string, currentPage?: number, pageSize?: number, filter?: VideoGame): Observable> {
- return this.httpClient.get>(`${environment.keepTrackApiUrl}/api/video-games?search=${search ?? ''}&platform=${filter?.platform ?? ''}&state=${filter?.state ?? ''}&page=${currentPage ?? 0}&pageSize=${pageSize ?? 50}`);
- }
-
- create(input: VideoGame): Observable {
- return this.httpClient.post(`${environment.keepTrackApiUrl}/api/video-games`, input);
- }
-
- update(input: VideoGame): Observable {
- delete input.isEditable;
- if (!input.finishedAt) {
- delete input.finishedAt;
- }
- if (!input.releasedAt) {
- delete input.releasedAt;
- }
- return this.httpClient.put(`${environment.keepTrackApiUrl}/api/video-games/${input.id}`, input);
- }
-
- delete(input: VideoGame): Observable {
- return this.httpClient.delete(`${environment.keepTrackApiUrl}/api/video-games/${input.id}`);
- }
-}
diff --git a/samples/angular/src/app/backend/types/backend-data.d.ts b/samples/angular/src/app/backend/types/backend-data.d.ts
deleted file mode 100644
index 54ce53d7..00000000
--- a/samples/angular/src/app/backend/types/backend-data.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface BackendData {
- id?: string;
- isEditable?: boolean;
-}
diff --git a/samples/angular/src/app/backend/types/book.d.ts b/samples/angular/src/app/backend/types/book.d.ts
deleted file mode 100644
index 8849e68f..00000000
--- a/samples/angular/src/app/backend/types/book.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface Book extends BackendData {
- id?: string;
- title?: string;
- author?: string;
- series?: string;
- finishedAt?: Date;
- isEditable?: boolean;
-}
diff --git a/samples/angular/src/app/backend/types/car-history.d.ts b/samples/angular/src/app/backend/types/car-history.d.ts
deleted file mode 100644
index 28e2c670..00000000
--- a/samples/angular/src/app/backend/types/car-history.d.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface CarHistory extends BackendData {
- id?: string;
- carId?: string;
- historyDate?: Date;
- mileage?: number;
- action?: string;
- city?: string;
- longitude?: number;
- latitude?: number;
- fuelCategory?: string;
- fuelVolume?: number;
- fuelUnitPrice?: number;
- amount?: number;
- isFullTank?: boolean;
- deltaMileage?: number;
- lastRefuelHistoryId?: string;
- stationBrandName?: string;
-}
diff --git a/samples/angular/src/app/backend/types/car.d.ts b/samples/angular/src/app/backend/types/car.d.ts
deleted file mode 100644
index 0392e2e2..00000000
--- a/samples/angular/src/app/backend/types/car.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface Car extends BackendData {
- id?: string;
- name?: string;
-}
diff --git a/samples/angular/src/app/backend/types/movie.d.ts b/samples/angular/src/app/backend/types/movie.d.ts
deleted file mode 100644
index b179aaa1..00000000
--- a/samples/angular/src/app/backend/types/movie.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface Movie extends BackendData {
- id?: string;
- title?: string;
- year?: number;
- imdbPageId?: string;
- allocineId?: string;
- isEditable?: boolean;
-}
diff --git a/samples/angular/src/app/backend/types/tv-show.d.ts b/samples/angular/src/app/backend/types/tv-show.d.ts
deleted file mode 100644
index 512d92c1..00000000
--- a/samples/angular/src/app/backend/types/tv-show.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface TvShow extends BackendData {
- id?: string;
- title?: string;
- isEditable?: boolean;
-}
diff --git a/samples/angular/src/app/backend/types/video-game.d.ts b/samples/angular/src/app/backend/types/video-game.d.ts
deleted file mode 100644
index 800fcbe1..00000000
--- a/samples/angular/src/app/backend/types/video-game.d.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { BackendData } from "./backend-data";
-
-export interface VideoGame extends BackendData {
- id?: string;
- title?: string;
- platform?: string;
- releasedAt?: Date;
- state?: string;
- finishedAt?: Date;
- isEditable?: boolean;
-}
diff --git a/samples/angular/src/app/home/home.component.html b/samples/angular/src/app/home/home.component.html
deleted file mode 100644
index 59ba4617..00000000
--- a/samples/angular/src/app/home/home.component.html
+++ /dev/null
@@ -1,9 +0,0 @@
-Welcome!
-Welcome to your application to keep track of things. So far, we can manage:
-
- Books
- Cars
- Movies
- Video games
- TV shows
-
diff --git a/samples/angular/src/app/home/home.component.ts b/samples/angular/src/app/home/home.component.ts
deleted file mode 100644
index 4d02ae5d..00000000
--- a/samples/angular/src/app/home/home.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-import { FormsModule } from "@angular/forms";
-
-@Component({
- selector: 'app-home',
- imports: [FormsModule],
- templateUrl: './home.component.html'
-})
-export class HomeComponent {
-}
diff --git a/samples/angular/src/app/interceptors/auth.interceptor.ts b/samples/angular/src/app/interceptors/auth.interceptor.ts
deleted file mode 100644
index 5c7f6c35..00000000
--- a/samples/angular/src/app/interceptors/auth.interceptor.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { HttpInterceptorFn } from '@angular/common/http';
-import { inject } from '@angular/core';
-import { from, switchMap } from 'rxjs';
-import { Auth, idToken } from '@angular/fire/auth';
-import { environment } from "../../environments/environment";
-
-export const authInterceptor: HttpInterceptorFn = (req, next) => {
- if (!req.url.startsWith(environment.keepTrackApiUrl)) {
- return next(req);
- }
-
- return from(idToken(inject(Auth))).pipe(
- switchMap(token => {
- if (token) {
- // eslint-disable-next-line @typescript-eslint/naming-convention
- return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
- }
- return next(req);
- })
- );
-};
diff --git a/samples/angular/src/app/inventory/base/data.component.ts b/samples/angular/src/app/inventory/base/data.component.ts
deleted file mode 100644
index 2c8258c5..00000000
--- a/samples/angular/src/app/inventory/base/data.component.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import {Directive, inject, OnDestroy, OnInit} from '@angular/core';
-import { Subscription } from 'rxjs';
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-import { DataService } from 'src/app/backend/services/data.interface';
-import { BackendData } from 'src/app/backend/types/backend-data';
-
-@Directive()
-export abstract class DataComponent implements OnInit, OnDestroy {
- protected abstract readonly dataService: DataService;
- private authenticateService = inject(AuthenticateService);
- userEventsSubscription: Subscription | undefined;
- items: Array | undefined;
- currentPage = 1;
- pageSize = 50;
-
- ngOnInit() {
- this.userEventsSubscription = this.authenticateService.authState$.subscribe(() => {
- this.load('', 1);
- });
- }
-
- ngOnDestroy() {
- if (this.userEventsSubscription) {
- this.userEventsSubscription.unsubscribe();
- }
- }
-
- load(search: string, currentPage: number, filter?: T) {
- this.currentPage = currentPage;
- this.dataService.list(search, currentPage - 1, this.pageSize, filter).subscribe({
- next: (items) => this.items = items,
- error: (error) => console.warn(error)
- });
- }
-
- updateCurrentPage(event: Event, newValue: number, search?: string) {
- event.preventDefault();
- if (newValue > 0) {
- this.load(search ?? '', newValue);
- }
- }
-
- create(item: T) {
- this.dataService.create(item).subscribe(created => {
- this.items?.push(created);
- this.resetInputFields();
- });
- }
-
- abstract resetInputFields(): void;
-
- startEditing(item: T) {
- item.isEditable = true;
- }
-
- cancel(item: T) {
- item.isEditable = false;
-
- if (!item.id) {
- return;
- }
-
- this.dataService.get(item.id).subscribe(existing => {
- item = existing;
- });
- }
-
- update(item: T) {
- this.dataService.update(item)
- .subscribe(() => item.isEditable = false);
- }
-
- delete(item: T) {
- this.dataService.delete(item)
- .subscribe(() => {
- if (this.items) {
- this.items.splice(this.items.findIndex(x => x.id === item.id), 1);
- }
- });
- }
-}
diff --git a/samples/angular/src/app/inventory/book/book.component.html b/samples/angular/src/app/inventory/book/book.component.html
deleted file mode 100644
index e46a1b60..00000000
--- a/samples/angular/src/app/inventory/book/book.component.html
+++ /dev/null
@@ -1,129 +0,0 @@
-Books
-
-@if (!items) {
- Loading...
-}
-
-
-
-
-
-@if (items) {
-
-
-
-}
-
-@if (items) {
-
-
-
-
-
-
- Title
-
-
-
- Author
-
-
-
- Series
-
-
-
- Action
-
- Validate
-
-
-
-
-
-}
diff --git a/samples/angular/src/app/inventory/book/book.component.spec.ts b/samples/angular/src/app/inventory/book/book.component.spec.ts
deleted file mode 100644
index 6145e221..00000000
--- a/samples/angular/src/app/inventory/book/book.component.spec.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { TestBed, waitForAsync } from '@angular/core/testing';
-import { User } from '@angular/fire/auth';
-import { Observable } from 'rxjs';
-
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-import { BookComponent } from './book.component';
-import { BookService } from 'src/app/backend/services/book.service';
-
-describe('BookComponent', () => {
- let fakeBookService: jasmine.SpyObj;
- let fakeAuthenticateService: jasmine.SpyObj;
-
- beforeEach(() => {
- const emptyUser = new Observable();
- fakeBookService = jasmine.createSpyObj('BookService', ['list']);
- fakeAuthenticateService = jasmine.createSpyObj('AuthenticateService', [], { 'authState$': emptyUser });
-
- TestBed.configureTestingModule({
- imports: [],
- providers: [
- { provide: BookService, useValue: fakeBookService },
- { provide: AuthenticateService, useValue: fakeAuthenticateService }
- ]
- });
- });
-
- it('should listen to userEvents in ngOnInit', waitForAsync(() => {
- const fixture = TestBed.createComponent(BookComponent);
- const component = fixture.componentInstance;
- component.ngOnInit();
- }));
-});
diff --git a/samples/angular/src/app/inventory/book/book.component.ts b/samples/angular/src/app/inventory/book/book.component.ts
deleted file mode 100644
index 7295ec81..00000000
--- a/samples/angular/src/app/inventory/book/book.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component, OnInit, OnDestroy, ElementRef, ViewChild, inject } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { BookService } from 'src/app/backend/services/book.service';
-import { Book } from 'src/app/backend/types/book';
-import { DataComponent } from '../base/data.component';
-
-@Component({
- selector: 'app-book',
- imports: [CommonModule, FormsModule],
- templateUrl: './book.component.html'
-})
-export class BookComponent extends DataComponent implements OnInit, OnDestroy {
- protected override readonly dataService = inject(BookService);
-
- @ViewChild('titleInput') titleInput = {} as ElementRef;
- @ViewChild('authorInput') authorInput = {} as ElementRef;
- @ViewChild('seriesInput') seriesInput = {} as ElementRef;
- @ViewChild('searchInput') searchInput = {} as ElementRef;
-
- resetInputFields() {
- this.titleInput.nativeElement.value = '';
- this.authorInput.nativeElement.value = '';
- this.seriesInput.nativeElement.value = '';
- }
-}
diff --git a/samples/angular/src/app/inventory/car/car.component.html b/samples/angular/src/app/inventory/car/car.component.html
deleted file mode 100644
index 7608405e..00000000
--- a/samples/angular/src/app/inventory/car/car.component.html
+++ /dev/null
@@ -1 +0,0 @@
-car works!
diff --git a/samples/angular/src/app/inventory/car/car.component.spec.ts b/samples/angular/src/app/inventory/car/car.component.spec.ts
deleted file mode 100644
index ce22e0cb..00000000
--- a/samples/angular/src/app/inventory/car/car.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-
-import { CarComponent } from './car.component';
-
-describe('CarComponent', () => {
- let component: CarComponent;
- let fixture: ComponentFixture;
-
- beforeEach(waitForAsync(() => {
- TestBed.configureTestingModule({
- declarations: [ CarComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(CarComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/samples/angular/src/app/inventory/car/car.component.ts b/samples/angular/src/app/inventory/car/car.component.ts
deleted file mode 100644
index e3f9e46a..00000000
--- a/samples/angular/src/app/inventory/car/car.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-import { FormsModule } from "@angular/forms";
-
-@Component({
- selector: 'app-car',
- imports: [FormsModule],
- templateUrl: './car.component.html'
-})
-export class CarComponent {
-}
diff --git a/samples/angular/src/app/inventory/inventory.module.ts b/samples/angular/src/app/inventory/inventory.module.ts
deleted file mode 100644
index ede9e107..00000000
--- a/samples/angular/src/app/inventory/inventory.module.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { MovieComponent } from './movie/movie.component';
-import { BookComponent } from './book/book.component';
-import { CarComponent } from './car/car.component';
-import { VideoGameComponent } from './video-game/video-game.component';
-import { TvShowComponent } from './tv-show/tv-show.component';
-import { FormsModule } from '@angular/forms';
-
-@NgModule({
- declarations: [
- MovieComponent,
- BookComponent,
- CarComponent,
- VideoGameComponent,
- TvShowComponent
- ],
- imports: [
- CommonModule,
- FormsModule
- ]
-})
-export class InventoryModule { }
diff --git a/samples/angular/src/app/inventory/movie/movie.component.html b/samples/angular/src/app/inventory/movie/movie.component.html
deleted file mode 100644
index 85011b6e..00000000
--- a/samples/angular/src/app/inventory/movie/movie.component.html
+++ /dev/null
@@ -1,104 +0,0 @@
-Movies
-
-@if (!items) {
- Loading...
-}
-
-
-
-
-
-@if (items) {
-
-
-
-
-
-
-
-
-
- Title
-
-
-
- Year
-
-
-
- Action
-
- Validate
-
-
-
-
-
-}
diff --git a/samples/angular/src/app/inventory/movie/movie.component.spec.ts b/samples/angular/src/app/inventory/movie/movie.component.spec.ts
deleted file mode 100644
index 008b1aff..00000000
--- a/samples/angular/src/app/inventory/movie/movie.component.spec.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { TestBed, waitForAsync } from '@angular/core/testing';
-import { User } from '@angular/fire/auth';
-import { Observable } from 'rxjs';
-
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-import { MovieComponent } from './movie.component';
-import { MovieService } from 'src/app/backend/services/movie.service';
-
-describe('MovieComponent', () => {
- let fakeMovieService: jasmine.SpyObj;
- let fakeAuthenticateService: jasmine.SpyObj;
-
- beforeEach(() => {
- const emptyUser = new Observable();
- fakeMovieService = jasmine.createSpyObj('MovieService', ['list']);
- fakeAuthenticateService = jasmine.createSpyObj('AuthenticateService', [], { 'authState$': emptyUser });
-
- TestBed.configureTestingModule({
- imports: [],
- providers: [
- { provide: MovieService, useValue: fakeMovieService },
- { provide: AuthenticateService, useValue: fakeAuthenticateService }
- ]
- });
- });
-
- it('should listen to userEvents in ngOnInit', waitForAsync(() => {
- const fixture = TestBed.createComponent(MovieComponent);
- const component = fixture.componentInstance;
- component.ngOnInit();
- }));
-});
diff --git a/samples/angular/src/app/inventory/movie/movie.component.ts b/samples/angular/src/app/inventory/movie/movie.component.ts
deleted file mode 100644
index cb0f3ed0..00000000
--- a/samples/angular/src/app/inventory/movie/movie.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component, OnInit, OnDestroy, ViewChild, ElementRef, inject } from '@angular/core';
-import { CommonModule } from "@angular/common";
-import { FormsModule } from "@angular/forms";
-import { MovieService } from 'src/app/backend/services/movie.service';
-import { Movie } from 'src/app/backend/types/movie';
-import { DataComponent } from '../base/data.component';
-
-@Component({
- selector: 'app-movie',
- imports: [CommonModule, FormsModule],
- templateUrl: './movie.component.html'
-})
-export class MovieComponent extends DataComponent implements OnInit, OnDestroy {
- protected override readonly dataService = inject(MovieService);
-
- @ViewChild('titleInput') titleInput= {} as ElementRef;
- @ViewChild('yearInput') yearInput= {} as ElementRef;
-
- resetInputFields() {
- this.titleInput.nativeElement.value = '';
- this.yearInput.nativeElement.value = '';
- }
-}
diff --git a/samples/angular/src/app/inventory/tv-show/tv-show.component.html b/samples/angular/src/app/inventory/tv-show/tv-show.component.html
deleted file mode 100644
index ae3298fc..00000000
--- a/samples/angular/src/app/inventory/tv-show/tv-show.component.html
+++ /dev/null
@@ -1,59 +0,0 @@
-TV shows
-
-@if (!items) {
- Loading...
-}
-
-
-
-@if (items) {
-
-
-
- Title
- Actions
-
-
-
- @for (tvShow of items; track tvShow) {
-
- {{ tvShow.title }}
-
-
- }
-
-
-}
-
-@if (items) {
-
-
-
-
-
-
-
-
- Title
-
-
-
- Action
-
- Validate
-
-
-
-
-}
diff --git a/samples/angular/src/app/inventory/tv-show/tv-show.component.spec.ts b/samples/angular/src/app/inventory/tv-show/tv-show.component.spec.ts
deleted file mode 100644
index 9c5ab05f..00000000
--- a/samples/angular/src/app/inventory/tv-show/tv-show.component.spec.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { TestBed, waitForAsync } from '@angular/core/testing';
-import { User } from '@angular/fire/auth';
-import { Observable } from 'rxjs';
-
-import { AppModule } from 'src/app/app.module';
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-import { TvShowComponent } from './tv-show.component';
-import { TvShowService } from 'src/app/backend/services/tv-show.service';
-
-describe('TvShowComponent', () => {
- let fakeTvShowService: jasmine.SpyObj;
- let fakeAuthenticateService: jasmine.SpyObj;
-
- beforeEach(() => {
- const emptyUser = new Observable();
- fakeTvShowService = jasmine.createSpyObj('TvShowService', ['list']);
- fakeAuthenticateService = jasmine.createSpyObj('AuthenticateService', [], { 'authState$': emptyUser });
-
- TestBed.configureTestingModule({
- imports: [AppModule],
- providers: [
- { provide: TvShowService, useValue: fakeTvShowService },
- { provide: AuthenticateService, useValue: fakeAuthenticateService }
- ]
- });
- });
-
- it('should listen to userEvents in ngOnInit', waitForAsync(() => {
- const fixture = TestBed.createComponent(TvShowComponent);
- const component = fixture.componentInstance;
- component.ngOnInit();
- }));
-});
diff --git a/samples/angular/src/app/inventory/tv-show/tv-show.component.ts b/samples/angular/src/app/inventory/tv-show/tv-show.component.ts
deleted file mode 100644
index bb44ed05..00000000
--- a/samples/angular/src/app/inventory/tv-show/tv-show.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component, ElementRef, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
-import { CommonModule } from "@angular/common";
-import { FormsModule } from "@angular/forms";
-import { TvShowService } from 'src/app/backend/services/tv-show.service';
-import { TvShow } from 'src/app/backend/types/tv-show';
-import { DataComponent } from '../base/data.component';
-
-@Component({
- selector: 'app-tv-show',
- imports: [CommonModule, FormsModule],
- templateUrl: './tv-show.component.html'
-})
-export class TvShowComponent extends DataComponent implements OnInit, OnDestroy {
- protected override readonly dataService = inject(TvShowService);
-
- @ViewChild('titleInput') titleInput= {} as ElementRef;
-
- resetInputFields() {
- this.titleInput.nativeElement.value = '';
- }
-}
diff --git a/samples/angular/src/app/inventory/video-game/video-game.component.html b/samples/angular/src/app/inventory/video-game/video-game.component.html
deleted file mode 100644
index 7f8694a0..00000000
--- a/samples/angular/src/app/inventory/video-game/video-game.component.html
+++ /dev/null
@@ -1,175 +0,0 @@
-Video games
-
-@if (!items) {
- Loading...
-}
-
-
-
-
-
-@if (items) {
-
-
-
-}
-
-@if (items) {
-
-
-
-
-
-
- Title
-
-
-
- Platform
-
-
- Xbox Series X
- PS5
- PC
- Xbox One X
- PS4
- WII
- Xbox 360
- PS2
- PS1
-
-
-
- State
-
-
- Available
- Completed
- To resume
- Current
- On-hold
-
-
-
- Validate
-
- Add
-
-
-
-
-
-}
diff --git a/samples/angular/src/app/inventory/video-game/video-game.component.spec.ts b/samples/angular/src/app/inventory/video-game/video-game.component.spec.ts
deleted file mode 100644
index d9ce95a6..00000000
--- a/samples/angular/src/app/inventory/video-game/video-game.component.spec.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { TestBed, waitForAsync } from '@angular/core/testing';
-import { User } from '@angular/fire/auth';
-import { Observable } from 'rxjs';
-
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-import { VideoGameComponent } from './video-game.component';
-import { VideoGameService } from 'src/app/backend/services/video-game.service';
-
-describe('VideoGameComponent', () => {
- let fakeVideoGameService: jasmine.SpyObj;
- let fakeAuthenticateService: jasmine.SpyObj;
-
- beforeEach(() => {
- const emptyUser = new Observable();
- fakeVideoGameService = jasmine.createSpyObj('VideoGameService', ['list']);
- fakeAuthenticateService = jasmine.createSpyObj('AuthenticateService', [], { 'authState$': emptyUser });
-
- TestBed.configureTestingModule({
- imports: [],
- providers: [
- { provide: VideoGameService, useValue: fakeVideoGameService },
- { provide: AuthenticateService, useValue: fakeAuthenticateService }
- ]
- });
- });
-
- it('should listen to userEvents in ngOnInit', waitForAsync(() => {
- const fixture = TestBed.createComponent(VideoGameComponent);
- const component = fixture.componentInstance;
- component.ngOnInit();
- }));
-});
diff --git a/samples/angular/src/app/inventory/video-game/video-game.component.ts b/samples/angular/src/app/inventory/video-game/video-game.component.ts
deleted file mode 100644
index b5047879..00000000
--- a/samples/angular/src/app/inventory/video-game/video-game.component.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Component, ElementRef, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
-import { CommonModule } from "@angular/common";
-import { FormsModule } from "@angular/forms";
-import { VideoGameService } from 'src/app/backend/services/video-game.service';
-import { VideoGame } from 'src/app/backend/types/video-game';
-import { DataComponent } from '../base/data.component';
-
-@Component({
- selector: 'app-video-game',
- imports: [CommonModule, FormsModule],
- templateUrl: './video-game.component.html'
-})
-export class VideoGameComponent extends DataComponent implements OnInit, OnDestroy {
- protected override readonly dataService = inject(VideoGameService);
-
- @ViewChild('titleInput') titleInput = {} as ElementRef;
- @ViewChild('platformInput') platformInput= {} as ElementRef;
- @ViewChild('stateInput') stateInput= {} as ElementRef;
-
- resetInputFields() {
- this.titleInput.nativeElement.value = '';
- this.platformInput.nativeElement.value = '';
- this.stateInput.nativeElement.value = '';
- }
-}
diff --git a/samples/angular/src/app/layout/header/header.component.css b/samples/angular/src/app/layout/header/header.component.css
deleted file mode 100644
index 10389ef9..00000000
--- a/samples/angular/src/app/layout/header/header.component.css
+++ /dev/null
@@ -1,18 +0,0 @@
-a.navbar-brand {
- white-space: normal;
- text-align: center;
- word-break: break-all;
-}
-
-html {
- font-size: 14px;
-}
-@media (min-width: 768px) {
- html {
- font-size: 16px;
- }
-}
-
-.box-shadow {
- box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
-}
diff --git a/samples/angular/src/app/layout/header/header.component.html b/samples/angular/src/app/layout/header/header.component.html
deleted file mode 100644
index c51e253a..00000000
--- a/samples/angular/src/app/layout/header/header.component.html
+++ /dev/null
@@ -1,45 +0,0 @@
-
diff --git a/samples/angular/src/app/layout/header/header.component.ts b/samples/angular/src/app/layout/header/header.component.ts
deleted file mode 100644
index fcb40801..00000000
--- a/samples/angular/src/app/layout/header/header.component.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Component, OnInit, OnDestroy, inject } from '@angular/core';
-import { User } from '@angular/fire/auth';
-import { Router, RouterLink, RouterLinkActive } from '@angular/router';
-import { CommonModule } from "@angular/common";
-import { Subscription } from 'rxjs';
-import { AuthenticateService } from 'src/app/user/services/authenticate.service';
-
-@Component({
- selector: 'app-header',
- imports: [
- CommonModule,
- RouterLink,
- RouterLinkActive
- ],
- templateUrl: './header.component.html',
- styleUrls: ['./header.component.css']
-})
-export class HeaderComponent implements OnInit, OnDestroy {
- private authenticateService = inject(AuthenticateService);
- private router = inject(Router);
-
- isExpanded = false;
-
- user = null as User | null;
- userEventsSubscription: Subscription | undefined;
-
- ngOnInit() {
- this.userEventsSubscription = this.authenticateService.authState$.subscribe({
- next: (user: User | null) => this.user = user,
- error: (error) => console.log(error)
- });
- }
-
- ngOnDestroy() {
- if (this.userEventsSubscription) {
- this.userEventsSubscription.unsubscribe();
- }
- }
-
- async logout(event: Event) {
- event.preventDefault();
- await this.authenticateService.logout();
- await this.router.navigate(['/login']);
- }
-
- collapse() {
- this.isExpanded = false;
- }
-
- toggle() {
- this.isExpanded = !this.isExpanded;
- }
-
-}
diff --git a/samples/angular/src/app/layout/layout.module.ts b/samples/angular/src/app/layout/layout.module.ts
deleted file mode 100644
index 10fcaf58..00000000
--- a/samples/angular/src/app/layout/layout.module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-@NgModule({
- declarations: [ ],
- imports: [
- CommonModule,
- RouterModule
- ]
-})
-export class LayoutModule { }
diff --git a/samples/angular/src/app/user/login/login.component.html b/samples/angular/src/app/user/login/login.component.html
deleted file mode 100644
index 2287fd8e..00000000
--- a/samples/angular/src/app/user/login/login.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Login with GitHub
-
diff --git a/samples/angular/src/app/user/login/login.component.spec.ts b/samples/angular/src/app/user/login/login.component.spec.ts
deleted file mode 100644
index 41140adf..00000000
--- a/samples/angular/src/app/user/login/login.component.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { LoginComponent } from './login.component';
-import { AuthenticateService } from '../services/authenticate.service';
-
-describe('LoginComponent', () => {
-
- const fakeAuthenticateService = jasmine.createSpyObj('AuthenticateService', ['signInWithGitHub']);
-
- beforeEach(() => TestBed.configureTestingModule({
- imports: [],
- providers: [
- { provide: AuthenticateService, useValue: fakeAuthenticateService }
- ]
- }));
-
- beforeEach(() => {
- fakeAuthenticateService.signInWithGitHub.calls.reset();
- });
-
- it('should have a title', () => {
- const fixture = TestBed.createComponent(LoginComponent);
-
- // when we trigger the change detection
- fixture.detectChanges();
-
- // then we should have a title
- const element = fixture.nativeElement;
- expect(element.querySelector('button')).not.toBeNull('The template should have a `h1` tag');
- });
-});
diff --git a/samples/angular/src/app/user/login/login.component.ts b/samples/angular/src/app/user/login/login.component.ts
deleted file mode 100644
index b3e3101d..00000000
--- a/samples/angular/src/app/user/login/login.component.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Component, inject } from '@angular/core';
-import { Router } from '@angular/router';
-import { AuthenticateService } from '../services/authenticate.service';
-
-@Component({
- selector: 'app-login',
- templateUrl: './login.component.html'
-})
-export class LoginComponent {
- private authenticateService = inject(AuthenticateService);
- private router = inject(Router);
-
- async signInWithGitHub() {
- await this.authenticateService.signInWithGitHub();
- await this.router.navigate(['/']);
- }
-}
diff --git a/samples/angular/src/app/user/models/user.model.ts b/samples/angular/src/app/user/models/user.model.ts
deleted file mode 100644
index 88056c0a..00000000
--- a/samples/angular/src/app/user/models/user.model.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface UserModel {
- username: string;
- token?: string;
-}
diff --git a/samples/angular/src/app/user/services/authenticate.service.spec.ts b/samples/angular/src/app/user/services/authenticate.service.spec.ts
deleted file mode 100644
index 2005ee7c..00000000
--- a/samples/angular/src/app/user/services/authenticate.service.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
-import { AngularFireModule } from '@angular/fire/compat';
-import { AngularFireAuthModule } from '@angular/fire/compat/auth';
-import { AuthenticateService } from './authenticate.service';
-import { environment } from 'src/environments/environment.dev';
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('AuthenticateService', () => {
- let authenticateService: AuthenticateService;
- let http: HttpTestingController;
-
- beforeEach(() => TestBed.configureTestingModule({
- imports: [
- AngularFireModule.initializeApp(environment.firebase),
- AngularFireAuthModule],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-}));
-
- beforeEach(() => {
- http = TestBed.inject(HttpTestingController);
- authenticateService = TestBed.inject(AuthenticateService);
- });
-
- afterAll(() => http.verify());
-
- it('should logout', () => {
- authenticateService.logout();
- });
-});
diff --git a/samples/angular/src/app/user/services/authenticate.service.ts b/samples/angular/src/app/user/services/authenticate.service.ts
deleted file mode 100644
index bfb694b2..00000000
--- a/samples/angular/src/app/user/services/authenticate.service.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Injectable, inject } from '@angular/core';
-import { Auth, GithubAuthProvider, authState, signInWithPopup } from '@angular/fire/auth';
-
-// see https://github.com/angular/angularfire/blob/main/docs/auth.md
-@Injectable({
- providedIn: 'root'
-})
-export class AuthenticateService {
- private auth: Auth = inject(Auth);
- authState$ = authState(this.auth);
-
- // see https://firebase.google.com/docs/auth/web/github-auth
- async signInWithGitHub() {
- try {
- await signInWithPopup(this.auth, new GithubAuthProvider());
- } catch (error) {
- console.log(error);
- }
- }
-
- async logout() {
- await this.auth.signOut();
- }
-}
diff --git a/samples/angular/src/app/user/user.module.ts b/samples/angular/src/app/user/user.module.ts
deleted file mode 100644
index 06108402..00000000
--- a/samples/angular/src/app/user/user.module.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-import { AuthenticateService } from './services/authenticate.service';
-import { LoginComponent } from './login/login.component';
-
-@NgModule({
- declarations: [LoginComponent],
- imports: [
- CommonModule
- ],
- providers: [
- AuthenticateService
- ]
-})
-export class UserModule { }
diff --git a/samples/angular/src/assets/.gitkeep b/samples/angular/src/assets/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/samples/angular/src/environments/environment.ts b/samples/angular/src/environments/environment.ts
deleted file mode 100644
index b25fe8c1..00000000
--- a/samples/angular/src/environments/environment.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file can be replaced during build by using the `fileReplacements` array.
-// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
-// The list of file replacements can be found in `angular.json`.
-
-export const environment = {
- production: false,
- keepTrackApiUrl: '',
- firebase: {
- apiKey: '',
- authDomain: '',
- databaseURL: '',
- projectId: '',
- storageBucket: '',
- messagingSenderId: '',
- appId: '',
- measurementId: ''
- }
-};
-
-/*
- * In development mode, to ignore zone related error stack frames such as
- * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
- * import the following file, but please comment it out in production mode
- * because it will have performance impact when throw error
- */
-// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
diff --git a/samples/angular/src/favicon.ico b/samples/angular/src/favicon.ico
deleted file mode 100644
index 997406ad..00000000
Binary files a/samples/angular/src/favicon.ico and /dev/null differ
diff --git a/samples/angular/src/index.html b/samples/angular/src/index.html
deleted file mode 100644
index a0ac61e7..00000000
--- a/samples/angular/src/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- Keeptrack
-
-
-
-
-
- Loading...
-
-
diff --git a/samples/angular/src/main.ts b/samples/angular/src/main.ts
deleted file mode 100644
index 5888d1ae..00000000
--- a/samples/angular/src/main.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { enableProdMode, provideZoneChangeDetection } from '@angular/core';
-import { bootstrapApplication } from '@angular/platform-browser';
-import { provideRouter } from '@angular/router';
-import { provideHttpClient, withInterceptors } from '@angular/common/http';
-import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
-import { provideAuth, getAuth } from '@angular/fire/auth';
-
-import { AppComponent } from './app/app.component';
-import { routes } from './app/app.routes';
-import { authInterceptor } from "./app/interceptors/auth.interceptor";
-import { environment } from './environments/environment';
-
-if (environment.production) {
- enableProdMode();
-}
-
-bootstrapApplication(AppComponent, {
- providers: [
- provideZoneChangeDetection(),provideRouter(routes),
- provideHttpClient(withInterceptors([authInterceptor])),
- provideFirebaseApp(() => initializeApp(environment.firebase)),
- provideAuth(() => getAuth()),
- provideHttpClient(
- withInterceptors([authInterceptor])
- )
- ]
-}).catch(err => console.error(err));
diff --git a/samples/angular/src/styles.css b/samples/angular/src/styles.css
deleted file mode 100644
index 9e5d916d..00000000
--- a/samples/angular/src/styles.css
+++ /dev/null
@@ -1,18 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
-
-@import 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css';
-
-/* Provide sufficient contrast against white background */
-a {
- color: #0366d6;
-}
-
-code {
- color: #e01a76;
-}
-
-.btn-primary {
- color: #fff;
- background-color: #1b6ec2;
- border-color: #1861ac;
-}
diff --git a/samples/angular/src/web.config b/samples/angular/src/web.config
deleted file mode 100644
index 5ec2c9d6..00000000
--- a/samples/angular/src/web.config
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/angular/tsconfig.app.json b/samples/angular/tsconfig.app.json
deleted file mode 100644
index 7d7c716d..00000000
--- a/samples/angular/tsconfig.app.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "outDir": "./out-tsc/app",
- "types": []
- },
- "files": [
- "src/main.ts"
- ],
- "include": [
- "src/**/*.d.ts"
- ]
-}
diff --git a/samples/angular/tsconfig.json b/samples/angular/tsconfig.json
deleted file mode 100644
index 213dba09..00000000
--- a/samples/angular/tsconfig.json
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Ref. https://angular.io/config/tsconfig */
-{
- "compileOnSave": false,
- "compilerOptions": {
- "baseUrl": "./",
- "outDir": "./dist/out-tsc",
- "forceConsistentCasingInFileNames": true,
- "esModuleInterop": true,
- "strict": true,
- "noImplicitOverride": true,
- "noPropertyAccessFromIndexSignature": true,
- "noImplicitReturns": true,
- "noFallthroughCasesInSwitch": true,
- "noUnusedLocals": true,
- "sourceMap": true,
- "declaration": false,
- "experimentalDecorators": true,
- "moduleResolution": "bundler",
- "importHelpers": true,
- "target": "ES2022",
- "module": "ES2022",
- "useDefineForClassFields": false
- },
- "angularCompilerOptions": {
- "enableI18nLegacyMessageIdFormat": false,
- "strictInjectionParameters": true,
- "strictInputAccessModifiers": true,
- "strictTemplates": true,
- "extendedDiagnostics": {
- "defaultCategory": "error"
- }
- }
-}
diff --git a/samples/angular/tsconfig.spec.json b/samples/angular/tsconfig.spec.json
deleted file mode 100644
index b18619fd..00000000
--- a/samples/angular/tsconfig.spec.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "outDir": "./out-tsc/spec",
- "types": [
- "jasmine"
- ]
- },
- "include": [
- "src/**/*.spec.ts",
- "src/**/*.d.ts"
- ]
-}
diff --git a/scripts/mongodb-create-index.js b/scripts/mongodb-create-index.js
index f56043ea..dba3c20b 100644
--- a/scripts/mongodb-create-index.js
+++ b/scripts/mongodb-create-index.js
@@ -26,6 +26,8 @@ ensureIndex(db.house, { owner_id: 1 }, { name: "house_owner" });
ensureIndex(db.house_history, { owner_id: 1 }, { name: "house_history_owner" });
ensureIndex(db.health_profile, { owner_id: 1 }, { name: "health_profile_owner" });
ensureIndex(db.health_record, { owner_id: 1 }, { name: "health_record_owner" });
+ensureIndex(db.collectible, { owner_id: 1 }, { name: "collectible_owner" });
+ensureIndex(db.gear, { owner_id: 1 }, { name: "gear_owner" });
ensureIndex(db.movie, { owner_id: 1 }, { name: "movie_owner" });
ensureIndex(db.tvshow, { owner_id: 1 }, { name: "tvshow_owner" });
ensureIndex(db.videogame, { owner_id: 1 }, { name: "videogame_owner" });
@@ -91,6 +93,16 @@ ensureIndex(
{ owner_id: 1, is_favorite: 1 },
{ name: "book_favorite", partialFilterExpression: { is_favorite: true } }
);
+ensureIndex(
+ db.collectible,
+ { owner_id: 1, is_favorite: 1 },
+ { name: "collectible_favorite", partialFilterExpression: { is_favorite: true } }
+);
+ensureIndex(
+ db.gear,
+ { owner_id: 1, is_favorite: 1 },
+ { name: "gear_favorite", partialFilterExpression: { is_favorite: true } }
+);
// movie / tvshow / book / videogame: same sparse-flag partial-index rationale as the favorite/want-to-watch
// indexes above, for the is_wishlisted flag. VideoGame has no favorite/want-to-watch flags, so this is its
@@ -147,6 +159,16 @@ ensureIndex(
{ owner_id: 1 },
{ name: "videogame_owned", partialFilterExpression: { "platforms.0": { $exists: true } } }
);
+ensureIndex(
+ db.collectible,
+ { owner_id: 1 },
+ { name: "collectible_owned", partialFilterExpression: { "owned_versions.0": { $exists: true } } }
+);
+ensureIndex(
+ db.gear,
+ { owner_id: 1 },
+ { name: "gear_owned", partialFilterExpression: { "owned_versions.0": { $exists: true } } }
+);
// background_job: transient job-progress documents (TV Time import, reference-data "sync now") polled by
// job id - MongoDB-backed (instead of in-memory) so any WebApi replica can answer a poll. TTL cleanup
@@ -248,3 +270,8 @@ ensureIndex(
{ "external_ids.discogs": 1 },
{ name: "album_reference_discogs_id", unique: true, partialFilterExpression: { "external_ids.discogs": { $exists: true } } }
);
+
+// user_preference: exactly one document per owner (upserted by owner_id, never listed) - the unique
+// index is what actually guarantees that, the same way the application-level upsert-by-owner-id logic in
+// UserPreferencesRepository is only "supposed to" prevent a second document.
+ensureIndex(db.user_preference, { owner_id: 1 }, { name: "user_preference_owner", unique: true });
diff --git a/src/BlazorApp/Components/Account/Pages/Manage.razor b/src/BlazorApp/Components/Account/Pages/Manage.razor
index fc74f1a0..162b95cf 100644
--- a/src/BlazorApp/Components/Account/Pages/Manage.razor
+++ b/src/BlazorApp/Components/Account/Pages/Manage.razor
@@ -1,5 +1,6 @@
-@page "/account/manage"
+@page "/account/manage"
@attribute [Authorize]
+@inject UserPreferencesState PreferencesState
Manage
@@ -10,3 +11,38 @@
+
+@if (_preferences is not null)
+{
+ Preferences
+
+ SetFeatureAsync((f, v) => f.ShowChasseAuxLivresLink = v, (bool)e.Value!)"/>
+
+ Show chasse-aux-livres.fr link on book pages
+
+
+
+ SetFeatureAsync((f, v) => f.ShowAmazonProductLink = v, (bool)e.Value!)"/>
+
+ Show Amazon product link on owned copies imported from Amazon
+
+
+}
+
+@code {
+ private UserPreferencesDto? _preferences;
+
+ protected override async Task OnInitializedAsync()
+ {
+ _preferences = await PreferencesState.GetAsync();
+ }
+
+ private async Task SetFeatureAsync(Action apply, bool value)
+ {
+ if (_preferences is null) return;
+ apply(_preferences.Features, value);
+ await PreferencesState.UpdateAsync(_preferences);
+ }
+}
diff --git a/src/BlazorApp/Components/Account/UserPreferencesApiClient.cs b/src/BlazorApp/Components/Account/UserPreferencesApiClient.cs
new file mode 100644
index 00000000..73fd5964
--- /dev/null
+++ b/src/BlazorApp/Components/Account/UserPreferencesApiClient.cs
@@ -0,0 +1,15 @@
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Account;
+
+public sealed class UserPreferencesApiClient(HttpClient http)
+{
+ public async Task GetAsync()
+ {
+ var result = await http.GetFromJsonAsync("/api/user-preferences");
+ return result ?? new UserPreferencesDto();
+ }
+
+ public async Task UpdateAsync(UserPreferencesDto dto) =>
+ (await http.PutAsJsonAsync("/api/user-preferences", dto)).EnsureSuccessStatusCode();
+}
diff --git a/src/BlazorApp/Components/Account/UserPreferencesState.cs b/src/BlazorApp/Components/Account/UserPreferencesState.cs
new file mode 100644
index 00000000..61db8cba
--- /dev/null
+++ b/src/BlazorApp/Components/Account/UserPreferencesState.cs
@@ -0,0 +1,25 @@
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Account;
+
+///
+/// Circuit-scoped cache of the caller's own (registered AddScoped ).
+/// Preference checks now happen in more than one place per page (e.g. every owned copy's
+/// OwnedVersionFields instance on a detail page), so each consumer fetching independently would fire
+/// one HTTP request per instance; this fetches once per circuit and every consumer shares the same
+/// in-flight/completed instead. (only called from
+/// Manage.razor) refreshes the cache immediately, so a page navigated to afterward sees the new value
+/// without a second round trip.
+///
+public sealed class UserPreferencesState(UserPreferencesApiClient api)
+{
+ private Task? _cached;
+
+ public Task GetAsync() => _cached ??= api.GetAsync();
+
+ public async Task UpdateAsync(UserPreferencesDto dto)
+ {
+ await api.UpdateAsync(dto);
+ _cached = Task.FromResult(dto);
+ }
+}
diff --git a/src/BlazorApp/Components/Import/AmazonImportApiClient.cs b/src/BlazorApp/Components/Import/AmazonImportApiClient.cs
new file mode 100644
index 00000000..5571198c
--- /dev/null
+++ b/src/BlazorApp/Components/Import/AmazonImportApiClient.cs
@@ -0,0 +1,28 @@
+using System.Net.Http.Headers;
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Import;
+
+public sealed class AmazonImportApiClient(HttpClient http)
+{
+ public async Task> PreviewAsync(Stream csvStream, string fileName)
+ {
+ using var content = new MultipartFormDataContent();
+ using var fileContent = new StreamContent(csvStream);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
+ content.Add(fileContent, "file", fileName);
+
+ var response = await http.PostAsync("/api/import/amazon/preview", content);
+ response.EnsureSuccessStatusCode();
+
+ return (await response.Content.ReadFromJsonAsync>())!;
+ }
+
+ public async Task CommitAsync(List items)
+ {
+ var response = await http.PostAsJsonAsync("/api/import/amazon/commit", new AmazonImportCommitRequestDto { Items = items });
+ response.EnsureSuccessStatusCode();
+
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
+}
diff --git a/src/BlazorApp/Components/Import/AmazonImportPage.razor b/src/BlazorApp/Components/Import/AmazonImportPage.razor
new file mode 100644
index 00000000..f99d9b20
--- /dev/null
+++ b/src/BlazorApp/Components/Import/AmazonImportPage.razor
@@ -0,0 +1,314 @@
+@page "/import/amazon"
+@attribute [Authorize]
+@using Keeptrack.WebApi.Contracts.Dto
+@using Keeptrack.BlazorApp.Components.Inventory.Pages
+
+
+
+
+
+@if (_rows is not null)
+{
+
+}
+
+@if (_commitError is not null)
+{
+ @_commitError
+}
+
+@if (_commitResult is not null)
+{
+
+
Books: @_commitResult.BooksCreated created, @_commitResult.BooksMergedInto merged into existing, @_commitResult.BooksSkipped already imported
+
Movies: @_commitResult.MoviesCreated created, @_commitResult.MoviesMergedInto merged into existing, @_commitResult.MoviesSkipped already imported
+
TV shows: @_commitResult.TvShowsCreated created, @_commitResult.TvShowsMergedInto merged into existing, @_commitResult.TvShowsSkipped already imported
+
Video games: @_commitResult.VideoGamesCreated created, @_commitResult.VideoGamesMergedInto merged into existing, @_commitResult.VideoGamesSkipped already imported
+
Gear: @_commitResult.GearCreated created, @_commitResult.GearMergedInto merged into existing, @_commitResult.GearSkipped already imported
+
Collectibles: @_commitResult.CollectiblesCreated created, @_commitResult.CollectiblesMergedInto merged into existing, @_commitResult.CollectiblesSkipped already imported
+
+}
+
+@code {
+ private const long MaxFileSize = 20_000_000;
+
+ [Inject] private AmazonImportApiClient ImportApi { get; set; } = null!;
+
+ private bool _previewing;
+ private string? _error;
+ private List? _rows;
+ private bool _showAll;
+
+ /// Defaults on - a re-uploaded export is mostly already-imported rows, and hiding them by
+ /// default is what actually keeps the review list short; "Show all order items" above is a separate,
+ /// orthogonal filter (books vs. everything).
+ private bool _hideAlreadyImported = true;
+
+ private bool _committing;
+ private string? _commitError;
+ private AmazonImportCommitResultDto? _commitResult;
+
+ private IEnumerable DisplayedRows => (_rows ?? [])
+ .Where(r => _showAll || r.Preview.LooksLikeBook)
+ .Where(r => !_hideAlreadyImported || !r.Preview.AlreadyImported);
+
+ private IEnumerable SelectedRows => _rows is null ? [] : _rows.Where(r => r.Selected);
+
+ private static bool IsMissingPlatform(EditableRow row) => row.MediaType == AmazonImportMediaType.VideoGame && string.IsNullOrEmpty(row.Platform);
+
+ /// Drives the header checkbox: checked only once every currently-shown row is selected.
+ private bool AllDisplayedSelected => DisplayedRows.Any() && DisplayedRows.All(r => r.Selected);
+
+ /// Applies to whatever currently shows, not the whole underlying list -
+ /// e.g. selecting all while only likely-books are shown never touches the hidden non-book rows.
+ private void SetAllDisplayedSelected(bool selected)
+ {
+ foreach (var row in DisplayedRows)
+ {
+ row.Selected = selected;
+ }
+ }
+
+ private async Task OnFileSelectedAsync(InputFileChangeEventArgs e)
+ {
+ _previewing = true;
+ _error = null;
+ _rows = null;
+ _commitResult = null;
+ _commitError = null;
+
+ try
+ {
+#pragma warning disable S5693
+ await using var stream = e.File.OpenReadStream(MaxFileSize);
+#pragma warning restore S5693
+ var preview = await ImportApi.PreviewAsync(stream, e.File.Name);
+ _rows = preview.Select(ToEditableRow).ToList();
+ }
+ catch (Exception ex)
+ {
+ _error = ex.Message;
+ }
+ finally
+ {
+ _previewing = false;
+ }
+ }
+
+ private async Task CommitAsync()
+ {
+ _committing = true;
+ _commitError = null;
+ _commitResult = null;
+
+ try
+ {
+ var items = SelectedRows.Select(row => new AmazonImportCommitItemDto
+ {
+ RowId = row.Preview.RowId,
+ // Echoed back rather than trusting a client-computed Reference string - the server derives
+ // the actual owned-copy Reference (and dedup key) from these two itself, which is what makes
+ // two different items sharing one Amazon order distinguishable from each other.
+ OrderId = row.Preview.OrderId,
+ Asin = row.Preview.Asin,
+ Title = row.Title,
+ // The original, unedited title from the parsed preview - kept separate from the editable
+ // row.Title above so it can be recorded verbatim in the created item's notes, regardless of
+ // whatever the user typed over it here.
+ AmazonTitle = row.Preview.Title,
+ MediaType = row.MediaType,
+ // Amazon's export has no publication year (only the purchase date), so it's never pre-filled -
+ // but worth entering by hand here if known, since it sharpens the deferred reference-lookup
+ // pass's match precision later (an ambiguous multi-candidate title+year search is left
+ // unresolved for manual review rather than guessed, same as everywhere else in the app).
+ Year = row.Year,
+ Isbn = row.Isbn,
+ // Only meaningful (and only validated server-side) for a VideoGame row.
+ Platform = row.MediaType == AmazonImportMediaType.VideoGame ? row.Platform : null,
+ AcquiredAt = row.AcquiredAt,
+ Price = row.Price,
+ Vendor = row.Vendor,
+ CopyType = row.CopyType
+ }).ToList();
+
+ _commitResult = await ImportApi.CommitAsync(items);
+ }
+ catch (Exception ex)
+ {
+ _commitError = ex.Message;
+ }
+ finally
+ {
+ _committing = false;
+ }
+ }
+
+ private static EditableRow ToEditableRow(AmazonOrderPreviewRowDto preview) => new()
+ {
+ Preview = preview,
+ Selected = preview.LooksLikeBook && !preview.AlreadyImported,
+ // Empty (forcing an explicit choice) unless the ISBN heuristic suggested a book - there's no
+ // equivalent signal for the other three types, so guessing one of them would be worse than asking.
+ MediaType = preview.LooksLikeBook ? AmazonImportMediaType.Book : null,
+ Title = preview.Title,
+ Isbn = preview.SuggestedIsbn,
+ AcquiredAt = preview.OrderDate,
+ Price = preview.Price,
+ Vendor = preview.Vendor
+ };
+
+ private sealed class EditableRow
+ {
+ public required AmazonOrderPreviewRowDto Preview { get; init; }
+ public bool Selected { get; set; }
+
+ /// The one field worth editing at import time - everything else below is read-only, order-derived info.
+ public string Title { get; set; } = "";
+
+ /// Never pre-filled (Amazon's export has no publication year) - worth entering by hand if known, see .
+ public int? Year { get; set; }
+
+ ///
+ /// Null (forcing an explicit pick, see 's sibling commit-button
+ /// guard) unless the ISBN heuristic suggested Book - see .
+ ///
+ public AmazonImportMediaType? MediaType { get; set; }
+
+ public string? Isbn { get; set; }
+ public DateOnly? AcquiredAt { get; set; }
+ public decimal? Price { get; set; }
+ public string? Vendor { get; set; }
+
+ /// The other field worth a decision - Amazon's export can't reliably tell physical from digital.
+ public CopyType CopyType { get; set; }
+
+ /// PS5/Xbox/PC/Switch... - only relevant (and only sent) when is VideoGame.
+ public string? Platform { get; set; }
+ }
+}
diff --git a/src/BlazorApp/Components/Import/GenericVideoGameImportApiClient.cs b/src/BlazorApp/Components/Import/GenericVideoGameImportApiClient.cs
new file mode 100644
index 00000000..ef544612
--- /dev/null
+++ b/src/BlazorApp/Components/Import/GenericVideoGameImportApiClient.cs
@@ -0,0 +1,28 @@
+using System.Net.Http.Headers;
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Import;
+
+public sealed class GenericVideoGameImportApiClient(HttpClient http)
+{
+ public async Task> PreviewAsync(Stream csvStream, string fileName)
+ {
+ using var content = new MultipartFormDataContent();
+ using var fileContent = new StreamContent(csvStream);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
+ content.Add(fileContent, "file", fileName);
+
+ var response = await http.PostAsync("/api/import/video-games/preview", content);
+ response.EnsureSuccessStatusCode();
+
+ return (await response.Content.ReadFromJsonAsync>())!;
+ }
+
+ public async Task CommitAsync(List items)
+ {
+ var response = await http.PostAsJsonAsync("/api/import/video-games/commit", new GenericVideoGameImportCommitRequestDto { Items = items });
+ response.EnsureSuccessStatusCode();
+
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
+}
diff --git a/src/BlazorApp/Components/Import/GenericVideoGameImportPage.razor b/src/BlazorApp/Components/Import/GenericVideoGameImportPage.razor
new file mode 100644
index 00000000..b2c0d39f
--- /dev/null
+++ b/src/BlazorApp/Components/Import/GenericVideoGameImportPage.razor
@@ -0,0 +1,274 @@
+@page "/import/video-games"
+@attribute [Authorize]
+@using Keeptrack.WebApi.Contracts.Dto
+@using Keeptrack.BlazorApp.Components.Inventory.Pages
+
+
+
+
+
+@if (_rows is not null)
+{
+
+}
+
+@if (_commitError is not null)
+{
+ @_commitError
+}
+
+@if (_commitResult is not null)
+{
+
+
Video games: @_commitResult.VideoGamesCreated created, @_commitResult.VideoGamesMergedInto merged into existing, @_commitResult.VideoGamesSkipped already imported
+
+ @_commitResult.RowsImported of @_lastCommitRowCount selected row(s) imported.
+ @if (_commitResult.VideoGamesCreated + _commitResult.VideoGamesMergedInto < _commitResult.RowsImported)
+ {
+ Several rows shared a title and were combined into the same video game, so the created/merged count above is lower than the row count - nothing was lost.
+ }
+
+ @if (_commitResult.SkippedRowTitles.Count > 0)
+ {
+
+
Not imported (already imported before):
+
+ @foreach (var title in _commitResult.SkippedRowTitles)
+ {
+ @title
+ }
+
+ }
+
+}
+
+@code {
+ private const long MaxFileSize = 20_000_000;
+
+ [Inject] private GenericVideoGameImportApiClient ImportApi { get; set; } = null!;
+
+ private bool _previewing;
+ private string? _error;
+ private List? _rows;
+
+ /// Defaults on - a re-uploaded export is mostly already-imported rows, and hiding them by
+ /// default is what actually keeps the review list short.
+ private bool _hideAlreadyImported = true;
+
+ private bool _committing;
+ private string? _commitError;
+ private GenericVideoGameImportCommitResultDto? _commitResult;
+
+ /// How many rows the last commit actually submitted - the reconciliation total shown next to
+ /// .
+ private int _lastCommitRowCount;
+
+ private IEnumerable DisplayedRows => (_rows ?? [])
+ .Where(r => !_hideAlreadyImported || !r.Preview.AlreadyImported);
+
+ private IEnumerable SelectedRows => _rows is null ? [] : _rows.Where(r => r.Selected);
+
+ /// Drives the header checkbox: checked only once every currently-shown row is selected.
+ private bool AllDisplayedSelected => DisplayedRows.Any() && DisplayedRows.All(r => r.Selected);
+
+ private void SetAllDisplayedSelected(bool selected)
+ {
+ foreach (var row in DisplayedRows)
+ {
+ row.Selected = selected;
+ }
+ }
+
+ private async Task OnFileSelectedAsync(InputFileChangeEventArgs e)
+ {
+ _previewing = true;
+ _error = null;
+ _rows = null;
+ _commitResult = null;
+ _commitError = null;
+
+ try
+ {
+#pragma warning disable S5693
+ await using var stream = e.File.OpenReadStream(MaxFileSize);
+#pragma warning restore S5693
+ var preview = await ImportApi.PreviewAsync(stream, e.File.Name);
+ _rows = preview.Select(ToEditableRow).ToList();
+ }
+ catch (Exception ex)
+ {
+ _error = ex.Message;
+ }
+ finally
+ {
+ _previewing = false;
+ }
+ }
+
+ private async Task CommitAsync()
+ {
+ _committing = true;
+ _commitError = null;
+ _commitResult = null;
+
+ try
+ {
+ var items = SelectedRows.Select(row => new GenericVideoGameImportCommitItemDto
+ {
+ RowId = row.Preview.RowId,
+ Title = row.Title,
+ // The original, unedited title from the parsed preview - kept separate from the editable
+ // row.Title above so it can be recorded verbatim in the created item's notes, regardless of
+ // whatever the user typed over it here.
+ SourceTitle = row.Preview.Title,
+ Platform = row.Platform,
+ ProductName = row.Preview.ProductName,
+ TransactionId = row.Preview.TransactionId,
+ OrderId = row.Preview.OrderId,
+ Vendor = row.Preview.Vendor,
+ Year = row.Year,
+ AcquiredAt = row.Preview.TransactionDate,
+ Price = row.Preview.Price,
+ CopyType = row.CopyType
+ }).ToList();
+
+ _lastCommitRowCount = items.Count;
+ _commitResult = await ImportApi.CommitAsync(items);
+ }
+ catch (Exception ex)
+ {
+ _commitError = ex.Message;
+ }
+ finally
+ {
+ _committing = false;
+ }
+ }
+
+ private static EditableRow ToEditableRow(GenericVideoGameImportPreviewRowDto preview) => new()
+ {
+ Preview = preview,
+ Selected = !preview.AlreadyImported,
+ Title = preview.Title,
+ Platform = preview.Platform,
+ // This export is store purchase history, so a copy is inherently digital - still editable in case
+ // of an oddity (e.g. a listed physical bundle/voucher line).
+ CopyType = CopyType.Digital
+ };
+
+ private sealed class EditableRow
+ {
+ public required GenericVideoGameImportPreviewRowDto Preview { get; init; }
+ public bool Selected { get; set; }
+
+ /// The one field worth editing at import time besides Year/Platform/Copy - everything else is read-only, order-derived info.
+ public string Title { get; set; } = "";
+
+ /// Never pre-filled - the export has no publication/release year.
+ public int? Year { get; set; }
+
+ public string? Platform { get; set; }
+
+ public CopyType CopyType { get; set; }
+ }
+}
diff --git a/src/BlazorApp/Components/Import/ImportPage.razor b/src/BlazorApp/Components/Import/ImportPage.razor
index 6f5830b2..d7d77a17 100644
--- a/src/BlazorApp/Components/Import/ImportPage.razor
+++ b/src/BlazorApp/Components/Import/ImportPage.razor
@@ -2,6 +2,28 @@
@attribute [Authorize]
+
+
+
+
+
+
+
+
diff --git a/src/BlazorApp/Components/Inventory/Clients/AlbumApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/AlbumApiClient.cs
index c86c5b12..977bb18b 100644
--- a/src/BlazorApp/Components/Inventory/Clients/AlbumApiClient.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/AlbumApiClient.cs
@@ -13,4 +13,15 @@ public async Task RefreshReferenceAsync(string id)
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync())!;
}
+
+ ///
+ /// Admin-only: unlinks and permanently deletes the shared reference document
+ /// (POST api/albums/{id}/unlink-reference on WebApi).
+ ///
+ public async Task UnlinkReferenceAsync(string id)
+ {
+ var response = await Http.PostAsync($"{ApiResourceName}/{id}/unlink-reference", null);
+ response.EnsureSuccessStatusCode();
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
}
diff --git a/src/BlazorApp/Components/Inventory/Clients/BookApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/BookApiClient.cs
index 6d9a0b45..9feb451c 100644
--- a/src/BlazorApp/Components/Inventory/Clients/BookApiClient.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/BookApiClient.cs
@@ -13,4 +13,15 @@ public async Task RefreshReferenceAsync(string id)
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync())!;
}
+
+ ///
+ /// Admin-only: unlinks and permanently deletes the shared reference document
+ /// (POST api/books/{id}/unlink-reference on WebApi).
+ ///
+ public async Task UnlinkReferenceAsync(string id)
+ {
+ var response = await Http.PostAsync($"{ApiResourceName}/{id}/unlink-reference", null);
+ response.EnsureSuccessStatusCode();
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
}
diff --git a/src/BlazorApp/Components/Inventory/Clients/CollectibleApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/CollectibleApiClient.cs
new file mode 100644
index 00000000..30a1d6fc
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Clients/CollectibleApiClient.cs
@@ -0,0 +1,9 @@
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Inventory.Clients;
+
+public sealed class CollectibleApiClient(HttpClient http)
+ : InventoryApiClientBase(http)
+{
+ protected override string ApiResourceName => "/api/collectibles";
+}
diff --git a/src/BlazorApp/Components/Inventory/Clients/GearApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/GearApiClient.cs
new file mode 100644
index 00000000..0abdeeba
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Clients/GearApiClient.cs
@@ -0,0 +1,13 @@
+using Keeptrack.WebApi.Contracts.Dto;
+
+namespace Keeptrack.BlazorApp.Components.Inventory.Clients;
+
+public sealed class GearApiClient(HttpClient http)
+ : InventoryApiClientBase(http)
+{
+ protected override string ApiResourceName => "/api/gear";
+
+ /// Distinct categories already used across this tenant's gear - see GearController.GetCategories .
+ public async Task> GetCategoriesAsync() =>
+ await Http.GetFromJsonAsync>($"{ApiResourceName}/categories") ?? [];
+}
diff --git a/src/BlazorApp/Components/Inventory/Clients/InventoryApiClientBase.cs b/src/BlazorApp/Components/Inventory/Clients/InventoryApiClientBase.cs
index b0f30496..78b869eb 100644
--- a/src/BlazorApp/Components/Inventory/Clients/InventoryApiClientBase.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/InventoryApiClientBase.cs
@@ -42,7 +42,7 @@ public async Task AddAsync(TDto movie)
var response = await http.PostAsJsonAsync($"{ApiResourceName}", movie);
if (!response.IsSuccessStatusCode)
{
- // the API returns error bodies (free-tier quota 403s, ApiExceptionFilterAttribute's 400s/500s);
+ // the API returns error bodies (free-tier quota 403s, ApiExceptionFilterAttribute's 400s/500s) -
// surfacing that text beats EnsureSuccessStatusCode's opaque "403 (Forbidden)" in the Add form
var body = await response.Content.ReadFromJsonAsync();
throw new InvalidOperationException(string.IsNullOrEmpty(body?.Error)
diff --git a/src/BlazorApp/Components/Inventory/Clients/MovieApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/MovieApiClient.cs
index 66c9540f..1cfa0983 100644
--- a/src/BlazorApp/Components/Inventory/Clients/MovieApiClient.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/MovieApiClient.cs
@@ -18,4 +18,15 @@ public async Task RefreshReferenceAsync(string id)
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync())!;
}
+
+ ///
+ /// Admin-only: unlinks and permanently deletes the shared reference document
+ /// (POST api/movies/{id}/unlink-reference on WebApi).
+ ///
+ public async Task UnlinkReferenceAsync(string id)
+ {
+ var response = await Http.PostAsync($"{ApiResourceName}/{id}/unlink-reference", null);
+ response.EnsureSuccessStatusCode();
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
}
diff --git a/src/BlazorApp/Components/Inventory/Clients/TvShowApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/TvShowApiClient.cs
index 6458d356..8bd2ad4e 100644
--- a/src/BlazorApp/Components/Inventory/Clients/TvShowApiClient.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/TvShowApiClient.cs
@@ -18,4 +18,15 @@ public async Task RefreshReferenceAsync(string id)
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync())!;
}
+
+ ///
+ /// Admin-only: unlinks and permanently deletes the shared reference document
+ /// (POST api/tv-shows/{id}/unlink-reference on WebApi).
+ ///
+ public async Task UnlinkReferenceAsync(string id)
+ {
+ var response = await Http.PostAsync($"{ApiResourceName}/{id}/unlink-reference", null);
+ response.EnsureSuccessStatusCode();
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
}
diff --git a/src/BlazorApp/Components/Inventory/Clients/VideoGameApiClient.cs b/src/BlazorApp/Components/Inventory/Clients/VideoGameApiClient.cs
index d5e64d3b..45df7e83 100644
--- a/src/BlazorApp/Components/Inventory/Clients/VideoGameApiClient.cs
+++ b/src/BlazorApp/Components/Inventory/Clients/VideoGameApiClient.cs
@@ -13,4 +13,15 @@ public async Task RefreshReferenceAsync(string id)
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync())!;
}
+
+ ///
+ /// Admin-only: unlinks and permanently deletes the shared reference document
+ /// (POST api/video-games/{id}/unlink-reference on WebApi).
+ ///
+ public async Task UnlinkReferenceAsync(string id)
+ {
+ var response = await Http.PostAsync($"{ApiResourceName}/{id}/unlink-reference", null);
+ response.EnsureSuccessStatusCode();
+ return (await response.Content.ReadFromJsonAsync())!;
+ }
}
diff --git a/src/BlazorApp/Components/Inventory/Pages/AlbumDetail.razor b/src/BlazorApp/Components/Inventory/Pages/AlbumDetail.razor
index 160159e7..901a887d 100644
--- a/src/BlazorApp/Components/Inventory/Pages/AlbumDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/AlbumDetail.razor
@@ -31,6 +31,17 @@ else
title="@(string.IsNullOrEmpty(Album.ReferenceId) ? "Check for reference match using the title and year below." : $"Linked to \"{Reference?.Title}\". Not right? Edit the title or year below, then check again.")">
↻
+ @if (!string.IsNullOrEmpty(Album.ReferenceId))
+ {
+
+
+ _pendingUnlink = true">
+
+
+
+
+ }
@if (_refreshMessage is not null)
{
@_refreshMessage
@@ -49,11 +60,18 @@ else
}
+
+
-
- @if (!string.IsNullOrEmpty(Reference?.ImageUrl))
+ @{ var coverUrl = !string.IsNullOrEmpty(Album.CustomImageUrl) ? Album.CustomImageUrl : Reference?.ImageUrl; }
+
+ @if (!string.IsNullOrEmpty(coverUrl))
{
-
+
}
@@ -74,6 +92,11 @@ else
Rating
+
+ Cover image URL
+
+
@@ -161,6 +184,7 @@ else
private string? _refreshMessage;
private string _refreshMessageStyle = "neutral";
private object? _refreshMessageToken;
+ private bool _pendingUnlink;
///
Reuses CastGrid 's "photo + name" card for the single credited artist, instead of
/// introducing a one-off component for what's visually the same layout as a TV/movie cast member.
@@ -251,9 +275,7 @@ else
private async Task SaveArtistAsync(ChangeEventArgs e)
{
if (Album is null) return;
- var artist = e.Value?.ToString();
- if (string.IsNullOrWhiteSpace(artist)) return;
- Album.Artist = artist;
+ Album.Artist = e.Value?.ToString();
await AlbumApi.UpdateAsync(Album);
}
@@ -264,6 +286,13 @@ else
await AlbumApi.UpdateAsync(Album);
}
+ private async Task SaveCustomImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Album is null) return;
+ Album.CustomImageUrl = e.Value?.ToString();
+ await AlbumApi.UpdateAsync(Album);
+ }
+
private async Task RefreshReferenceAsync()
{
if (Album?.Id is null) return;
@@ -276,9 +305,7 @@ else
var refreshed = await AlbumApi.RefreshReferenceAsync(Album.Id);
await LoadAsync();
- (_refreshMessage, _refreshMessageStyle) = string.IsNullOrEmpty(refreshed.ReferenceId)
- ? string.IsNullOrEmpty(previousReferenceId) ? ("No match found", "neutral") : ("Unlinked - no match", "danger")
- : refreshed.ReferenceId == previousReferenceId ? ("Already linked", "neutral") : ("Linked!", "success");
+ (_refreshMessage, _refreshMessageStyle) = ReferenceRefreshMessage.Compute(previousReferenceId, refreshed.ReferenceId);
}
catch (Exception ex)
{
@@ -293,6 +320,26 @@ else
ScheduleRefreshMessageClear();
}
+ private async Task UnlinkReferenceAsync()
+ {
+ _pendingUnlink = false;
+ if (Album?.Id is null) return;
+
+ try
+ {
+ await AlbumApi.UnlinkReferenceAsync(Album.Id);
+ await LoadAsync();
+ (_refreshMessage, _refreshMessageStyle) = ("Unlinked and reference removed.", "success");
+ }
+ catch (Exception ex)
+ {
+ _refreshMessage = $"Error: {ex.Message}";
+ _refreshMessageStyle = "danger";
+ }
+
+ ScheduleRefreshMessageClear();
+ }
+
///
/// The toast is a small, auto-dismissing pill next to the refresh icon, not a permanent line of text -
/// a token guards against an in-flight delay from a previous click clearing a newer message.
diff --git a/src/BlazorApp/Components/Inventory/Pages/BookDetail.razor b/src/BlazorApp/Components/Inventory/Pages/BookDetail.razor
index d915ccbf..c1fb7cb3 100644
--- a/src/BlazorApp/Components/Inventory/Pages/BookDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/BookDetail.razor
@@ -31,6 +31,17 @@ else
title="@(string.IsNullOrEmpty(Book.ReferenceId) ? "Check for reference match using the title and year below." : $"Linked to \"{Reference?.Title}\". Not right? Edit the title or year below, then check again.")">
↻
+ @if (!string.IsNullOrEmpty(Book.ReferenceId))
+ {
+
+
+ _pendingUnlink = true">
+
+
+
+
+ }
@if (_refreshMessage is not null)
{
@_refreshMessage
@@ -50,6 +61,12 @@ else
}
+
+
@(Book.FirstReadAt is not null ? "✓ Read" : "Mark as read")
@@ -98,7 +115,16 @@ else
Cover image URL
@@ -131,6 +157,8 @@ else
[Inject] private ReferenceDataApiClient ReferenceDataApi { get; set; } = null!;
+ [Inject] private UserPreferencesState PreferencesState { get; set; } = null!;
+
private static readonly TimeSpan RefreshMessageDuration = TimeSpan.FromSeconds(4);
// _loading is delay-gated (see LoadingIndicator) and only turns on for a load that's genuinely slow.
@@ -149,10 +177,14 @@ else
[PersistentState]
public BookReferenceDto? Reference { get; set; }
+
+ [PersistentState]
+ public bool ShowChasseAuxLivresLink { get; set; }
private bool _refreshingReference;
private string? _refreshMessage;
private string _refreshMessageStyle = "neutral";
private object? _refreshMessageToken;
+ private bool _pendingUnlink;
protected override async Task OnParametersSetAsync()
{
@@ -178,6 +210,7 @@ else
{
Book = await BookApi.GetOneAsync(Id);
Reference = string.IsNullOrEmpty(Book?.ReferenceId) ? null : await ReferenceDataApi.GetBookAsync(Book.ReferenceId);
+ ShowChasseAuxLivresLink = (await PreferencesState.GetAsync()).Features.ShowChasseAuxLivresLink;
}
private async Task SetRatingAsync(float rating)
@@ -217,7 +250,7 @@ else
private async Task SetReadDateAsync(ChangeEventArgs e)
{
if (Book is null) return;
- Book.FirstReadAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ Book.FirstReadAt = DateOnlyInput.Parse(e.Value);
await BookApi.UpdateAsync(Book);
}
@@ -233,9 +266,7 @@ else
private async Task SaveAuthorAsync(ChangeEventArgs e)
{
if (Book is null) return;
- var author = e.Value?.ToString();
- if (string.IsNullOrWhiteSpace(author)) return;
- Book.Author = author;
+ Book.Author = e.Value?.ToString();
await BookApi.UpdateAsync(Book);
}
@@ -286,9 +317,7 @@ else
var refreshed = await BookApi.RefreshReferenceAsync(Book.Id);
await LoadAsync();
- (_refreshMessage, _refreshMessageStyle) = string.IsNullOrEmpty(refreshed.ReferenceId)
- ? string.IsNullOrEmpty(previousReferenceId) ? ("No match found", "neutral") : ("Unlinked - no match", "danger")
- : refreshed.ReferenceId == previousReferenceId ? ("Already linked", "neutral") : ("Linked!", "success");
+ (_refreshMessage, _refreshMessageStyle) = ReferenceRefreshMessage.Compute(previousReferenceId, refreshed.ReferenceId);
}
catch (Exception ex)
{
@@ -303,6 +332,26 @@ else
ScheduleRefreshMessageClear();
}
+ private async Task UnlinkReferenceAsync()
+ {
+ _pendingUnlink = false;
+ if (Book?.Id is null) return;
+
+ try
+ {
+ await BookApi.UnlinkReferenceAsync(Book.Id);
+ await LoadAsync();
+ (_refreshMessage, _refreshMessageStyle) = ("Unlinked and reference removed.", "success");
+ }
+ catch (Exception ex)
+ {
+ _refreshMessage = $"Error: {ex.Message}";
+ _refreshMessageStyle = "danger";
+ }
+
+ ScheduleRefreshMessageClear();
+ }
+
///
/// The toast is a small, auto-dismissing pill next to the refresh icon, not a permanent line of text -
/// a token guards against an in-flight delay from a previous click clearing a newer message.
diff --git a/src/BlazorApp/Components/Inventory/Pages/CarDetail.razor b/src/BlazorApp/Components/Inventory/Pages/CarDetail.razor
index d5198b1f..e55e6451 100644
--- a/src/BlazorApp/Components/Inventory/Pages/CarDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/CarDetail.razor
@@ -28,6 +28,13 @@ else
+ @if (!string.IsNullOrEmpty(Car.ImageUrl))
+ {
+
+ }
+
@if (HasMetricsToShow)
@@ -362,6 +373,13 @@ else
await SaveCarAsync();
}
+ private async Task SaveImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Car is null) return;
+ Car.ImageUrl = e.Value?.ToString();
+ await SaveCarAsync();
+ }
+
private void ShowAddModal()
{
_modalEntry = CarHistoryForm.NewEntry(Id);
diff --git a/src/BlazorApp/Components/Inventory/Pages/Cars.razor b/src/BlazorApp/Components/Inventory/Pages/Cars.razor
index 74737f4e..ae982c61 100644
--- a/src/BlazorApp/Components/Inventory/Pages/Cars.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/Cars.razor
@@ -17,6 +17,8 @@
ShowForm="@_showForm"
Form="@_form"
ItemTitle="@(car => car.Name)"
+ ItemImageUrl="@(car => car.ImageUrl)"
+ ItemImageShape="wide"
OnSearchKeyUp="@OnSearchKeyUp"
OnClearSearch="@ClearSearch"
OnGoToPage="@GoToPage"
diff --git a/src/BlazorApp/Components/Inventory/Pages/CollectibleDetail.razor b/src/BlazorApp/Components/Inventory/Pages/CollectibleDetail.razor
new file mode 100644
index 00000000..708d0634
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/CollectibleDetail.razor
@@ -0,0 +1,158 @@
+@page "/collectibles/{Id}"
+@attribute [Authorize]
+
+@if (!_loaded)
+{
+ @if (_loading)
+ {
+
+ }
+}
+else if (Collectible is null)
+{
+
+
◈
+
Collectible not found.
+
+}
+else
+{
+
+
+
+
+ @if (!string.IsNullOrEmpty(Collectible.ImageUrl))
+ {
+
+
+
+ }
+
+
+
+
+}
+
+@code {
+ [Parameter] public required string Id { get; set; }
+
+ [Inject] private CollectibleApiClient CollectibleApi { get; set; } = null!;
+
+ // _loading is delay-gated (see LoadingIndicator) and only turns on for a load that's genuinely slow.
+ // _loaded tracks whether a load attempt has finished at all, fresh or restored from persisted
+ // prerender state - both default false so the forced render Blazor triggers right after the
+ // synchronous prefix of OnParametersSetAsync (before any awaited fetch resolves) shows blank instead
+ // of a spinner flash on every navigation to this page.
+ private bool _loading;
+ private bool _loaded;
+
+ // Public property (a framework requirement for [PersistentState]): the data loaded during the
+ // prerender pass is carried over to the interactive circuit, so the first interactive render reuses
+ // it instead of resetting to the spinner and re-fetching - same pattern as AlbumDetail/CarDetail.
+ [PersistentState]
+ public CollectibleDto? Collectible { get; set; }
+
+ protected override async Task OnParametersSetAsync()
+ {
+ // Collectible already holds this route's item when PersistentState restored the prerendered data
+ // the id check keeps an in-circuit navigation to a different collectible reloading as before
+ if (Collectible?.Id == Id)
+ {
+ _loading = false;
+ _loaded = true;
+ return;
+ }
+ await LoadAsync();
+ }
+
+ private async Task LoadAsync()
+ {
+ await LoadingIndicator.RunAsync(FetchAsync(), v => _loading = v, StateHasChanged);
+ _loading = false;
+ _loaded = true;
+ }
+
+ private async Task FetchAsync()
+ {
+ Collectible = await CollectibleApi.GetOneAsync(Id);
+ }
+
+ private async Task SaveCollectibleAsync()
+ {
+ if (Collectible is null) return;
+ await CollectibleApi.UpdateAsync(Collectible);
+ }
+
+ private async Task SaveTitleAsync(ChangeEventArgs e)
+ {
+ if (Collectible is null) return;
+ var title = e.Value?.ToString();
+ if (string.IsNullOrWhiteSpace(title)) return;
+ Collectible.Title = title;
+ await SaveCollectibleAsync();
+ }
+
+ private async Task SaveBrandAsync(ChangeEventArgs e)
+ {
+ if (Collectible is null) return;
+ Collectible.Brand = e.Value?.ToString();
+ await SaveCollectibleAsync();
+ }
+
+ private async Task SaveYearAsync(ChangeEventArgs e)
+ {
+ if (Collectible is null) return;
+ Collectible.Year = int.TryParse(e.Value?.ToString(), out var year) ? year : null;
+ await SaveCollectibleAsync();
+ }
+
+ private async Task SaveNotesAsync(ChangeEventArgs e)
+ {
+ if (Collectible is null) return;
+ Collectible.Notes = e.Value?.ToString();
+ await SaveCollectibleAsync();
+ }
+
+ private async Task SaveImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Collectible is null) return;
+ Collectible.ImageUrl = e.Value?.ToString();
+ await SaveCollectibleAsync();
+ }
+
+ private async Task ToggleFavoriteAsync()
+ {
+ if (Collectible is null) return;
+ Collectible.IsFavorite = !Collectible.IsFavorite;
+ await SaveCollectibleAsync();
+ }
+}
diff --git a/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor b/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor
new file mode 100644
index 00000000..b4377ce1
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor
@@ -0,0 +1,65 @@
+@page "/collectibles"
+@inherits InventoryPageBase
+@attribute [Authorize]
+
+
+
+ ToggleFilter("favorite", FavoriteFilter)'>★ Favorites
+ ToggleFilter("owned", OwnedFilter)'>● Owned
+
+
+ @if (!string.IsNullOrEmpty(collectible.Brand))
+ {
+ @collectible.Brand
+ }
+ @if (collectible.Year > 0)
+ {
+ @collectible.Year
+ }
+ @if (collectible.IsFavorite)
+ {
+ Favorite
+ }
+ @if (collectible.OwnedVersions.Count > 0)
+ {
+ Owned
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor.cs b/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor.cs
new file mode 100644
index 00000000..3d0c11fa
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/Collectibles.razor.cs
@@ -0,0 +1,30 @@
+using Keeptrack.WebApi.Contracts.Dto;
+using Microsoft.AspNetCore.Components;
+
+namespace Keeptrack.BlazorApp.Components.Inventory.Pages;
+
+public partial class Collectibles : InventoryPageBase
+{
+ [Inject] private CollectibleApiClient CollectibleApi { get; set; } = null!;
+
+ protected override InventoryApiClientBase Api => CollectibleApi;
+
+ protected override string ListRoute => "/collectibles";
+
+ [SupplyParameterFromQuery(Name = "favorite")]
+ public bool FavoriteFilter { get; set; }
+
+ [SupplyParameterFromQuery(Name = "owned")]
+ public bool OwnedFilter { get; set; }
+
+ protected override IReadOnlyDictionary? ExtraQuery
+ {
+ get
+ {
+ var query = new Dictionary();
+ if (FavoriteFilter) query["IsFavorite"] = "true";
+ if (OwnedFilter) query["IsOwned"] = "true";
+ return query.Count > 0 ? query : null;
+ }
+ }
+}
diff --git a/src/BlazorApp/Components/Inventory/Pages/Gear.razor b/src/BlazorApp/Components/Inventory/Pages/Gear.razor
new file mode 100644
index 00000000..b43c1e2c
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/Gear.razor
@@ -0,0 +1,80 @@
+@page "/gear"
+@inherits InventoryPageBase
+@attribute [Authorize]
+
+
+
+ ToggleFilter("favorite", FavoriteFilter)'>★ Favorites
+ ToggleFilter("owned", OwnedFilter)'>● Owned
+ @if (Categories.Count > 0)
+ {
+ @* a button per category (VideoGame's State pattern) doesn't scale here - Category is an open,
+ tenant-grown list rather than a small fixed set. A plain was tried and dropped: its
+ required width:100% fought the flex row's own sizing and forced the whole Filters group onto
+ a wrapped second line even with room to spare - SuggestInput is a fixed-width flex item instead. *@
+
+ }
+
+
+ @if (!string.IsNullOrEmpty(gear.Brand))
+ {
+ @gear.Brand
+ }
+ @if (!string.IsNullOrEmpty(gear.Category))
+ {
+ @gear.Category
+ }
+ @if (gear.Year > 0)
+ {
+ @gear.Year
+ }
+ @if (gear.IsFavorite)
+ {
+ Favorite
+ }
+ @if (gear.OwnedVersions.Count > 0)
+ {
+ Owned
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BlazorApp/Components/Inventory/Pages/Gear.razor.cs b/src/BlazorApp/Components/Inventory/Pages/Gear.razor.cs
new file mode 100644
index 00000000..55e4bf4c
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/Gear.razor.cs
@@ -0,0 +1,44 @@
+using Keeptrack.WebApi.Contracts.Dto;
+using Microsoft.AspNetCore.Components;
+
+namespace Keeptrack.BlazorApp.Components.Inventory.Pages;
+
+public partial class Gear : InventoryPageBase
+{
+ [Inject] private GearApiClient GearApi { get; set; } = null!;
+
+ protected override InventoryApiClientBase Api => GearApi;
+
+ protected override string ListRoute => "/gear";
+
+ [SupplyParameterFromQuery(Name = "favorite")]
+ public bool FavoriteFilter { get; set; }
+
+ [SupplyParameterFromQuery(Name = "owned")]
+ public bool OwnedFilter { get; set; }
+
+ [SupplyParameterFromQuery(Name = "category")]
+ public string? CategoryFilter { get; set; }
+
+ /// Distinct categories across this tenant's gear, for the Filters row - fetched once, not
+ /// tied to search/page/sort state like is.
+ protected List Categories { get; private set; } = [];
+
+ protected override IReadOnlyDictionary? ExtraQuery
+ {
+ get
+ {
+ var query = new Dictionary();
+ if (FavoriteFilter) query["IsFavorite"] = "true";
+ if (OwnedFilter) query["IsOwned"] = "true";
+ if (!string.IsNullOrEmpty(CategoryFilter)) query["Category"] = CategoryFilter;
+ return query.Count > 0 ? query : null;
+ }
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ Categories = await GearApi.GetCategoriesAsync();
+ }
+}
diff --git a/src/BlazorApp/Components/Inventory/Pages/GearDetail.razor b/src/BlazorApp/Components/Inventory/Pages/GearDetail.razor
new file mode 100644
index 00000000..db749ac3
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Pages/GearDetail.razor
@@ -0,0 +1,179 @@
+@page "/gear/{Id}"
+@attribute [Authorize]
+
+@if (!_loaded)
+{
+ @if (_loading)
+ {
+
+ }
+}
+else if (Item is null)
+{
+
+}
+else
+{
+
+
+
+
+ @if (!string.IsNullOrEmpty(Item.ImageUrl))
+ {
+
+
+
+ }
+
+
+
+
+}
+
+@code {
+ [Parameter] public required string Id { get; set; }
+
+ [Inject] private GearApiClient GearApi { get; set; } = null!;
+
+ // _loading is delay-gated (see LoadingIndicator) and only turns on for a load that's genuinely slow.
+ // _loaded tracks whether a load attempt has finished at all, fresh or restored from persisted
+ // prerender state - both default false so the forced render Blazor triggers right after the
+ // synchronous prefix of OnParametersSetAsync (before any awaited fetch resolves) shows blank instead
+ // of a spinner flash on every navigation to this page.
+ private bool _loading;
+ private bool _loaded;
+
+ // Suggested values for the Category input's datalist - fetched once, not tied to Item/Id like the
+ // rest of this page's state (a category typed on this item shouldn't wait on a second round trip).
+ private List _categories = [];
+
+ // Public property (a framework requirement for [PersistentState]): the data loaded during the
+ // prerender pass is carried over to the interactive circuit, so the first interactive render reuses
+ // it instead of resetting to the spinner and re-fetching - same pattern as AlbumDetail/CarDetail.
+ // Named "Item" rather than "Gear" - a property can't share its containing partial class's own name.
+ [PersistentState]
+ public GearDto? Item { get; set; }
+
+ protected override async Task OnInitializedAsync()
+ {
+ _categories = await GearApi.GetCategoriesAsync();
+ }
+
+ protected override async Task OnParametersSetAsync()
+ {
+ // Item already holds this route's item when PersistentState restored the prerendered data
+ // the id check keeps an in-circuit navigation to a different item reloading as before
+ if (Item?.Id == Id)
+ {
+ _loading = false;
+ _loaded = true;
+ return;
+ }
+ await LoadAsync();
+ }
+
+ private async Task LoadAsync()
+ {
+ await LoadingIndicator.RunAsync(FetchAsync(), v => _loading = v, StateHasChanged);
+ _loading = false;
+ _loaded = true;
+ }
+
+ private async Task FetchAsync()
+ {
+ Item = await GearApi.GetOneAsync(Id);
+ }
+
+ private async Task SaveItemAsync()
+ {
+ if (Item is null) return;
+ await GearApi.UpdateAsync(Item);
+ }
+
+ private async Task SaveTitleAsync(ChangeEventArgs e)
+ {
+ if (Item is null) return;
+ var title = e.Value?.ToString();
+ if (string.IsNullOrWhiteSpace(title)) return;
+ Item.Title = title;
+ await SaveItemAsync();
+ }
+
+ private async Task SaveBrandAsync(ChangeEventArgs e)
+ {
+ if (Item is null) return;
+ Item.Brand = e.Value?.ToString();
+ await SaveItemAsync();
+ }
+
+ private async Task SaveYearAsync(ChangeEventArgs e)
+ {
+ if (Item is null) return;
+ Item.Year = int.TryParse(e.Value?.ToString(), out var year) ? year : null;
+ await SaveItemAsync();
+ }
+
+ private async Task SaveCategoryAsync(string? category)
+ {
+ if (Item is null) return;
+ Item.Category = category;
+ await SaveItemAsync();
+ }
+
+ private async Task SaveNotesAsync(ChangeEventArgs e)
+ {
+ if (Item is null) return;
+ Item.Notes = e.Value?.ToString();
+ await SaveItemAsync();
+ }
+
+ private async Task SaveImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Item is null) return;
+ Item.ImageUrl = e.Value?.ToString();
+ await SaveItemAsync();
+ }
+
+ private async Task ToggleFavoriteAsync()
+ {
+ if (Item is null) return;
+ Item.IsFavorite = !Item.IsFavorite;
+ await SaveItemAsync();
+ }
+}
diff --git a/src/BlazorApp/Components/Inventory/Pages/HealthProfileDetail.razor b/src/BlazorApp/Components/Inventory/Pages/HealthProfileDetail.razor
index 106b4253..99cc1f97 100644
--- a/src/BlazorApp/Components/Inventory/Pages/HealthProfileDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/HealthProfileDetail.razor
@@ -28,9 +28,24 @@ else
+ @if (!string.IsNullOrEmpty(Profile.ImageUrl))
+ {
+
+ }
+
@* No summary panels before the journal on purpose (owner feedback):
@@ -258,6 +273,13 @@ else
await SaveProfileAsync();
}
+ private async Task SaveImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Profile is null) return;
+ Profile.ImageUrl = e.Value?.ToString();
+ await SaveProfileAsync();
+ }
+
private void ShowAddModal()
{
_modalEntry = HealthRecordForm.NewEntry(Id);
diff --git a/src/BlazorApp/Components/Inventory/Pages/HealthProfiles.razor b/src/BlazorApp/Components/Inventory/Pages/HealthProfiles.razor
index cabc274a..c2616c43 100644
--- a/src/BlazorApp/Components/Inventory/Pages/HealthProfiles.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/HealthProfiles.razor
@@ -17,6 +17,8 @@
ShowForm="@_showForm"
Form="@_form"
ItemTitle="@(profile => profile.Name)"
+ ItemImageUrl="@(profile => profile.ImageUrl)"
+ ItemImageShape="wide"
OnSearchKeyUp="@OnSearchKeyUp"
OnClearSearch="@ClearSearch"
OnGoToPage="@GoToPage"
diff --git a/src/BlazorApp/Components/Inventory/Pages/HouseDetail.razor b/src/BlazorApp/Components/Inventory/Pages/HouseDetail.razor
index 2f055de0..a9ae3206 100644
--- a/src/BlazorApp/Components/Inventory/Pages/HouseDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/HouseDetail.razor
@@ -35,6 +35,13 @@ else
}
+ @if (!string.IsNullOrEmpty(House.ImageUrl))
+ {
+
+ }
+
@@ -306,14 +317,14 @@ else
private async Task SaveMovedInAtAsync(ChangeEventArgs e)
{
if (House is null) return;
- House.MovedInAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ House.MovedInAt = DateOnlyInput.Parse(e.Value);
await SaveHouseAsync();
}
private async Task SaveMovedOutAtAsync(ChangeEventArgs e)
{
if (House is null) return;
- House.MovedOutAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ House.MovedOutAt = DateOnlyInput.Parse(e.Value);
await SaveHouseAsync();
}
@@ -324,6 +335,13 @@ else
await SaveHouseAsync();
}
+ private async Task SaveImageUrlAsync(ChangeEventArgs e)
+ {
+ if (House is null) return;
+ House.ImageUrl = e.Value?.ToString();
+ await SaveHouseAsync();
+ }
+
private void ShowAddModal()
{
_modalEntry = HouseHistoryForm.NewEntry(Id);
diff --git a/src/BlazorApp/Components/Inventory/Pages/Houses.razor b/src/BlazorApp/Components/Inventory/Pages/Houses.razor
index 4eb9a0ef..fcdb5fae 100644
--- a/src/BlazorApp/Components/Inventory/Pages/Houses.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/Houses.razor
@@ -17,6 +17,8 @@
ShowForm="@_showForm"
Form="@_form"
ItemTitle="@(house => house.Name)"
+ ItemImageUrl="@(house => house.ImageUrl)"
+ ItemImageShape="wide"
OnSearchKeyUp="@OnSearchKeyUp"
OnClearSearch="@ClearSearch"
OnGoToPage="@GoToPage"
diff --git a/src/BlazorApp/Components/Inventory/Pages/MovieDetail.razor b/src/BlazorApp/Components/Inventory/Pages/MovieDetail.razor
index f391c731..76c8a5a0 100644
--- a/src/BlazorApp/Components/Inventory/Pages/MovieDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/MovieDetail.razor
@@ -31,6 +31,17 @@ else
title="@(string.IsNullOrEmpty(Movie.ReferenceId) ? "Check for reference match using the title and year below." : $"Linked to \"{Reference?.Title}\". Not right? Edit the title or year below, then check again.")">
↻
+ @if (!string.IsNullOrEmpty(Movie.ReferenceId))
+ {
+
+
+ _pendingUnlink = true">
+
+
+
+
+ }
@if (_refreshMessage is not null)
{
@_refreshMessage
@@ -51,6 +62,12 @@ else
}
+
+
@(Movie.FirstSeenAt is not null ? "✓ Watched" : "Mark as watched")
@@ -136,6 +153,7 @@ else
private string? _refreshMessage;
private string _refreshMessageStyle = "neutral";
private object? _refreshMessageToken;
+ private bool _pendingUnlink;
protected override async Task OnParametersSetAsync()
{
@@ -207,7 +225,7 @@ else
private async Task SetWatchedDateAsync(ChangeEventArgs e)
{
if (Movie is null) return;
- Movie.FirstSeenAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ Movie.FirstSeenAt = DateOnlyInput.Parse(e.Value);
await MovieApi.UpdateAsync(Movie);
}
@@ -232,9 +250,7 @@ else
var refreshed = await MovieApi.RefreshReferenceAsync(Movie.Id);
await LoadAsync();
- (_refreshMessage, _refreshMessageStyle) = string.IsNullOrEmpty(refreshed.ReferenceId)
- ? string.IsNullOrEmpty(previousReferenceId) ? ("No match found", "neutral") : ("Unlinked - no match", "danger")
- : refreshed.ReferenceId == previousReferenceId ? ("Already linked", "neutral") : ("Linked!", "success");
+ (_refreshMessage, _refreshMessageStyle) = ReferenceRefreshMessage.Compute(previousReferenceId, refreshed.ReferenceId);
}
catch (Exception ex)
{
@@ -249,6 +265,26 @@ else
ScheduleRefreshMessageClear();
}
+ private async Task UnlinkReferenceAsync()
+ {
+ _pendingUnlink = false;
+ if (Movie?.Id is null) return;
+
+ try
+ {
+ await MovieApi.UnlinkReferenceAsync(Movie.Id);
+ await LoadAsync();
+ (_refreshMessage, _refreshMessageStyle) = ("Unlinked and reference removed.", "success");
+ }
+ catch (Exception ex)
+ {
+ _refreshMessage = $"Error: {ex.Message}";
+ _refreshMessageStyle = "danger";
+ }
+
+ ScheduleRefreshMessageClear();
+ }
+
///
/// The toast is a small, auto-dismissing pill next to the refresh icon, not a permanent line of text -
/// a token guards against an in-flight delay from a previous click clearing a newer message.
diff --git a/src/BlazorApp/Components/Inventory/Pages/TvShowDetail.razor b/src/BlazorApp/Components/Inventory/Pages/TvShowDetail.razor
index f350c04d..917d37db 100644
--- a/src/BlazorApp/Components/Inventory/Pages/TvShowDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/TvShowDetail.razor
@@ -13,7 +13,7 @@
}
}
-else if (Show is null)
+else if (_show is null)
{
◈
@@ -22,86 +22,103 @@ else if (Show is null)
}
else
{
-
+
- SetStateAsync(TvShowStatus.Current)'>Current
- SetStateAsync(TvShowStatus.Finished)'>Finished
- SetStateAsync(TvShowStatus.Stopped)'>Stopped
+ SetStateAsync(TvShowStatus.Current)'>Current
+ SetStateAsync(TvShowStatus.Finished)'>Finished
+ SetStateAsync(TvShowStatus.Stopped)'>Stopped
- @if (string.IsNullOrEmpty(Show.ReferenceId))
+ @if (string.IsNullOrEmpty(_show.ReferenceId))
{
-
+
}
+
+
- @if (Reference?.Cast.Count > 0)
+ @if (_reference?.Cast.Count > 0)
{
Cast
-
+
}
-
+
- @if (Reference is not null)
+ @if (_reference is not null)
{
@if (_seasons.Count == 0)
{
@@ -221,20 +238,9 @@ else
// of a spinner flash on every navigation to this page.
private bool _loading;
private bool _loaded;
-
- // Public properties (a framework requirement for [PersistentState]): the data loaded during the
- // prerender pass is carried over to the interactive circuit, so the first interactive render reuses
- // it instead of resetting to the spinner and re-fetching - same pattern as MovieDetail. The derived
- // season/watched lookups are rebuilt locally from these (a tuple-keyed dictionary can't round-trip
- // through the JSON-serialized persisted state).
- [PersistentState]
- public TvShowDto? Show { get; set; }
-
- [PersistentState]
- public TvShowReferenceDto? Reference { get; set; }
-
- [PersistentState]
- public List
? Episodes { get; set; }
+ private TvShowDto? _show;
+ private TvShowReferenceDto? _reference;
+ private List? _episodes;
private Dictionary<(int Season, int Episode), EpisodeDto> _watchedByKey = new();
private Dictionary> _referenceEpisodesBySeason = new();
private List _seasons = [];
@@ -247,24 +253,13 @@ else
private string? _refreshMessage;
private string _refreshMessageStyle = "neutral";
private object? _refreshMessageToken;
+ private bool _pendingUnlink;
private int _newSeason = 1;
private int _newEpisode = 1;
private DateOnly? _newWatchedAt = DateOnly.FromDateTime(DateTime.Today);
- protected override async Task OnParametersSetAsync()
- {
- // Show already holds this route's item when PersistentState restored the prerendered data
- // the id check keeps an in-circuit navigation to a different show reloading as before
- if (Show?.Id == Id && Episodes is not null)
- {
- BuildDerivedState();
- _loading = false;
- _loaded = true;
- return;
- }
- await LoadAsync();
- }
+ protected override async Task OnParametersSetAsync() => await LoadAsync();
private async Task LoadAsync()
{
@@ -275,28 +270,23 @@ else
private async Task FetchAsync()
{
- Show = await TvShowApi.GetOneAsync(Id);
+ _show = await TvShowApi.GetOneAsync(Id);
var episodes = await EpisodeApi.GetAsync(string.Empty, 1, 5000, new Dictionary { ["TvShowId"] = Id });
- Episodes = episodes.Items;
+ _episodes = episodes.Items;
// reference data (synopsis, real episode titles, cast, poster) is a progressive enhancement: it
// may not exist yet (background match still pending, or genuinely unresolved), so the page never
// blocks on it - see the two very different rendering branches below.
- Reference = string.IsNullOrEmpty(Show?.ReferenceId) ? null : await ReferenceDataApi.GetTvShowAsync(Show.ReferenceId);
- BuildDerivedState();
- }
+ _reference = string.IsNullOrEmpty(_show?.ReferenceId) ? null : await ReferenceDataApi.GetTvShowAsync(_show.ReferenceId);
+ _watchedByKey = _episodes.ToDictionary(e => (e.SeasonNumber, e.EpisodeNumber));
- private void BuildDerivedState()
- {
- _watchedByKey = Episodes!.ToDictionary(e => (e.SeasonNumber, e.EpisodeNumber));
-
- if (Reference is not null)
+ if (_reference is not null)
{
// full checklist, in natural watch-through order - an episode TMDB lists with a future air date
// hasn't aired yet, so it isn't "unseen and ready to watch", it doesn't exist yet from the
// viewer's perspective (same air-date filter WatchNextService applies for the "next episode" calc)
var today = DateOnly.FromDateTime(DateTime.Today);
- _referenceEpisodesBySeason = Reference.Episodes
+ _referenceEpisodesBySeason = _reference.Episodes
.Where(e => e.AirDate is null || e.AirDate <= today)
.GroupBy(e => e.SeasonNumber)
.ToDictionary(g => g.Key, g => g.OrderBy(e => e.EpisodeNumber).ToList());
@@ -306,7 +296,7 @@ else
else
{
// unresolved: only what's actually been recorded, most recently aired first
- _unresolvedEpisodesBySeason = Episodes!
+ _unresolvedEpisodesBySeason = _episodes
.GroupBy(e => e.SeasonNumber)
.ToDictionary(g => g.Key, g => g.OrderByDescending(e => e.EpisodeNumber).ToList());
_unresolvedSeasons = _unresolvedEpisodesBySeason.Keys.OrderByDescending(s => s).ToList();
@@ -318,36 +308,36 @@ else
private async Task ToggleFavoriteAsync()
{
- if (Show is null) return;
- Show.IsFavorite = !Show.IsFavorite;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.IsFavorite = !_show.IsFavorite;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task ToggleWantToWatchAsync()
{
- if (Show is null) return;
- Show.WantToWatch = !Show.WantToWatch;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.WantToWatch = !_show.WantToWatch;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task SaveShowAsync()
{
- if (Show is null) return;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task ToggleWishlistedAsync()
{
- if (Show is null) return;
- Show.IsWishlisted = !Show.IsWishlisted;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.IsWishlisted = !_show.IsWishlisted;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task SetRatingAsync(float rating)
{
- if (Show is null) return;
- Show.Rating = rating;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.Rating = rating;
+ await TvShowApi.UpdateAsync(_show);
}
///
@@ -357,35 +347,33 @@ else
///
private async Task SetStateAsync(TvShowStatus state)
{
- if (Show is null) return;
- Show.State = Show.State == state ? null : state;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.State = _show.State == state ? null : state;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task SaveTitleAsync(ChangeEventArgs e)
{
- if (Show is null) return;
+ if (_show is null) return;
var title = e.Value?.ToString();
if (string.IsNullOrWhiteSpace(title)) return;
- Show.Title = title;
- await TvShowApi.UpdateAsync(Show);
+ _show.Title = title;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task RefreshReferenceAsync()
{
- if (Show?.Id is null) return;
+ if (_show?.Id is null) return;
- var previousReferenceId = Show.ReferenceId;
+ var previousReferenceId = _show.ReferenceId;
_refreshingReference = true;
_refreshMessage = null;
try
{
- var refreshed = await TvShowApi.RefreshReferenceAsync(Show.Id);
+ var refreshed = await TvShowApi.RefreshReferenceAsync(_show.Id);
await LoadAsync();
- (_refreshMessage, _refreshMessageStyle) = string.IsNullOrEmpty(refreshed.ReferenceId)
- ? string.IsNullOrEmpty(previousReferenceId) ? ("No match found", "neutral") : ("Unlinked - no match", "danger")
- : refreshed.ReferenceId == previousReferenceId ? ("Already linked", "neutral") : ("Linked!", "success");
+ (_refreshMessage, _refreshMessageStyle) = ReferenceRefreshMessage.Compute(previousReferenceId, refreshed.ReferenceId);
}
catch (Exception ex)
{
@@ -400,6 +388,26 @@ else
ScheduleRefreshMessageClear();
}
+ private async Task UnlinkReferenceAsync()
+ {
+ _pendingUnlink = false;
+ if (_show?.Id is null) return;
+
+ try
+ {
+ await TvShowApi.UnlinkReferenceAsync(_show.Id);
+ await LoadAsync();
+ (_refreshMessage, _refreshMessageStyle) = ("Unlinked and reference removed.", "success");
+ }
+ catch (Exception ex)
+ {
+ _refreshMessage = $"Error: {ex.Message}";
+ _refreshMessageStyle = "danger";
+ }
+
+ ScheduleRefreshMessageClear();
+ }
+
///
/// The toast is a small, auto-dismissing pill next to the refresh icon, not a permanent line of text -
/// a token guards against an in-flight delay from a previous click clearing a newer message.
@@ -421,16 +429,16 @@ else
private async Task SaveYearAsync(ChangeEventArgs e)
{
- if (Show is null) return;
- Show.Year = int.TryParse(e.Value?.ToString(), out var year) ? year : null;
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.Year = int.TryParse(e.Value?.ToString(), out var year) ? year : null;
+ await TvShowApi.UpdateAsync(_show);
}
private async Task SaveNotesAsync(ChangeEventArgs e)
{
- if (Show is null) return;
- Show.Notes = e.Value?.ToString();
- await TvShowApi.UpdateAsync(Show);
+ if (_show is null) return;
+ _show.Notes = e.Value?.ToString();
+ await TvShowApi.UpdateAsync(_show);
}
private bool IsSelectedSeasonFullyWatched() =>
diff --git a/src/BlazorApp/Components/Inventory/Pages/VideoGameDetail.razor b/src/BlazorApp/Components/Inventory/Pages/VideoGameDetail.razor
index 9623c5ec..4455998f 100644
--- a/src/BlazorApp/Components/Inventory/Pages/VideoGameDetail.razor
+++ b/src/BlazorApp/Components/Inventory/Pages/VideoGameDetail.razor
@@ -31,6 +31,17 @@ else
title="@(string.IsNullOrEmpty(Game.ReferenceId) ? "Check for reference match using the title and year below." : $"Linked to \"{Reference?.Title}\". Not right? Edit the title or year below, then check again.")">
↻
+ @if (!string.IsNullOrEmpty(Game.ReferenceId))
+ {
+
+
+ _pendingUnlink = true">
+
+
+
+
+ }
@if (_refreshMessage is not null)
{
@_refreshMessage
@@ -50,10 +61,17 @@ else
}
- @if (!string.IsNullOrEmpty(Reference?.ImageUrl))
+
+
+ var coverUrl = !string.IsNullOrEmpty(Game.CustomImageUrl) ? Game.CustomImageUrl : Reference?.ImageUrl;
+ @if (!string.IsNullOrEmpty(coverUrl))
{
}
@@ -77,6 +95,11 @@ else
{
@string.Join(", ", Reference.Genres)
}
+
+ Cover image URL
+
+
Notes
@@ -113,6 +136,13 @@ else
+
+
+ Product
+ SetPlatformProductNameAsync(entry, e)"/>
+
+
@@ -213,6 +243,7 @@ else
private object? _refreshMessageToken;
private VideoGamePlatformDto? _draftPlatform;
private VideoGamePlatformDto? _pendingRemovePlatform;
+ private bool _pendingUnlink;
protected override async Task OnParametersSetAsync()
{
@@ -285,6 +316,13 @@ else
await SaveGameAsync();
}
+ private async Task SaveCustomImageUrlAsync(ChangeEventArgs e)
+ {
+ if (Game is null) return;
+ Game.CustomImageUrl = e.Value?.ToString();
+ await SaveGameAsync();
+ }
+
private void StartDraftPlatform() => _draftPlatform = new VideoGamePlatformDto();
private void CancelDraftPlatform() => _draftPlatform = null;
@@ -335,7 +373,8 @@ else
&& entry.Price is null
&& entry.AcquiredAt is null
&& string.IsNullOrWhiteSpace(entry.Vendor)
- && string.IsNullOrWhiteSpace(entry.Reference);
+ && string.IsNullOrWhiteSpace(entry.Reference)
+ && string.IsNullOrWhiteSpace(entry.ProductName);
///
/// Clicking the already-active state button clears it back to unset - same toggle-off-on-reclick
@@ -350,7 +389,13 @@ else
private async Task SetPlatformCompletedDateAsync(VideoGamePlatformDto entry, ChangeEventArgs e)
{
- entry.CompletedAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ entry.CompletedAt = DateOnlyInput.Parse(e.Value);
+ await SaveGameAsync();
+ }
+
+ private async Task SetPlatformProductNameAsync(VideoGamePlatformDto entry, ChangeEventArgs e)
+ {
+ entry.ProductName = e.Value?.ToString();
await SaveGameAsync();
}
@@ -363,7 +408,7 @@ else
private async Task SetFullyCompletedDateAsync(VideoGamePlatformDto entry, ChangeEventArgs e)
{
- entry.FullyCompletedAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ entry.FullyCompletedAt = DateOnlyInput.Parse(e.Value);
await SaveGameAsync();
}
@@ -387,7 +432,7 @@ else
private async Task UpdatePlaythroughDateAsync(PlaythroughDto playthrough, ChangeEventArgs e)
{
- playthrough.CompletedAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ playthrough.CompletedAt = DateOnlyInput.Parse(e.Value);
await SaveGameAsync();
}
@@ -403,9 +448,7 @@ else
var refreshed = await VideoGameApi.RefreshReferenceAsync(Game.Id);
await LoadAsync();
- (_refreshMessage, _refreshMessageStyle) = string.IsNullOrEmpty(refreshed.ReferenceId)
- ? string.IsNullOrEmpty(previousReferenceId) ? ("No match found", "neutral") : ("Unlinked - no match", "danger")
- : refreshed.ReferenceId == previousReferenceId ? ("Already linked", "neutral") : ("Linked!", "success");
+ (_refreshMessage, _refreshMessageStyle) = ReferenceRefreshMessage.Compute(previousReferenceId, refreshed.ReferenceId);
}
catch (Exception ex)
{
@@ -420,6 +463,26 @@ else
ScheduleRefreshMessageClear();
}
+ private async Task UnlinkReferenceAsync()
+ {
+ _pendingUnlink = false;
+ if (Game?.Id is null) return;
+
+ try
+ {
+ await VideoGameApi.UnlinkReferenceAsync(Game.Id);
+ await LoadAsync();
+ (_refreshMessage, _refreshMessageStyle) = ("Unlinked and reference removed.", "success");
+ }
+ catch (Exception ex)
+ {
+ _refreshMessage = $"Error: {ex.Message}";
+ _refreshMessageStyle = "danger";
+ }
+
+ ScheduleRefreshMessageClear();
+ }
+
///
/// The toast is a small, auto-dismissing pill next to the refresh icon, not a permanent line of text -
/// a token guards against an in-flight delay from a previous click clearing a newer message.
diff --git a/src/BlazorApp/Components/Inventory/Shared/OwnedVersionFields.razor b/src/BlazorApp/Components/Inventory/Shared/OwnedVersionFields.razor
index e97c602f..eb816096 100644
--- a/src/BlazorApp/Components/Inventory/Shared/OwnedVersionFields.razor
+++ b/src/BlazorApp/Components/Inventory/Shared/OwnedVersionFields.razor
@@ -1,11 +1,18 @@
@using System.Globalization
+@inject UserPreferencesState PreferencesState
@* Shared owned-copy field body (Physical/Digital toggle + price/acquired/vendor/reference) - used by
OwnedVersionsEditor.razor (movie/TV show/book/album), VideoGameDetail.razor's per-platform cards, and
Quick Add's own owned-copy toggle. Each caller keeps its own persistence timing (draft Save button vs.
auto-save vs. nothing-persists-until-Save) via OnChanged, which defaults to a no-op.
ButtonRowSuffix renders alongside the Physical/Digital buttons (e.g. a caller's own remove icon), so a
- caller's header chrome doesn't need to be split awkwardly around this component. *@
+ caller's header chrome doesn't need to be split awkwardly around this component.
+ ExtraFields renders as one more column in the same field row (e.g. VideoGamePlatformDto.ProductName,
+ which has no equivalent on the shared IOwnedCopyDto - not every caller has an extra field like this, so
+ this stays a slot rather than a member on the interface). The four columns below use unnumbered col-md
+ (equal-width auto layout) rather than a fixed col-md-3 for exactly this reason: with no ExtraFields they
+ still fill one row identically to a fixed 3/3/3/3 split, but a 5th ExtraFields column joins the same row
+ and everyone re-shares the width automatically, instead of wrapping to a second row. *@
@@ -16,26 +23,36 @@
@* the euro sign is a display choice, not stored data - a per-user currency setting may replace it later *@
-
+
Price (€)
-
+
Acquired on
-
+
Vendor
-
@code {
@@ -45,6 +62,15 @@
[Parameter] public RenderFragment? ButtonRowSuffix { get; set; }
+ [Parameter] public RenderFragment? ExtraFields { get; set; }
+
+ private bool _showAmazonProductLink;
+
+ protected override async Task OnInitializedAsync()
+ {
+ _showAmazonProductLink = (await PreferencesState.GetAsync()).Features.ShowAmazonProductLink;
+ }
+
private Task SetCopyTypeAsync(CopyType copyType)
{
Copy.CopyType = copyType;
@@ -66,7 +92,7 @@
private Task SetAcquiredAtAsync(ChangeEventArgs e)
{
- Copy.AcquiredAt = DateOnly.TryParse(e.Value?.ToString(), out var date) ? date : null;
+ Copy.AcquiredAt = DateOnlyInput.Parse(e.Value);
return OnChanged.InvokeAsync();
}
diff --git a/src/BlazorApp/Components/Inventory/Shared/OwnedVersionsEditor.razor b/src/BlazorApp/Components/Inventory/Shared/OwnedVersionsEditor.razor
index 60df46ec..4be083ea 100644
--- a/src/BlazorApp/Components/Inventory/Shared/OwnedVersionsEditor.razor
+++ b/src/BlazorApp/Components/Inventory/Shared/OwnedVersionsEditor.razor
@@ -34,6 +34,13 @@
}
+
+
+ Product
+ SetProductNameAsync(version, e)"/>
+
+
@if (isDraft)
{
@@ -112,7 +119,14 @@
version.Price is null
&& version.AcquiredAt is null
&& string.IsNullOrWhiteSpace(version.Vendor)
- && string.IsNullOrWhiteSpace(version.Reference);
+ && string.IsNullOrWhiteSpace(version.Reference)
+ && string.IsNullOrWhiteSpace(version.ProductName);
+
+ private async Task SetProductNameAsync(OwnedVersionDto version, ChangeEventArgs e)
+ {
+ version.ProductName = e.Value?.ToString();
+ await NotifyChangedAsync(version);
+ }
///
Saves after a mutation on a saved copy; a draft's edits stay local until its Save button.
private async Task NotifyChangedAsync(OwnedVersionDto version)
diff --git a/src/BlazorApp/Components/Inventory/Shared/SuggestInput.razor b/src/BlazorApp/Components/Inventory/Shared/SuggestInput.razor
new file mode 100644
index 00000000..b4f71884
--- /dev/null
+++ b/src/BlazorApp/Components/Inventory/Shared/SuggestInput.razor
@@ -0,0 +1,106 @@
+@* Free-text input with a filtered, click-to-fill suggestion dropdown - not a closed choice, the app's own
+ styled stand-in for an HTML
(whose popup is unstyleable native chrome that clashes with the
+ dark theme and floats over content below it - see GearDetail.razor's Category field, the first caller). *@
+
+
+ @if (_open && FilteredSuggestions.Any())
+ {
+
+ }
+
+
+@code {
+ private const int MaxSuggestions = 8;
+
+ [Parameter] public string? Value { get; set; }
+
+ [Parameter] public EventCallback ValueChanged { get; set; }
+
+ [Parameter] public IReadOnlyList Suggestions { get; set; } = [];
+
+ [Parameter] public string? Placeholder { get; set; }
+
+ /// Applied to the wrapper div - lets a caller size this differently than the default
+ /// full-width detail-page field (e.g. Gear.razor's fixed-width list-filter usage).
+ [Parameter] public string? CssClass { get; set; }
+
+ private string _query = "";
+ private string? _lastValueParam;
+ private bool _open;
+
+ // guards OnBlurAsync's delayed close against a suggestion click that's still in flight - see SelectAsync
+ private bool _suppressBlurClose;
+
+ private IEnumerable FilteredSuggestions =>
+ Suggestions.Where(s => string.IsNullOrEmpty(_query) || s.Contains(_query, StringComparison.OrdinalIgnoreCase)).Take(MaxSuggestions);
+
+ protected override void OnParametersSet()
+ {
+ // only adopt an externally-changed Value - typing of our own must never be clobbered by the
+ // re-render our own CommitAsync triggers, same "sent vs received" shape as InventoryList's search box
+ if (Value == _lastValueParam) return;
+ _lastValueParam = Value;
+ _query = Value ?? "";
+ }
+
+ private void OnFocus() => _open = true;
+
+ private void OnInput(ChangeEventArgs e)
+ {
+ _query = e.Value?.ToString() ?? "";
+ _open = true;
+ }
+
+ ///
+ /// A suggestion is a plain, non-focusable <li> rather than a <button>, so clicking it never
+ /// steals focus from the input in the first place - the input's blur handler still fires on a real
+ /// blur (tab away, click elsewhere), just not from this click. The delay/flag pair only has to cover
+ /// touch, where a tap can blur before the click event finishes.
+ ///
+ private async Task SelectAsync(string suggestion)
+ {
+ _suppressBlurClose = true;
+ _query = suggestion;
+ _open = false;
+ await CommitAsync(suggestion);
+ }
+
+ private async Task OnBlurAsync()
+ {
+ await Task.Delay(150);
+ if (_suppressBlurClose)
+ {
+ _suppressBlurClose = false;
+ return;
+ }
+ _open = false;
+ await CommitAsync(_query);
+ }
+
+ private async Task OnKeyDownAsync(KeyboardEventArgs e)
+ {
+ switch (e.Key)
+ {
+ case "Escape":
+ _open = false;
+ break;
+ case "Enter":
+ _open = false;
+ await CommitAsync(_query);
+ break;
+ }
+ }
+
+ private async Task CommitAsync(string? value)
+ {
+ var normalized = string.IsNullOrWhiteSpace(value) ? null : value;
+ if (normalized == Value) return;
+ await ValueChanged.InvokeAsync(normalized);
+ }
+}
diff --git a/src/BlazorApp/Components/Layout/NavMenu.razor b/src/BlazorApp/Components/Layout/NavMenu.razor
index 84706af8..0bfe4727 100644
--- a/src/BlazorApp/Components/Layout/NavMenu.razor
+++ b/src/BlazorApp/Components/Layout/NavMenu.razor
@@ -80,6 +80,16 @@
✚ Health
+
+
+ ▦ Collectibles
+
+
+
+
+ ▧ Gear
+
+
}
else
@@ -94,7 +96,7 @@
private long TotalItems =>
Stats is null
? 0
- : Stats.Movies + Stats.TvShows + Stats.Books + Stats.Albums + Stats.VideoGames + Stats.Playlists + Stats.Cars + Stats.Houses;
+ : Stats.Movies + Stats.TvShows + Stats.Books + Stats.Albums + Stats.VideoGames + Stats.Playlists + Stats.Cars + Stats.Houses + Stats.Collectibles + Stats.Gear;
protected override async Task OnInitializedAsync()
{
diff --git a/src/BlazorApp/Components/ReferenceDataAdmin/InlineReferenceLinker.razor b/src/BlazorApp/Components/ReferenceDataAdmin/InlineReferenceLinker.razor
index bf676dd1..dc073242 100644
--- a/src/BlazorApp/Components/ReferenceDataAdmin/InlineReferenceLinker.razor
+++ b/src/BlazorApp/Components/ReferenceDataAdmin/InlineReferenceLinker.razor
@@ -29,7 +29,7 @@
{
}
- else if (_searched && _results.Count == 0)
+ else if (_searched && _results.Count == 0 && _error is null)
{
No @ProviderName results for this title/year.
}
@@ -126,8 +126,11 @@
{
// an uncaught exception here would crash the whole Blazor Server circuit (not just this
// component), forcing a full page reload - same reasoning as LinkAsync's own try/catch below.
+ // The provider's own exchange (which HTTP client, which status code) is an implementation
+ // detail the admin shouldn't have to parse out of a raw HttpRequestException message -
+ // "Search failed" plus a retry hint says the one thing that's actually actionable.
_results = [];
- _error = ex.Message;
+ _error = $"{ProviderName} search failed ({ex.Message}). This is usually transient - wait a moment and try again.";
}
finally
{
diff --git a/src/BlazorApp/Components/Shared/DateOnlyInput.cs b/src/BlazorApp/Components/Shared/DateOnlyInput.cs
new file mode 100644
index 00000000..4e6d766a
--- /dev/null
+++ b/src/BlazorApp/Components/Shared/DateOnlyInput.cs
@@ -0,0 +1,15 @@
+using System.Globalization;
+
+namespace Keeptrack.BlazorApp.Components.Shared;
+
+///
+/// Shared parser for a plain HTML date input's onchange value - always "yyyy-MM-dd" per the HTML spec
+/// regardless of the browser's locale, so this always parses with the invariant culture rather than the
+/// current one. Used by every owned-copy/car-history/house-history/video-game-completion-style bound
+/// ? field instead of duplicating the same TryParse ternary at each call site.
+///
+public static class DateOnlyInput
+{
+ public static DateOnly? Parse(object? value) =>
+ DateOnly.TryParse(value?.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) ? date : null;
+}
diff --git a/src/BlazorApp/Components/Shared/ExternalLinkIcon.razor b/src/BlazorApp/Components/Shared/ExternalLinkIcon.razor
new file mode 100644
index 00000000..500a7def
--- /dev/null
+++ b/src/BlazorApp/Components/Shared/ExternalLinkIcon.razor
@@ -0,0 +1,14 @@
+@* A monochrome inline SVG "open external link" glyph, same shape as TrashIcon - used for actions that
+ leave the app (e.g. the chasse-aux-livres.fr shortcut on BookDetail.razor). Wrap it in the action's own
+ button/anchor; this is just the glyph. *@
+
+
+
+
+
+
+
+@code {
+ [Parameter] public int Size { get; set; } = 15;
+}
diff --git a/src/BlazorApp/Components/Shared/ReferenceRefreshMessage.cs b/src/BlazorApp/Components/Shared/ReferenceRefreshMessage.cs
new file mode 100644
index 00000000..d4d5facf
--- /dev/null
+++ b/src/BlazorApp/Components/Shared/ReferenceRefreshMessage.cs
@@ -0,0 +1,23 @@
+namespace Keeptrack.BlazorApp.Components.Shared;
+
+///
+/// Shared "check for reference match" result message for every reference-linked detail page's refresh-reference
+/// button (Movie/TvShow/Book/Album/VideoGame) - keeps the message/style rule in one place instead of duplicating
+/// the same four-way outcome across five detail pages.
+///
+public static class ReferenceRefreshMessage
+{
+ public static (string Message, string Style) Compute(string? previousReferenceId, string? newReferenceId)
+ {
+ if (string.IsNullOrEmpty(newReferenceId))
+ {
+ return string.IsNullOrEmpty(previousReferenceId)
+ ? ("No match found", "neutral")
+ : ("Unlinked - no match", "danger");
+ }
+
+ return newReferenceId == previousReferenceId
+ ? ("Already linked", "neutral")
+ : ("Linked!", "success");
+ }
+}
diff --git a/src/BlazorApp/Components/Shared/SvgChartHelpers.cs b/src/BlazorApp/Components/Shared/SvgChartHelpers.cs
index 27d2a5b2..663f11e3 100644
--- a/src/BlazorApp/Components/Shared/SvgChartHelpers.cs
+++ b/src/BlazorApp/Components/Shared/SvgChartHelpers.cs
@@ -29,6 +29,13 @@ public readonly record struct ChartGeometry(
public static readonly ChartGeometry FullWidthGeometry =
new(ViewWidth: 600, ViewHeight: 170, PlotLeft: 40, PlotRight: 588, PlotTop: 14, PlotBottom: 132);
+ private const string AttrStroke = "stroke";
+ private const string AttrStrokeWidth = "stroke-width";
+ private const string AttrVectorEffect = "vector-effect";
+ private const string NonScalingStroke = "non-scaling-stroke";
+ private const string AttrTextAnchor = "text-anchor";
+ private const string AttrClass = "class";
+
///
/// Draws a graduated X/Y axis pair (arrowhead, tick marks, tick labels, axis title).
/// Ticks are computed by the caller, since what counts as an evenly-spaced value differs between a continuous line chart and a per-bar categorical one.
@@ -65,9 +72,9 @@ public static void RenderAxes(
builder.AddAttribute(seq++, "y1", plotBottom.ToString("F1"));
builder.AddAttribute(seq++, "x2", plotLeft.ToString("F1"));
builder.AddAttribute(seq++, "y2", plotTop.ToString("F1"));
- builder.AddAttribute(seq++, "stroke", AxisColor);
- builder.AddAttribute(seq++, "stroke-width", "1");
- builder.AddAttribute(seq++, "vector-effect", "non-scaling-stroke");
+ builder.AddAttribute(seq++, AttrStroke, AxisColor);
+ builder.AddAttribute(seq++, AttrStrokeWidth, "1");
+ builder.AddAttribute(seq++, AttrVectorEffect, NonScalingStroke);
builder.AddAttribute(seq++, "marker-end", $"url(#{markerId})");
builder.CloseElement();
@@ -77,9 +84,9 @@ public static void RenderAxes(
builder.AddAttribute(seq++, "y1", plotBottom.ToString("F1"));
builder.AddAttribute(seq++, "x2", plotRight.ToString("F1"));
builder.AddAttribute(seq++, "y2", plotBottom.ToString("F1"));
- builder.AddAttribute(seq++, "stroke", AxisColor);
- builder.AddAttribute(seq++, "stroke-width", "1");
- builder.AddAttribute(seq++, "vector-effect", "non-scaling-stroke");
+ builder.AddAttribute(seq++, AttrStroke, AxisColor);
+ builder.AddAttribute(seq++, AttrStrokeWidth, "1");
+ builder.AddAttribute(seq++, AttrVectorEffect, NonScalingStroke);
builder.AddAttribute(seq++, "marker-end", $"url(#{markerId})");
builder.CloseElement();
@@ -90,16 +97,16 @@ public static void RenderAxes(
builder.AddAttribute(seq++, "y1", y.ToString("F1"));
builder.AddAttribute(seq++, "x2", plotLeft.ToString("F1"));
builder.AddAttribute(seq++, "y2", y.ToString("F1"));
- builder.AddAttribute(seq++, "stroke", AxisColor);
- builder.AddAttribute(seq++, "stroke-width", "1");
- builder.AddAttribute(seq++, "vector-effect", "non-scaling-stroke");
+ builder.AddAttribute(seq++, AttrStroke, AxisColor);
+ builder.AddAttribute(seq++, AttrStrokeWidth, "1");
+ builder.AddAttribute(seq++, AttrVectorEffect, NonScalingStroke);
builder.CloseElement();
builder.OpenElement(seq++, "text");
builder.AddAttribute(seq++, "x", (plotLeft - 5).ToString("F1"));
builder.AddAttribute(seq++, "y", (y + 2.5).ToString("F1"));
- builder.AddAttribute(seq++, "text-anchor", "end");
- builder.AddAttribute(seq++, "class", "kt-chart-axis-text");
+ builder.AddAttribute(seq++, AttrTextAnchor, "end");
+ builder.AddAttribute(seq++, AttrClass, "kt-chart-axis-text");
builder.AddContent(seq++, label);
builder.CloseElement();
}
@@ -111,16 +118,16 @@ public static void RenderAxes(
builder.AddAttribute(seq++, "y1", plotBottom.ToString("F1"));
builder.AddAttribute(seq++, "x2", x.ToString("F1"));
builder.AddAttribute(seq++, "y2", (plotBottom + 3).ToString("F1"));
- builder.AddAttribute(seq++, "stroke", AxisColor);
- builder.AddAttribute(seq++, "stroke-width", "1");
- builder.AddAttribute(seq++, "vector-effect", "non-scaling-stroke");
+ builder.AddAttribute(seq++, AttrStroke, AxisColor);
+ builder.AddAttribute(seq++, AttrStrokeWidth, "1");
+ builder.AddAttribute(seq++, AttrVectorEffect, NonScalingStroke);
builder.CloseElement();
builder.OpenElement(seq++, "text");
builder.AddAttribute(seq++, "x", x.ToString("F1"));
builder.AddAttribute(seq++, "y", (plotBottom + 12).ToString("F1"));
- builder.AddAttribute(seq++, "text-anchor", "middle");
- builder.AddAttribute(seq++, "class", "kt-chart-axis-text");
+ builder.AddAttribute(seq++, AttrTextAnchor, "middle");
+ builder.AddAttribute(seq++, AttrClass, "kt-chart-axis-text");
builder.AddContent(seq++, label);
builder.CloseElement();
}
@@ -129,8 +136,8 @@ public static void RenderAxes(
builder.OpenElement(seq++, "text");
builder.AddAttribute(seq++, "x", "2");
builder.AddAttribute(seq++, "y", (plotTop - 4).ToString("F1"));
- builder.AddAttribute(seq++, "text-anchor", "start");
- builder.AddAttribute(seq++, "class", "kt-chart-axis-title");
+ builder.AddAttribute(seq++, AttrTextAnchor, "start");
+ builder.AddAttribute(seq++, AttrClass, "kt-chart-axis-title");
builder.AddContent(seq++, yAxisLabel);
builder.CloseElement();
@@ -138,8 +145,8 @@ public static void RenderAxes(
builder.OpenElement(seq++, "text");
builder.AddAttribute(seq++, "x", xTitleCenter.ToString("F1"));
builder.AddAttribute(seq++, "y", (viewHeight - 4).ToString("F1"));
- builder.AddAttribute(seq++, "text-anchor", "middle");
- builder.AddAttribute(seq++, "class", "kt-chart-axis-title");
+ builder.AddAttribute(seq++, AttrTextAnchor, "middle");
+ builder.AddAttribute(seq++, AttrClass, "kt-chart-axis-title");
builder.AddContent(seq++, xAxisLabel);
builder.CloseElement();
}
diff --git a/src/BlazorApp/Components/WatchNext/WatchNextPage.razor b/src/BlazorApp/Components/WatchNext/WatchNextPage.razor
index 78aad0d2..4b32c84e 100644
--- a/src/BlazorApp/Components/WatchNext/WatchNextPage.razor
+++ b/src/BlazorApp/Components/WatchNext/WatchNextPage.razor
@@ -99,6 +99,15 @@ else if (Data is not null)
[Inject] private WatchNextApiClient WatchNextApi { get; set; } = null!;
+ [Inject] private NavigationManager Navigation { get; set; } = null!;
+
+ // Persisted in the URL (?tab=) the same way list pages persist search/page/filters - see
+ // InventoryPageBase's ApplyQueryChanges/SetFilter for the pattern this mirrors. Unlike a list page's
+ // query params, changing this one never triggers a reload: Data isn't query-dependent here (WatchNextDto
+ // is one full, unpaged aggregate), so OnParametersSet only ever reparses the tab, it never calls LoadAsync.
+ [SupplyParameterFromQuery(Name = "tab")]
+ public string? TabQuery { get; set; }
+
// _loading is delay-gated (see LoadingIndicator) and only turns on for a load that's genuinely slow.
// _loaded tracks whether a load attempt has finished at all - both default false so the forced
// render Blazor triggers right after the synchronous prefix of OnInitializedAsync (before any
@@ -117,6 +126,8 @@ else if (Data is not null)
protected override Task OnInitializedAsync() => LoadAsync();
+ protected override void OnParametersSet() => _tab = Enum.TryParse(TabQuery, out var tab) ? tab : Tab.TvShows;
+
private async Task LoadAsync()
{
await LoadingIndicator.RunAsync(FetchAsync(), v => _loading = v, StateHasChanged);
@@ -129,5 +140,6 @@ else if (Data is not null)
Data = await WatchNextApi.GetAsync();
}
- private void SelectTab(Tab tab) => _tab = tab;
+ private void SelectTab(Tab tab) =>
+ Navigation.NavigateTo(Navigation.GetUriWithQueryParameters(new Dictionary { ["tab"] = tab.ToString() }));
}
diff --git a/src/BlazorApp/Components/Wishlist/WishlistPage.razor b/src/BlazorApp/Components/Wishlist/WishlistPage.razor
index fd125c29..a03d8f7b 100644
--- a/src/BlazorApp/Components/Wishlist/WishlistPage.razor
+++ b/src/BlazorApp/Components/Wishlist/WishlistPage.razor
@@ -110,6 +110,13 @@ else if (Data is not null)
[Inject] private IJSRuntime JsRuntime { get; set; } = null!;
+ // Persisted in the URL (?tab=) the same way list pages persist search/page/filters - see
+ // InventoryPageBase's ApplyQueryChanges/SetFilter for the pattern this mirrors. Unlike a list page's
+ // query params, changing this one never triggers a reload: Data isn't query-dependent here (WishlistDto
+ // is one full, unpaged aggregate), so OnParametersSet only ever reparses the tab, it never calls LoadAsync.
+ [SupplyParameterFromQuery(Name = "tab")]
+ public string? TabQuery { get; set; }
+
// _loading is delay-gated (see LoadingIndicator) and only turns on for a load that's genuinely slow.
// _loaded tracks whether a load attempt has finished at all - both default false so the forced
// render Blazor triggers right after the synchronous prefix of OnInitializedAsync (before any
@@ -136,6 +143,8 @@ else if (Data is not null)
protected override Task OnInitializedAsync() => LoadAsync();
+ protected override void OnParametersSet() => _tab = Enum.TryParse(TabQuery, out var tab) ? tab : Tab.Movies;
+
private async Task LoadAsync()
{
await LoadingIndicator.RunAsync(FetchAsync(), v => _loading = v, StateHasChanged);
@@ -148,7 +157,8 @@ else if (Data is not null)
Data = await WishlistApi.GetAsync();
}
- private void SelectTab(Tab tab) => _tab = tab;
+ private void SelectTab(Tab tab) =>
+ Navigation.NavigateTo(Navigation.GetUriWithQueryParameters(new Dictionary { ["tab"] = tab.ToString() }));
private async Task ToggleSharePanelAsync()
{
diff --git a/src/BlazorApp/DependencyInjection/InfrastructureServiceCollectionExtensions.cs b/src/BlazorApp/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
index 53dff133..2ebff0f0 100644
--- a/src/BlazorApp/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
+++ b/src/BlazorApp/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
@@ -21,6 +21,10 @@ internal static void AddWebApiHttpClient(this IServiceCollection services, strin
.AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
+ services.AddHttpClient(client => client.BaseAddress = webApiUri)
+ .AddHttpMessageHandler();
+ services.AddHttpClient(client => client.BaseAddress = webApiUri)
+ .AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
@@ -43,12 +47,18 @@ internal static void AddWebApiHttpClient(this IServiceCollection services, strin
.AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
+ services.AddHttpClient(client => client.BaseAddress = webApiUri)
+ .AddHttpMessageHandler();
+ services.AddHttpClient(client => client.BaseAddress = webApiUri)
+ .AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
services.AddHttpClient(client => client.BaseAddress = webApiUri)
.AddHttpMessageHandler();
+ services.AddHttpClient(client => client.BaseAddress = webApiUri)
+ .AddHttpMessageHandler();
// deliberately NO AuthenticationTokenHandler: the shared-wishlist view is anonymous by design
services.AddHttpClient(client => client.BaseAddress = webApiUri);
}
diff --git a/src/BlazorApp/Program.cs b/src/BlazorApp/Program.cs
index f9dd2da6..19247232 100644
--- a/src/BlazorApp/Program.cs
+++ b/src/BlazorApp/Program.cs
@@ -43,6 +43,7 @@
}
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddWebApiHttpClient(builder.Configuration.TryGetSection("WebApi:BaseUrl"));
builder.Services.AddHealthChecks();
diff --git a/src/BlazorApp/wwwroot/app.css b/src/BlazorApp/wwwroot/app.css
index ba38720b..399b7aea 100644
--- a/src/BlazorApp/wwwroot/app.css
+++ b/src/BlazorApp/wwwroot/app.css
@@ -601,6 +601,28 @@ a.kt-item-row { color: inherit; text-decoration: none; }
.kt-album-cover { width: 200px; height: 200px; }
}
+/* Gear/Collectible detail cover: full-width, on top of the fields (unlike .kt-album-hero's side-by-side
+ layout) - sized and fitted for e-shop-style product photography (e.g. an Amazon listing photo), which is
+ usually a product shot on a plain background rather than a poster/album-art image with a fixed aspect
+ ratio. "contain" (not "cover") so the product is never cropped. No background/border - just the image,
+ centered and capped to a sensible height. */
+.kt-product-cover-box {
+ width: 100%;
+ height: 480px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.kt-product-cover-box img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+}
+@media (max-width: 767px) {
+ .kt-product-cover-box { height: 320px; }
+}
+
/* compact aligned row for a list of tracks/songs inside a .kt-form-card (e.g. a playlist's song list) -
a CSS grid so title/artist/album line up in columns across rows without resorting to a full
.kt-table-wrap/, which reads as a top-level list page rather than a detail-page sub-section. */
@@ -976,6 +998,60 @@ a.kt-item-row { color: inherit; text-decoration: none; }
flex: 0 0 auto;
}
+/* Gear's category filter, sizing SuggestInput.razor's wrapper to a fixed compact width instead of the
+ .form-control default of 100% - a percentage width inside a flex row (like .kt-search-filters) resolves
+ against the row's own size, which is what pushed the whole filters row onto a wrapped second line for
+ no reason. Same fixed-width-flex-item shape as .kt-sort-select above, just wide enough for typed text. */
+.kt-category-select {
+ width: 150px;
+ flex: 0 0 auto;
+}
+
+@media (max-width: 576px) {
+ .kt-category-select { width: 45vw; }
+}
+
+/* ── Suggest input (SuggestInput.razor) ───────────────────────────────
+ A plain text input with a filtered, click-to-fill suggestion menu - the app's own dark-themed
+ replacement for an HTML popup, which is unstyleable browser chrome that ignores
+ data-bs-theme and floats over whatever markup sits below it. */
+.kt-autocomplete {
+ position: relative;
+}
+.kt-autocomplete-menu {
+ position: absolute;
+ z-index: 20;
+ top: calc(100% + 4px);
+ left: 0;
+ right: 0;
+ margin: 0;
+ padding: 0.25rem;
+ list-style: none;
+ max-height: 220px;
+ overflow-y: auto;
+ background: var(--kt-surface-2);
+ border: 1px solid var(--kt-border);
+ border-radius: var(--kt-radius);
+ box-shadow: var(--kt-shadow);
+}
+.kt-autocomplete-item {
+ padding: 0.4rem 0.65rem;
+ border-radius: calc(var(--kt-radius) - 4px);
+ font-size: 0.85rem;
+ color: var(--kt-text);
+ cursor: pointer;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.kt-autocomplete-item:hover {
+ background: var(--kt-surface-3);
+}
+.kt-autocomplete-item.active {
+ background: var(--kt-accent-glow);
+ color: var(--kt-accent);
+}
+
.kt-search-filters {
display: flex;
align-items: center;
diff --git a/src/Common.System/AmazonReference.cs b/src/Common.System/AmazonReference.cs
new file mode 100644
index 00000000..f9c57d50
--- /dev/null
+++ b/src/Common.System/AmazonReference.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace Keeptrack.Common.System;
+
+///
+/// The read side of an Amazon-imported owned copy's Reference text (written by
+/// AmazonImportMergeService.FormatOrderReference in the Domain project as
+/// "Amazon order {orderId} (ASIN {asin})" ). Lives here, not next to the writer, because it also
+/// needs to be callable from BlazorApp (which can't reference Domain) to build an "open on Amazon" link -
+/// keeping both halves of the same string shape in one place avoids the two drifting apart independently.
+///
+public static partial class AmazonReference
+{
+ [GeneratedRegex(@"\(ASIN\s+([A-Za-z0-9]{10})\)")]
+ private static partial Regex AsinPattern();
+
+ ///
+ /// The ASIN embedded in an owned copy's Reference text, or null when it isn't there (most
+ /// copies weren't imported from Amazon at all).
+ ///
+ public static string? TryExtractAsin(string? reference)
+ {
+ if (string.IsNullOrEmpty(reference))
+ {
+ return null;
+ }
+
+ var match = AsinPattern().Match(reference);
+ return match.Success ? match.Groups[1].Value : null;
+ }
+
+ ///
+ /// Amazon's own permalink shape (works on any of its marketplaces). is the
+ /// owned copy's own Vendor field - for an Amazon-imported copy this is the CSV export's "Website"
+ /// column (e.g. "Amazon.fr"), so a mixed-marketplace order history still opens on the right site
+ /// instead of always guessing one locale. Falls back to amazon.fr when the vendor isn't recognizably
+ /// an Amazon domain (a manually-entered copy, or an older import that didn't carry one).
+ ///
+ public static string BuildProductUrl(string asin, string? vendor) => $"https://{TryGetAmazonDomain(vendor) ?? "www.amazon.fr"}/dp/{asin}";
+
+ private static string? TryGetAmazonDomain(string? vendor)
+ {
+ var trimmed = vendor?.Trim();
+ if (string.IsNullOrEmpty(trimmed) || !trimmed.Contains("amazon", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ var withoutScheme = SchemePrefix().Replace(trimmed, "");
+ return withoutScheme.Split('/')[0].ToLowerInvariant();
+ }
+
+ [GeneratedRegex(@"^https?://", RegexOptions.IgnoreCase)]
+ private static partial Regex SchemePrefix();
+}
diff --git a/src/Common.System/ListSort.cs b/src/Common.System/ListSort.cs
index ea4fd8ea..bf83392f 100644
--- a/src/Common.System/ListSort.cs
+++ b/src/Common.System/ListSort.cs
@@ -22,4 +22,7 @@ public static class ListSort
/// Most recently completed video game first (max CompletedAt across a game's platform entries); items with none last.
public const string LastCompleted = "completed";
+
+ /// Most recently bought gear first (max AcquiredAt across a gear item's owned versions); items with none last.
+ public const string Bought = "bought";
}
diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj
index ff76858c..9505dfaf 100644
--- a/src/Domain/Domain.csproj
+++ b/src/Domain/Domain.csproj
@@ -11,4 +11,10 @@
+
+
+
+
+
diff --git a/src/Domain/Models/AlbumModel.cs b/src/Domain/Models/AlbumModel.cs
index c4a57070..f93ebf88 100644
--- a/src/Domain/Models/AlbumModel.cs
+++ b/src/Domain/Models/AlbumModel.cs
@@ -21,6 +21,13 @@ public class AlbumModel : IHasIdAndOwnerId
public string? ReferenceId { get; set; }
+ ///
+ /// Tenant-owned cover image override - takes priority over the linked reference's own cover wherever
+ /// a cover is shown (list thumbnail, detail page). Null means "use the reference's cover, if any" -
+ /// the previous, only behavior.
+ ///
+ public string? CustomImageUrl { get; set; }
+
public bool IsFavorite { get; set; }
public List OwnedVersions { get; set; } = [];
diff --git a/src/Domain/Models/AmazonOrderPreviewRow.cs b/src/Domain/Models/AmazonOrderPreviewRow.cs
new file mode 100644
index 00000000..e2ebaa2d
--- /dev/null
+++ b/src/Domain/Models/AmazonOrderPreviewRow.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace Keeptrack.Domain.Models;
+
+///
+/// One line item parsed from an Amazon order-history export, before the user has reviewed/selected it.
+/// Transient - never persisted. Produced by and mapped
+/// to AmazonOrderPreviewRowDto by AmazonOrderPreviewRowDtoMapper for the review UI.
+///
+public class AmazonOrderPreviewRow
+{
+ ///
+ /// "{OrderId}:{Asin}" - stable within one export, used only to correlate a selected/edited row
+ /// back to this one at commit time. Never stored.
+ ///
+ public required string RowId { get; set; }
+
+ /// Amazon's product name, with the export's mojibake repaired.
+ public required string Title { get; set; }
+
+ public required string Asin { get; set; }
+
+ public required string OrderId { get; set; }
+
+ public DateOnly? OrderDate { get; set; }
+
+ /// What was actually paid for this item - tax and any per-item discount already netted in.
+ public decimal? Price { get; set; }
+
+ public required string Vendor { get; set; }
+
+ /// Display only ("New", "Used"...) - not carried onto the created book.
+ public string? Condition { get; set; }
+
+ /// True when passes an ISBN-10 checksum - the review table's default filter.
+ public bool LooksLikeBook { get; set; }
+
+ /// again, only when is true.
+ public string? SuggestedIsbn { get; set; }
+
+ ///
+ /// True when an existing book already has an owned version referencing this order - see
+ /// .
+ ///
+ public bool AlreadyImported { get; set; }
+}
diff --git a/src/Domain/Models/AmazonOwnedItemImportRequestItem.cs b/src/Domain/Models/AmazonOwnedItemImportRequestItem.cs
new file mode 100644
index 00000000..d170795b
--- /dev/null
+++ b/src/Domain/Models/AmazonOwnedItemImportRequestItem.cs
@@ -0,0 +1,26 @@
+namespace Keeptrack.Domain.Models;
+
+///
+/// One user-selected/edited row from the review UI, already translated from the web contract - the input
+/// to for the domains
+/// that use (Book, Movie, TvShow, Gear, Collectible). See
+/// for VideoGame's own shape.
+///
+public class AmazonOwnedItemImportRequestItem
+{
+ public required string Title { get; set; }
+
+ ///
+ /// The title exactly as Amazon listed it (before any edit the user made in the review UI) - preserved
+ /// in the created item's notes, since reference-data linking is expected to overwrite
+ /// later.
+ ///
+ public required string AmazonTitle { get; set; }
+
+ public int? Year { get; set; }
+
+ /// Book-only (null for Movie/TvShow/Gear/Collectible) - see .
+ public string? Isbn { get; set; }
+
+ public required OwnedVersionModel OwnedVersion { get; set; }
+}
diff --git a/src/Domain/Models/AmazonVideoGameImportRequestItem.cs b/src/Domain/Models/AmazonVideoGameImportRequestItem.cs
new file mode 100644
index 00000000..1a53c3eb
--- /dev/null
+++ b/src/Domain/Models/AmazonVideoGameImportRequestItem.cs
@@ -0,0 +1,18 @@
+namespace Keeptrack.Domain.Models;
+
+///
+/// VideoGame's own shape for -
+/// has no concept, using
+/// (with a required platform name Amazon's export can never supply)
+/// instead. See for Book/Movie/TvShow's shared shape.
+///
+public class AmazonVideoGameImportRequestItem
+{
+ public required string Title { get; set; }
+
+ public required string AmazonTitle { get; set; }
+
+ public int? Year { get; set; }
+
+ public required VideoGamePlatformModel Platform { get; set; }
+}
diff --git a/src/Domain/Models/CarModel.cs b/src/Domain/Models/CarModel.cs
index c4451401..014075a5 100644
--- a/src/Domain/Models/CarModel.cs
+++ b/src/Domain/Models/CarModel.cs
@@ -19,4 +19,9 @@ public class CarModel : IHasIdAndOwnerId
public string? LicensePlate { get; set; }
public required CarEnergyType EnergyType { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Domain/Models/CollectibleModel.cs b/src/Domain/Models/CollectibleModel.cs
new file mode 100644
index 00000000..0cbd1ba0
--- /dev/null
+++ b/src/Domain/Models/CollectibleModel.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+
+namespace Keeptrack.Domain.Models;
+
+public class CollectibleModel : IHasIdAndOwnerId
+{
+ public string? Id { get; set; }
+
+ public required string OwnerId { get; set; }
+
+ public required string Title { get; set; }
+
+ public string? Brand { get; set; }
+
+ public int? Year { get; set; }
+
+ public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
+
+ public bool IsFavorite { get; set; }
+
+ public List OwnedVersions { get; set; } = [];
+
+ ///
+ /// Filter-only: matches if is non-empty. Never persisted - see
+ /// .
+ ///
+ public bool IsOwned { get; set; }
+}
diff --git a/src/Domain/Models/GearModel.cs b/src/Domain/Models/GearModel.cs
new file mode 100644
index 00000000..6838a53b
--- /dev/null
+++ b/src/Domain/Models/GearModel.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+
+namespace Keeptrack.Domain.Models;
+
+public class GearModel : IHasIdAndOwnerId
+{
+ public string? Id { get; set; }
+
+ public required string OwnerId { get; set; }
+
+ public required string Title { get; set; }
+
+ public string? Brand { get; set; }
+
+ ///
+ /// Free-text category (e.g. "Electronics", "Camping") - suggested from the tenant's other gear but
+ /// not a closed list. Unlike a blog's tags, one gear item has exactly one category.
+ ///
+ public string? Category { get; set; }
+
+ public int? Year { get; set; }
+
+ public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
+
+ public bool IsFavorite { get; set; }
+
+ public List OwnedVersions { get; set; } = [];
+
+ ///
+ /// Filter-only: matches if is non-empty. Never persisted - see
+ /// .
+ ///
+ public bool IsOwned { get; set; }
+}
diff --git a/src/Domain/Models/GenericVideoGameImportPreviewRow.cs b/src/Domain/Models/GenericVideoGameImportPreviewRow.cs
new file mode 100644
index 00000000..47f7638a
--- /dev/null
+++ b/src/Domain/Models/GenericVideoGameImportPreviewRow.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Keeptrack.Domain.Models;
+
+///
+/// One line item parsed from a generic video game transaction-history CSV (store purchase history - PSN
+/// today, any store exporting the same shape later), for the user to review before commit. See
+/// .
+///
+public class GenericVideoGameImportPreviewRow
+{
+ /// The transaction id - unique per line item in every export seen so far.
+ public required string RowId { get; set; }
+
+ /// The game title, cleaned of a trailing platform suffix (e.g. "(PS4)") when present.
+ public required string Title { get; set; }
+
+ public required string Platform { get; set; }
+
+ /// The store's own specific product/edition text (e.g. "Grand Theft Auto V : Édition Premium").
+ public string? ProductName { get; set; }
+
+ public required string Vendor { get; set; }
+
+ public required string TransactionId { get; set; }
+
+ public required string OrderId { get; set; }
+
+ public DateOnly? TransactionDate { get; set; }
+
+ public decimal? Price { get; set; }
+
+ public bool AlreadyImported { get; set; }
+}
diff --git a/src/Domain/Models/GenericVideoGameImportRequestItem.cs b/src/Domain/Models/GenericVideoGameImportRequestItem.cs
new file mode 100644
index 00000000..9ec655c2
--- /dev/null
+++ b/src/Domain/Models/GenericVideoGameImportRequestItem.cs
@@ -0,0 +1,21 @@
+namespace Keeptrack.Domain.Models;
+
+///
+/// One user-selected/edited row from the generic video game import review UI, already translated from the
+/// web contract - the input to .
+///
+public class GenericVideoGameImportRequestItem
+{
+ public required string Title { get; set; }
+
+ ///
+ /// The title exactly as the source export listed it (before any edit the user made in the review UI) -
+ /// preserved in the created item's notes, since reference-data linking is expected to overwrite
+ /// later.
+ ///
+ public required string SourceTitle { get; set; }
+
+ public int? Year { get; set; }
+
+ public required VideoGamePlatformModel Platform { get; set; }
+}
diff --git a/src/Domain/Models/HealthProfileModel.cs b/src/Domain/Models/HealthProfileModel.cs
index 4299689d..ba0fdda1 100644
--- a/src/Domain/Models/HealthProfileModel.cs
+++ b/src/Domain/Models/HealthProfileModel.cs
@@ -16,4 +16,9 @@ public class HealthProfileModel : IHasIdAndOwnerId
public required string Name { get; set; }
public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Domain/Models/HouseModel.cs b/src/Domain/Models/HouseModel.cs
index a35ab155..d322272b 100644
--- a/src/Domain/Models/HouseModel.cs
+++ b/src/Domain/Models/HouseModel.cs
@@ -22,4 +22,9 @@ public class HouseModel : IHasIdAndOwnerId
public DateOnly? MovedOutAt { get; set; }
public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Domain/Models/ImportCommitPlan.cs b/src/Domain/Models/ImportCommitPlan.cs
new file mode 100644
index 00000000..52906464
--- /dev/null
+++ b/src/Domain/Models/ImportCommitPlan.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+
+namespace Keeptrack.Domain.Models;
+
+///
+/// What decided:
+/// brand new items to create, and existing (or already-created-this-batch) items to update - each already
+/// carrying the extra owned copy appended (an or a
+/// , depending on ).
+///
+public class ImportCommitPlan
+{
+ public List ItemsToCreate { get; set; } = [];
+
+ public List ItemsToUpdate { get; set; } = [];
+
+ ///
+ /// Every row that successfully got an owned copy added - the true per-row count, unlike
+ /// / 's counts, which are per distinct item.
+ /// Several selected rows sharing a title can consolidate into a single newly-created item within one
+ /// commit batch, which is not a data loss (every one of those rows still got its owned copy), but does
+ /// make ItemsToCreate.Count + ItemsToUpdate.Count come out lower than the number of selected rows.
+ /// OwnedCopiesAdded + OwnedCopiesSkipped always equals the number of rows submitted - the
+ /// reconciling total a caller can show the user to prove nothing was silently dropped.
+ ///
+ public int OwnedCopiesAdded { get; set; }
+
+ /// A row whose reference already matched an existing owned copy - not duplicated.
+ public int OwnedCopiesSkipped { get; set; }
+
+ /// The title of each row counted in , in submission order - lets a
+ /// caller show the user exactly which selected rows were treated as already-imported duplicates.
+ public List SkippedTitles { get; set; } = [];
+}
diff --git a/src/Domain/Models/OwnedVersionModel.cs b/src/Domain/Models/OwnedVersionModel.cs
index e2bdf3ef..a98bc604 100644
--- a/src/Domain/Models/OwnedVersionModel.cs
+++ b/src/Domain/Models/OwnedVersionModel.cs
@@ -31,4 +31,10 @@ public class OwnedVersionModel
/// Unrelated to the reference-data ReferenceId concept.
///
public string? Reference { get; set; }
+
+ ///
+ /// The store's own specific product/edition text for this copy (e.g. a retailer's listing title),
+ /// distinct from the item's own Title. Optional free text.
+ ///
+ public string? ProductName { get; set; }
}
diff --git a/src/Domain/Models/UserPreferencesFeaturesModel.cs b/src/Domain/Models/UserPreferencesFeaturesModel.cs
new file mode 100644
index 00000000..efd6c0c3
--- /dev/null
+++ b/src/Domain/Models/UserPreferencesFeaturesModel.cs
@@ -0,0 +1,23 @@
+namespace Keeptrack.Domain.Models;
+
+///
+/// The actual opt-in/opt-out toggles held by . A new feature of
+/// this kind adds its own named boolean property here, the same convention every other flag in this
+/// codebase already follows (e.g. ) - not a generic settings dictionary.
+///
+public class UserPreferencesFeaturesModel
+{
+ ///
+ /// Shows an "open in a new tab" link to https://www.chasse-aux-livres.fr/{isbn} next to a book's ISBN
+ /// field on BookDetail.razor , for users who use that site to price-check/verify French books.
+ ///
+ public bool ShowChasseAuxLivresLink { get; set; }
+
+ ///
+ /// Shows an "open on Amazon" link next to an owned copy's Reference field
+ /// (OwnedVersionFields.razor , shared by every owned-copy type) whenever
+ /// finds an ASIN in it - i.e. the copy was
+ /// created by the Amazon order-history import.
+ ///
+ public bool ShowAmazonProductLink { get; set; }
+}
diff --git a/src/Domain/Models/UserPreferencesModel.cs b/src/Domain/Models/UserPreferencesModel.cs
new file mode 100644
index 00000000..355023b0
--- /dev/null
+++ b/src/Domain/Models/UserPreferencesModel.cs
@@ -0,0 +1,17 @@
+using Keeptrack.Common.System;
+
+namespace Keeptrack.Domain.Models;
+
+///
+/// One document per user (see ). The opt-in/opt-out
+/// toggles themselves live under , not as flat properties here, so a new one only
+/// ever means adding a property to .
+///
+public class UserPreferencesModel : IHasIdAndOwnerId
+{
+ public string? Id { get; set; }
+
+ public required string OwnerId { get; set; }
+
+ public UserPreferencesFeaturesModel Features { get; set; } = new();
+}
diff --git a/src/Domain/Models/VideoGameModel.cs b/src/Domain/Models/VideoGameModel.cs
index cdb1a999..717612b7 100644
--- a/src/Domain/Models/VideoGameModel.cs
+++ b/src/Domain/Models/VideoGameModel.cs
@@ -21,6 +21,13 @@ public class VideoGameModel : IHasIdAndOwnerId
public string? ReferenceId { get; set; }
+ ///
+ /// Tenant-owned cover image override - takes priority over the linked reference's own cover wherever
+ /// a cover is shown (list thumbnail, detail page). Null means "use the reference's cover, if any" -
+ /// the previous, only behavior.
+ ///
+ public string? CustomImageUrl { get; set; }
+
///
/// Filter-only: matches if is non-empty. Never persisted - a platform entry
/// (with its own ) is this type's owned copy, so ownership derives from having
diff --git a/src/Domain/Models/VideoGamePlatformModel.cs b/src/Domain/Models/VideoGamePlatformModel.cs
index d517d9ce..35436678 100644
--- a/src/Domain/Models/VideoGamePlatformModel.cs
+++ b/src/Domain/Models/VideoGamePlatformModel.cs
@@ -47,4 +47,11 @@ public class VideoGamePlatformModel
/// Unrelated to the reference-data ReferenceId concept.
///
public string? Reference { get; set; }
+
+ ///
+ /// The store's own specific product/edition text for this copy (e.g. "Grand Theft Auto V : Édition
+ /// Premium"), as opposed to the game's own . Populated by the generic
+ /// video game transaction import from its "Product Name" column.
+ ///
+ public string? ProductName { get; set; }
}
diff --git a/src/Domain/Repositories/IAlbumReferenceRepository.cs b/src/Domain/Repositories/IAlbumReferenceRepository.cs
index 449480a0..d2f81a3a 100644
--- a/src/Domain/Repositories/IAlbumReferenceRepository.cs
+++ b/src/Domain/Repositories/IAlbumReferenceRepository.cs
@@ -45,4 +45,10 @@ public interface IAlbumReferenceRepository
/// full unpaged read is fine.
///
Task> FindAllAsync();
+
+ ///
+ /// Permanently removes a reference document - backs the admin "unlink" action, which deletes the
+ /// shared document outright rather than merely detaching one tenant's link.
+ ///
+ Task DeleteAsync(string id);
}
diff --git a/src/Domain/Repositories/IBookReferenceRepository.cs b/src/Domain/Repositories/IBookReferenceRepository.cs
index d19aea9d..919af338 100644
--- a/src/Domain/Repositories/IBookReferenceRepository.cs
+++ b/src/Domain/Repositories/IBookReferenceRepository.cs
@@ -45,4 +45,10 @@ public interface IBookReferenceRepository
/// full unpaged read is fine.
///
Task> FindAllAsync();
+
+ ///
+ /// Permanently removes a reference document - backs the admin "unlink" action, which deletes the
+ /// shared document outright rather than merely detaching one tenant's link.
+ ///
+ Task DeleteAsync(string id);
}
diff --git a/src/Domain/Repositories/ICollectibleRepository.cs b/src/Domain/Repositories/ICollectibleRepository.cs
new file mode 100644
index 00000000..a9e243eb
--- /dev/null
+++ b/src/Domain/Repositories/ICollectibleRepository.cs
@@ -0,0 +1,7 @@
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Repositories;
+
+public interface ICollectibleRepository : IDataRepository
+{
+}
diff --git a/src/Domain/Repositories/IGearRepository.cs b/src/Domain/Repositories/IGearRepository.cs
new file mode 100644
index 00000000..7c3bcfbd
--- /dev/null
+++ b/src/Domain/Repositories/IGearRepository.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Repositories;
+
+public interface IGearRepository : IDataRepository
+{
+ ///
+ /// Distinct, non-empty values across this tenant's gear, sorted
+ /// alphabetically - feeds the list page's category filter buttons and the detail page's suggested
+ /// values. One gear item has exactly one category (unlike a blog's tags), so this is a plain
+ /// distinct scan, not a tag-cloud aggregation.
+ ///
+ Task> FindDistinctCategoriesAsync(string ownerId);
+}
diff --git a/src/Domain/Repositories/IMovieReferenceRepository.cs b/src/Domain/Repositories/IMovieReferenceRepository.cs
index 56c30465..1f6d41ed 100644
--- a/src/Domain/Repositories/IMovieReferenceRepository.cs
+++ b/src/Domain/Repositories/IMovieReferenceRepository.cs
@@ -39,4 +39,10 @@ public interface IMovieReferenceRepository
/// full unpaged read is fine.
///
Task> FindAllAsync();
+
+ ///
+ /// Permanently removes a reference document - backs the admin "unlink" action, which deletes the
+ /// shared document outright rather than merely detaching one tenant's link.
+ ///
+ Task DeleteAsync(string id);
}
diff --git a/src/Domain/Repositories/ITvShowReferenceRepository.cs b/src/Domain/Repositories/ITvShowReferenceRepository.cs
index 48d1cf30..2b1cd6e8 100644
--- a/src/Domain/Repositories/ITvShowReferenceRepository.cs
+++ b/src/Domain/Repositories/ITvShowReferenceRepository.cs
@@ -40,4 +40,10 @@ public interface ITvShowReferenceRepository
/// full unpaged read is fine.
///
Task> FindAllAsync();
+
+ ///
+ /// Permanently removes a reference document - backs the admin "unlink" action, which deletes the
+ /// shared document outright rather than merely detaching one tenant's link.
+ ///
+ Task DeleteAsync(string id);
}
diff --git a/src/Domain/Repositories/IUserPreferencesRepository.cs b/src/Domain/Repositories/IUserPreferencesRepository.cs
new file mode 100644
index 00000000..adc73eea
--- /dev/null
+++ b/src/Domain/Repositories/IUserPreferencesRepository.cs
@@ -0,0 +1,23 @@
+using System.Threading.Tasks;
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Repositories;
+
+///
+/// A single per-user preferences document, not a full owner-scoped CRUD collection (there's exactly one
+/// document per owner, never listed/paged) - a small purpose-built repository like
+/// , rather than forced through .
+///
+public interface IUserPreferencesRepository
+{
+ ///
+ /// The owner's preferences, or null if they've never saved any - callers should fall back to an
+ /// all-default instance rather than writing one on read.
+ ///
+ Task FindByOwnerIdAsync(string ownerId);
+
+ ///
+ /// Creates or fully replaces the owner's preferences document.
+ ///
+ Task UpsertAsync(UserPreferencesModel model);
+}
diff --git a/src/Domain/Repositories/IVideoGameReferenceRepository.cs b/src/Domain/Repositories/IVideoGameReferenceRepository.cs
index 82d7479f..a772cd9a 100644
--- a/src/Domain/Repositories/IVideoGameReferenceRepository.cs
+++ b/src/Domain/Repositories/IVideoGameReferenceRepository.cs
@@ -38,4 +38,10 @@ public interface IVideoGameReferenceRepository
/// full unpaged read is fine.
///
Task> FindAllAsync();
+
+ ///
+ /// Permanently removes a reference document - backs the admin "unlink" action, which deletes the
+ /// shared document outright rather than merely detaching one tenant's link.
+ ///
+ Task DeleteAsync(string id);
}
diff --git a/src/Domain/Services/AmazonImportMergeService.cs b/src/Domain/Services/AmazonImportMergeService.cs
new file mode 100644
index 00000000..27978c9f
--- /dev/null
+++ b/src/Domain/Services/AmazonImportMergeService.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+
+namespace Keeptrack.Domain.Services;
+
+///
+/// Amazon-specific formatting used when committing a set of user-reviewed Amazon order rows: the ASIN-based
+/// reference text and the provenance-notes text. The actual create/merge/dedup engine used to live here too,
+/// but it was already fully generic - it moved to once the generic
+/// video game transaction importer needed the exact same engine, leaving this class with only the two
+/// members that are genuinely Amazon-specific.
+///
+public static class AmazonImportMergeService
+{
+ ///
+ /// The one place that formats an owned copy's Reference for an imported order line - human-readable,
+ /// and also the exact-match dedup key
+ /// looks for on a later re-import. Includes the ASIN, not just the order id: a single Amazon order commonly
+ /// contains several different line items, and order-id-only matching (a real bug, found before this shipped)
+ /// meant a second, genuinely different item from the same order was silently skipped as a "duplicate" of the
+ /// first - or, at preview time, incorrectly flagged "already imported" just because a sibling item from
+ /// the same order had been. The ASIN is Amazon's own stable per-product id, already parsed from every
+ /// row, so it's a precise disambiguator - unlike the product title, which is user-editable before commit.
+ ///
+ public static string FormatOrderReference(string orderId, string asin) => $"Amazon order {orderId} (ASIN {asin})";
+
+ ///
+ /// Reference-data linking is expected to overwrite the created item's title (and, for a book, its ISBN)
+ /// with the provider's canonical values, and the user may have already cleaned up the title before
+ /// commit - so this is the one place Amazon's own original listing text is preserved, for an item
+ /// created by this import. Only used at creation time: a pre-existing item's provenance isn't this
+ /// import's to invent. is null for every domain but Book.
+ ///
+ public static string BuildAmazonProvenanceNotes(string amazonTitle, string? isbn)
+ {
+ var lines = new List { $"Title from Amazon: {amazonTitle}" };
+ if (isbn is not null) lines.Add($"ISBN from Amazon: {isbn}");
+ return string.Join('\n', lines);
+ }
+}
diff --git a/src/Domain/Services/AmazonOrderPreviewService.cs b/src/Domain/Services/AmazonOrderPreviewService.cs
new file mode 100644
index 00000000..23bce9b9
--- /dev/null
+++ b/src/Domain/Services/AmazonOrderPreviewService.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using CsvHelper;
+using CsvHelper.Configuration;
+using CsvHelper.Configuration.Attributes;
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Services;
+
+///
+/// Parses an Amazon.fr order-history export ("Request My Data" -> "Your Orders") into review rows. Pure:
+/// bytes in, rows out, no repository access - alreadyImportedReferences below is computed by the
+/// caller from already-fetched data so this stays testable without a database.
+/// Amazon's export has no category column at all, so this never decides "is this a book" on its own - it
+/// only computes as a checksum-verified suggestion for
+/// the review UI's default filter, confirmed against a real export (an Amazon book's ASIN is routinely its
+/// ISBN-10). The user makes the actual call.
+///
+public static class AmazonOrderPreviewService
+{
+ private sealed class AmazonOrderRecord
+ {
+ [Name("ASIN")]
+ public required string Asin { get; set; }
+
+ [Name("Order Date")]
+ public required string OrderDate { get; set; }
+
+ [Name("Order ID")]
+ public required string OrderId { get; set; }
+
+ [Name("Product Name")]
+ public required string ProductName { get; set; }
+
+ [Name("Product Condition")]
+ public string? ProductCondition { get; set; }
+
+ [Name("Total Amount")]
+ public string? TotalAmount { get; set; }
+
+ [Name("Website")]
+ public string? Website { get; set; }
+ }
+
+ private static readonly CsvConfiguration s_csvConfiguration = new(CultureInfo.InvariantCulture)
+ {
+ PrepareHeaderForMatch = args => args.Header.Trim().ToLowerInvariant()
+ };
+
+ public static List BuildPreview(Stream csvStream, IReadOnlySet alreadyImportedReferences)
+ {
+ using var reader = new StreamReader(csvStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
+ using var csv = new CsvReader(reader, s_csvConfiguration);
+ var records = csv.GetRecords().ToList();
+
+ return records.Select(record =>
+ {
+ var suggestedIsbn = IsValidIsbn10(record.Asin) ? record.Asin : null;
+
+ return new AmazonOrderPreviewRow
+ {
+ RowId = $"{record.OrderId}:{record.Asin}",
+ // Some product names also carry a raw numeric HTML entity instead of the character itself
+ // (confirmed in a real export: "Le Dernier Vœu" instead of "Le Dernier Vœu"). Decoded
+ // after the mojibake repair, not before - the entity text is plain ASCII, so it round-trips
+ // through that repair unchanged regardless of order, but repairing first keeps the two
+ // independent fixes from interacting in either direction.
+ Title = WebUtility.HtmlDecode(FixMojibake(record.ProductName)),
+ Asin = record.Asin,
+ OrderId = record.OrderId,
+ OrderDate = ParseOrderDate(record.OrderDate),
+ Price = ParsePrice(record.TotalAmount),
+ Vendor = record.Website ?? string.Empty,
+ Condition = record.ProductCondition,
+ LooksLikeBook = suggestedIsbn is not null,
+ SuggestedIsbn = suggestedIsbn,
+ // Per (order, ASIN), not per order - an order commonly has several different line items, and
+ // an order-id-only check would incorrectly flag every sibling item once any one of them had
+ // actually been imported (see AmazonImportMergeService.FormatOrderReference).
+ AlreadyImported = alreadyImportedReferences.Contains(AmazonImportMergeService.FormatOrderReference(record.OrderId, record.Asin))
+ };
+ }).ToList();
+ }
+
+ ///
+ /// Amazon's export is mojibake for accented text: UTF-8 bytes were re-encoded as if they were Latin-1
+ /// (confirmed against the raw bytes of a real export - "protection d'écrans" reads back as "protection
+ /// d'écrans"). Reinterpreting the decoded chars as Latin-1 bytes and re-decoding as UTF-8 repairs it.
+ /// The repaired text is only used when it contains no U+FFFD replacement character, so already-correct
+ /// text (a lone, validly-encoded accented char has no valid single-byte UTF-8 reading) round-trips
+ /// untouched instead of being corrupted a second time.
+ ///
+ private static string FixMojibake(string text)
+ {
+ if (text.Length == 0 || text.Any(c => c > 255)) return text;
+
+ var bytes = new byte[text.Length];
+ for (var i = 0; i < text.Length; i++)
+ {
+ bytes[i] = (byte)text[i];
+ }
+
+ var repaired = Encoding.UTF8.GetString(bytes);
+ return repaired.Contains('�') ? text : repaired;
+ }
+
+ ///
+ /// Strips Amazon's Excel-formula-injection-prevention leading apostrophe (confirmed in a real export's
+ /// "Total Discounts" column, e.g. the literal text '-5' ) before parsing.
+ ///
+ private static decimal? ParsePrice(string? raw)
+ {
+ if (string.IsNullOrWhiteSpace(raw)) return null;
+
+ var cleaned = raw.Trim().Trim('\'');
+ return decimal.TryParse(cleaned, NumberStyles.Number, CultureInfo.InvariantCulture, out var value) ? value : null;
+ }
+
+ private static DateOnly? ParseOrderDate(string raw) =>
+ DateTimeOffset.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsed)
+ ? DateOnly.FromDateTime(parsed.UtcDateTime)
+ : null;
+
+ ///
+ /// ISBN-10 checksum: 10 digits (last one may be 'X'/'x' meaning check value 10), weighted sum over
+ /// position (10 down to 1) divisible by 11. Amazon's book ASINs are routinely a real ISBN-10, so this
+ /// is a strong, false-positive-resistant "is this a book" signal - unlike a bare "10 characters" check.
+ ///
+ private static bool IsValidIsbn10(string asin)
+ {
+ if (asin.Length != 10) return false;
+
+ var sum = 0;
+ for (var i = 0; i < 9; i++)
+ {
+ if (!char.IsDigit(asin[i])) return false;
+ sum += (asin[i] - '0') * (10 - i);
+ }
+
+ var last = asin[9];
+ int checkDigit;
+ if (last is 'X' or 'x') checkDigit = 10;
+ else if (char.IsDigit(last)) checkDigit = last - '0';
+ else return false;
+
+ sum += checkDigit;
+ return sum % 11 == 0;
+ }
+}
diff --git a/src/Domain/Services/GenericVideoGameImportService.cs b/src/Domain/Services/GenericVideoGameImportService.cs
new file mode 100644
index 00000000..7b314d8e
--- /dev/null
+++ b/src/Domain/Services/GenericVideoGameImportService.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using CsvHelper;
+using CsvHelper.Configuration;
+using CsvHelper.Configuration.Attributes;
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Services;
+
+///
+/// Parses a generic video game transaction-history CSV (a store's purchase history export - PSN's own GDPR
+/// export today, reshaped by the user into this shape; any store exporting the same columns would work too,
+/// since is a per-row column rather than something hardcoded here) into review rows.
+/// Pure: bytes in, rows out, no repository access - alreadyImportedReferences below is computed by
+/// the caller from already-fetched data so this stays testable without a database. Every row is a video
+/// game (unlike the Amazon import, which mixes several media types and needs a per-row type guess) - the
+/// export is store-purchase history for games specifically.
+///
+public static class GenericVideoGameImportService
+{
+ private sealed class VideoGameTransactionRecord
+ {
+ [Name("Transaction Date")]
+ public required string TransactionDate { get; set; }
+
+ [Name("Game Name")]
+ public required string GameName { get; set; }
+
+ [Name("Product Name")]
+ public string? ProductName { get; set; }
+
+ [Name("Platform")]
+ public required string Platform { get; set; }
+
+ [Name("Vendor")]
+ public required string Vendor { get; set; }
+
+ [Name("Transaction Id")]
+ public required string TransactionId { get; set; }
+
+ [Name("Order Id")]
+ public required string OrderId { get; set; }
+
+ [Name("Final Price (€)")]
+ public string? FinalPrice { get; set; }
+ }
+
+ private static readonly CsvConfiguration s_csvConfiguration = new(CultureInfo.InvariantCulture)
+ {
+ PrepareHeaderForMatch = args => args.Header.Trim().ToLowerInvariant()
+ };
+
+ public static List BuildPreview(Stream csvStream, IReadOnlySet alreadyImportedReferences)
+ {
+ using var reader = new StreamReader(csvStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
+ using var csv = new CsvReader(reader, s_csvConfiguration);
+ var records = csv.GetRecords().ToList();
+
+ return records.Select(record => new GenericVideoGameImportPreviewRow
+ {
+ // Transaction Id alone isn't unique per row (see FormatReference's doc comment - a bundled
+ // transaction has several lines sharing one), so RowId needs the same product disambiguator,
+ // same shape as AmazonOrderPreviewRow.RowId's "{OrderId}:{Asin}".
+ RowId = $"{record.TransactionId}:{record.ProductName ?? record.GameName}",
+ Title = CleanTitle(record.GameName, record.Platform),
+ Platform = record.Platform,
+ ProductName = record.ProductName,
+ Vendor = record.Vendor,
+ TransactionId = record.TransactionId,
+ OrderId = record.OrderId,
+ TransactionDate = ParseTransactionDate(record.TransactionDate),
+ Price = ParsePrice(record.FinalPrice),
+ AlreadyImported = alreadyImportedReferences.Contains(FormatReference(record.TransactionId, record.OrderId, record.ProductName, record.GameName))
+ }).ToList();
+ }
+
+ ///
+ /// Strips a trailing " ({platform})" suffix from the game title (e.g. "Grand Theft Auto V (PS4)" with
+ /// platform "PS4" becomes "Grand Theft Auto V") - the export's own title column routinely repeats the
+ /// platform, redundant now that is its own
+ /// column. Falls back to the raw title unchanged when the suffix isn't present, so a source that doesn't
+ /// follow this convention is left alone rather than mangled.
+ ///
+ public static string CleanTitle(string gameName, string platform)
+ {
+ var suffix = $" ({platform})";
+ return gameName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase) ? gameName[..^suffix.Length] : gameName;
+ }
+
+ ///
+ /// The one place that formats an owned copy's Reference for an imported transaction line -
+ /// human-readable, and also the exact-match dedup key
+ /// looks for on a later
+ /// re-import. No vendor name here - already carries that.
+ ///
+ /// Appends (falling back to when blank)
+ /// because the transaction id + order id pair is *not* reliably unique per line: a single transaction
+ /// commonly bundles several different products together (confirmed against a real PSN export - one
+ /// order/transaction contained three separate "Far Cry 4" DLC packs, each its own line with a distinct
+ /// Product Name but the exact same Transaction Id/Order Id). Without this, two of those three lines'
+ /// references collided and were wrongly treated as re-imports of each other - the same class of bug
+ /// already avoids by including the ASIN
+ /// alongside the order id, since Amazon's export has a real stable per-product id to use for that;
+ /// this export has no such id, so the product/edition text is the next best disambiguator.
+ ///
+ public static string FormatReference(string transactionId, string orderId, string? productName, string fallbackTitle)
+ {
+ var descriptor = string.IsNullOrWhiteSpace(productName) ? fallbackTitle : productName;
+ return $"Order {orderId} (transaction {transactionId}) - {descriptor}";
+ }
+
+ ///
+ /// Reference-data linking is expected to overwrite the created item's title with the provider's
+ /// canonical value, and the user may have already cleaned up the title before commit - so this is the
+ /// one place the source export's original listing text is preserved, for an item created by this
+ /// import. Only used at creation time: a pre-existing item's provenance isn't this import's to invent.
+ ///
+ public static string BuildProvenanceNotes(string vendor, string sourceTitle) => $"Title from {vendor}: {sourceTitle}";
+
+ private static decimal? ParsePrice(string? raw)
+ {
+ if (string.IsNullOrWhiteSpace(raw)) return null;
+
+ return decimal.TryParse(raw.Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out var value) ? value : null;
+ }
+
+ private static DateOnly? ParseTransactionDate(string raw) =>
+ DateTimeOffset.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsed)
+ ? DateOnly.FromDateTime(parsed.UtcDateTime)
+ : null;
+}
diff --git a/src/Domain/Services/OwnedItemImportMergeService.cs b/src/Domain/Services/OwnedItemImportMergeService.cs
new file mode 100644
index 00000000..89f70485
--- /dev/null
+++ b/src/Domain/Services/OwnedItemImportMergeService.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Keeptrack.Common.System;
+using Keeptrack.Domain.Models;
+
+namespace Keeptrack.Domain.Services;
+
+///
+/// Computes what to create/update when committing a set of user-reviewed import rows, for any trackable item
+/// type. Pure: takes the owner's already-fetched items and the selected rows, returns a plan - all
+/// repository access stays in the calling controller. Matching is by normalized title
+/// ( , the same "same title" rule the TV Time import already established),
+/// merging within the same commit batch too: two selected rows sharing a title become one item with two
+/// owned copies, even if neither existed before this commit.
+///
+/// Generic over both the tracked model (BookModel /MovieModel /TvShowModel /VideoGameModel )
+/// and its own request-item shape via delegates rather than a shared interface - Book/Movie/TvShow's
+/// OwnedVersions and VideoGame's differently-shaped Platforms both flow through the exact same
+/// algorithm this way, with no changes to any of those models. Extracted out of the originally Amazon-only
+/// once a second importer (the generic video game transaction import)
+/// needed the exact same engine - the two members here were already fully generic (no Amazon-specific
+/// concept anywhere in their bodies), only and
+/// are genuinely Amazon-specific and stayed
+/// behind.
+///
+public static class OwnedItemImportMergeService
+{
+ ///
+ /// Every reference already recorded on an existing owned copy - used at preview time to flag rows that
+ /// look like they were imported before, so re-uploading a newer export doesn't duplicate them. Compared
+ /// by exact string equality against whatever reference-formatting the calling importer uses for its own
+ /// candidate rows (e.g. ), which is what makes
+ /// per-line-item (not per-order) precision actually take effect. reads
+ /// whichever collection carries the owned copies for (OwnedVersions
+ /// or Platforms ).
+ ///
+ public static HashSet FindImportedReferences(IEnumerable existingItems, Func> getReferences) =>
+ existingItems.SelectMany(getReferences).Where(reference => reference is not null).ToHashSet()!;
+
+ ///
+ /// / make the caller's own
+ /// reference (see ) the *primary* dedup key, not just an
+ /// advisory preview-time flag: a re-committed row whose reference already exists on some existing item -
+ /// under any title, even one reference-data linking has since renamed - is skipped outright rather than
+ /// falling through to title matching. Two real bugs (found on the original Amazon import) motivated this:
+ /// (1) re-running the same commit created a second owned copy for the same order every time, because
+ /// title-matching alone doesn't know "this exact copy is already here"; (2) once an item's title changed
+ /// after a first import, title-matching a re-import of the same order no longer found it at all and
+ /// created a brand new duplicate item instead. Title matching is still the fallback for a genuinely new
+ /// order of an item already owned under a different order (a second copy bought separately).
+ ///
+ public static ImportCommitPlan ComputeCommitPlan(
+ IReadOnlyCollection existingItems,
+ IReadOnlyList items,
+ Func getExistingTitle,
+ Func> getExistingReferences,
+ Func getItemTitle,
+ Func getItemReference,
+ Func createNew,
+ Action appendOwnedCopy)
+ where TModel : class
+ {
+ var plan = new ImportCommitPlan();
+
+ var byNormalizedTitle = new Dictionary();
+ var byReference = new Dictionary();
+ IndexExistingItems(existingItems, getExistingTitle, getExistingReferences, byNormalizedTitle, byReference);
+
+ // Items created earlier in this same batch are tracked separately from pre-existing ones: a later
+ // row matching one must only get its owned copy appended (already reflected via ItemsToCreate),
+ // never also queued onto ItemsToUpdate - it has no Id yet, so "updating" it would be meaningless.
+ var createdThisBatch = new HashSet();
+
+ foreach (var item in items)
+ {
+ MergeItem(item, plan, byNormalizedTitle, byReference, createdThisBatch, getItemTitle, getItemReference, createNew, appendOwnedCopy);
+ }
+
+ return plan;
+ }
+
+ private static void IndexExistingItems(
+ IReadOnlyCollection existingItems,
+ Func getExistingTitle,
+ Func> getExistingReferences,
+ Dictionary byNormalizedTitle,
+ Dictionary byReference)
+ where TModel : class
+ {
+ foreach (var existing in existingItems)
+ {
+ byNormalizedTitle.TryAdd(TitleNormalizer.Normalize(getExistingTitle(existing)), existing);
+ foreach (var reference in getExistingReferences(existing).OfType())
+ {
+ byReference.TryAdd(reference, existing);
+ }
+ }
+ }
+
+ private static void MergeItem(
+ TRequestItem item,
+ ImportCommitPlan plan,
+ Dictionary byNormalizedTitle,
+ Dictionary byReference,
+ HashSet createdThisBatch,
+ Func getItemTitle,
+ Func getItemReference,
+ Func createNew,
+ Action appendOwnedCopy)
+ where TModel : class
+ {
+ var reference = getItemReference(item);
+ if (reference is not null && byReference.ContainsKey(reference))
+ {
+ plan.OwnedCopiesSkipped++;
+ plan.SkippedTitles.Add(getItemTitle(item));
+ return;
+ }
+
+ var key = TitleNormalizer.Normalize(getItemTitle(item));
+ TModel target;
+
+ if (byNormalizedTitle.TryGetValue(key, out var existing))
+ {
+ appendOwnedCopy(existing, item);
+ if (!createdThisBatch.Contains(existing) && !plan.ItemsToUpdate.Contains(existing))
+ {
+ plan.ItemsToUpdate.Add(existing);
+ }
+
+ target = existing;
+ }
+ else
+ {
+ var created = createNew(item);
+ plan.ItemsToCreate.Add(created);
+ createdThisBatch.Add(created);
+
+ // so a later row in the same batch sharing this title merges into it too, instead of
+ // creating a second item
+ byNormalizedTitle[key] = created;
+ target = created;
+ }
+
+ // registers this reference immediately (not just from the initial existingItems scan), so a
+ // second row in the same batch carrying the same reference is caught by the check above too
+ if (reference is not null) byReference[reference] = target;
+
+ plan.OwnedCopiesAdded++;
+ }
+}
diff --git a/src/Infrastructure.MongoDb/Entities/Album.cs b/src/Infrastructure.MongoDb/Entities/Album.cs
index bef1f3ec..3306fe63 100644
--- a/src/Infrastructure.MongoDb/Entities/Album.cs
+++ b/src/Infrastructure.MongoDb/Entities/Album.cs
@@ -27,6 +27,9 @@ public class Album : IHasIdAndOwnerId
[BsonElement("reference_id")]
public string? ReferenceId { get; set; }
+ [BsonElement("custom_image_url")]
+ public string? CustomImageUrl { get; set; }
+
[BsonElement("is_favorite")]
public bool IsFavorite { get; set; }
diff --git a/src/Infrastructure.MongoDb/Entities/Car.cs b/src/Infrastructure.MongoDb/Entities/Car.cs
index 246bc6de..8fb3603a 100644
--- a/src/Infrastructure.MongoDb/Entities/Car.cs
+++ b/src/Infrastructure.MongoDb/Entities/Car.cs
@@ -28,4 +28,7 @@ public class Car : IHasIdAndOwnerId
[BsonElement("energy_type")]
public required CarEnergyType EnergyType { get; set; }
+
+ [BsonElement("image_url")]
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Entities/Collectible.cs b/src/Infrastructure.MongoDb/Entities/Collectible.cs
new file mode 100644
index 00000000..71d97c0f
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Entities/Collectible.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Keeptrack.Infrastructure.MongoDb.Entities;
+
+public class Collectible : IHasIdAndOwnerId
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]
+ public string? Id { get; set; }
+
+ [BsonElement("owner_id")]
+ public required string OwnerId { get; set; }
+
+ public required string Title { get; set; }
+
+ public string? Brand { get; set; }
+
+ public int? Year { get; set; }
+
+ public string? Notes { get; set; }
+
+ [BsonElement("image_url")]
+ public string? ImageUrl { get; set; }
+
+ [BsonElement("is_favorite")]
+ public bool IsFavorite { get; set; }
+
+ [BsonElement("owned_versions")]
+ public List OwnedVersions { get; set; } = [];
+}
diff --git a/src/Infrastructure.MongoDb/Entities/Gear.cs b/src/Infrastructure.MongoDb/Entities/Gear.cs
new file mode 100644
index 00000000..2f3b2193
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Entities/Gear.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Keeptrack.Infrastructure.MongoDb.Entities;
+
+public class Gear : IHasIdAndOwnerId
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]
+ public string? Id { get; set; }
+
+ [BsonElement("owner_id")]
+ public required string OwnerId { get; set; }
+
+ public required string Title { get; set; }
+
+ public string? Brand { get; set; }
+
+ public string? Category { get; set; }
+
+ public int? Year { get; set; }
+
+ public string? Notes { get; set; }
+
+ [BsonElement("image_url")]
+ public string? ImageUrl { get; set; }
+
+ [BsonElement("is_favorite")]
+ public bool IsFavorite { get; set; }
+
+ [BsonElement("owned_versions")]
+ public List OwnedVersions { get; set; } = [];
+}
diff --git a/src/Infrastructure.MongoDb/Entities/HealthProfile.cs b/src/Infrastructure.MongoDb/Entities/HealthProfile.cs
index ac47fcfc..06f545f5 100644
--- a/src/Infrastructure.MongoDb/Entities/HealthProfile.cs
+++ b/src/Infrastructure.MongoDb/Entities/HealthProfile.cs
@@ -16,4 +16,7 @@ public class HealthProfile : IHasIdAndOwnerId
public required string Name { get; set; }
public string? Notes { get; set; }
+
+ [BsonElement("image_url")]
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Entities/House.cs b/src/Infrastructure.MongoDb/Entities/House.cs
index 9f0322f1..181e4a37 100644
--- a/src/Infrastructure.MongoDb/Entities/House.cs
+++ b/src/Infrastructure.MongoDb/Entities/House.cs
@@ -29,4 +29,7 @@ public class House : IHasIdAndOwnerId
public DateTime? MovedOutAt { get; set; }
public string? Notes { get; set; }
+
+ [BsonElement("image_url")]
+ public string? ImageUrl { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Entities/OwnedVersion.cs b/src/Infrastructure.MongoDb/Entities/OwnedVersion.cs
index 5ddcd06e..7d38b462 100644
--- a/src/Infrastructure.MongoDb/Entities/OwnedVersion.cs
+++ b/src/Infrastructure.MongoDb/Entities/OwnedVersion.cs
@@ -19,4 +19,7 @@ public class OwnedVersion
public DateTime? AcquiredAt { get; set; }
public string? Reference { get; set; }
+
+ [BsonElement("product_name")]
+ public string? ProductName { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Entities/UserPreferences.cs b/src/Infrastructure.MongoDb/Entities/UserPreferences.cs
new file mode 100644
index 00000000..00dbd811
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Entities/UserPreferences.cs
@@ -0,0 +1,18 @@
+using Keeptrack.Common.System;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Keeptrack.Infrastructure.MongoDb.Entities;
+
+public class UserPreferences : IHasIdAndOwnerId
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]
+ public string? Id { get; set; }
+
+ [BsonElement("owner_id")]
+ public required string OwnerId { get; set; }
+
+ [BsonElement("features")]
+ public UserPreferencesFeatures Features { get; set; } = new();
+}
diff --git a/src/Infrastructure.MongoDb/Entities/UserPreferencesFeatures.cs b/src/Infrastructure.MongoDb/Entities/UserPreferencesFeatures.cs
new file mode 100644
index 00000000..179fe177
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Entities/UserPreferencesFeatures.cs
@@ -0,0 +1,12 @@
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Keeptrack.Infrastructure.MongoDb.Entities;
+
+public class UserPreferencesFeatures
+{
+ [BsonElement("show_chasse_aux_livres_link")]
+ public bool ShowChasseAuxLivresLink { get; set; }
+
+ [BsonElement("show_amazon_product_link")]
+ public bool ShowAmazonProductLink { get; set; }
+}
diff --git a/src/Infrastructure.MongoDb/Entities/VideoGame.cs b/src/Infrastructure.MongoDb/Entities/VideoGame.cs
index bbe239da..66b3c483 100644
--- a/src/Infrastructure.MongoDb/Entities/VideoGame.cs
+++ b/src/Infrastructure.MongoDb/Entities/VideoGame.cs
@@ -27,6 +27,9 @@ public class VideoGame : IHasIdAndOwnerId
[BsonElement("reference_id")]
public string? ReferenceId { get; set; }
+ [BsonElement("custom_image_url")]
+ public string? CustomImageUrl { get; set; }
+
[BsonElement("is_wishlisted")]
public bool IsWishlisted { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Entities/VideoGamePlatform.cs b/src/Infrastructure.MongoDb/Entities/VideoGamePlatform.cs
index c5abe624..ba65eaa3 100644
--- a/src/Infrastructure.MongoDb/Entities/VideoGamePlatform.cs
+++ b/src/Infrastructure.MongoDb/Entities/VideoGamePlatform.cs
@@ -35,4 +35,7 @@ public class VideoGamePlatform
public DateTime? AcquiredAt { get; set; }
public string? Reference { get; set; }
+
+ [BsonElement("product_name")]
+ public string? ProductName { get; set; }
}
diff --git a/src/Infrastructure.MongoDb/Mappers/CollectibleStorageMapper.cs b/src/Infrastructure.MongoDb/Mappers/CollectibleStorageMapper.cs
new file mode 100644
index 00000000..58a19614
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Mappers/CollectibleStorageMapper.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using Keeptrack.Domain.Models;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Riok.Mapperly.Abstractions;
+
+namespace Keeptrack.Infrastructure.MongoDb.Mappers;
+
+[Mapper]
+[UseStaticMapper(typeof(CommonStorageMappings))]
+public partial class CollectibleStorageMapper : IStorageMapper
+{
+ // IsOwned is filter-only (derived from OwnedVersions) - see MovieStorageMapper.
+ [MapperIgnoreSource(nameof(CollectibleModel.IsOwned))]
+ public partial Collectible ToEntity(CollectibleModel model);
+
+ [MapperIgnoreTarget(nameof(CollectibleModel.IsOwned))]
+ public partial CollectibleModel ToModel(Collectible entity);
+
+ public partial List ToModels(List entities);
+}
diff --git a/src/Infrastructure.MongoDb/Mappers/GearStorageMapper.cs b/src/Infrastructure.MongoDb/Mappers/GearStorageMapper.cs
new file mode 100644
index 00000000..a4587c42
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Mappers/GearStorageMapper.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using Keeptrack.Domain.Models;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Riok.Mapperly.Abstractions;
+
+namespace Keeptrack.Infrastructure.MongoDb.Mappers;
+
+[Mapper]
+[UseStaticMapper(typeof(CommonStorageMappings))]
+public partial class GearStorageMapper : IStorageMapper
+{
+ // IsOwned is filter-only (derived from OwnedVersions) - see MovieStorageMapper.
+ [MapperIgnoreSource(nameof(GearModel.IsOwned))]
+ public partial Gear ToEntity(GearModel model);
+
+ [MapperIgnoreTarget(nameof(GearModel.IsOwned))]
+ public partial GearModel ToModel(Gear entity);
+
+ public partial List ToModels(List entities);
+}
diff --git a/src/Infrastructure.MongoDb/Mappers/UserPreferencesStorageMapper.cs b/src/Infrastructure.MongoDb/Mappers/UserPreferencesStorageMapper.cs
new file mode 100644
index 00000000..e5c5af2f
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Mappers/UserPreferencesStorageMapper.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Keeptrack.Domain.Models;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Riok.Mapperly.Abstractions;
+
+namespace Keeptrack.Infrastructure.MongoDb.Mappers;
+
+[Mapper]
+public partial class UserPreferencesStorageMapper : IStorageMapper
+{
+ public partial UserPreferences ToEntity(UserPreferencesModel model);
+
+ public partial UserPreferencesModel ToModel(UserPreferences entity);
+
+ public partial List ToModels(List entities);
+}
diff --git a/src/Infrastructure.MongoDb/Repositories/AlbumReferenceRepository.cs b/src/Infrastructure.MongoDb/Repositories/AlbumReferenceRepository.cs
index a50fec1b..44b80e2d 100644
--- a/src/Infrastructure.MongoDb/Repositories/AlbumReferenceRepository.cs
+++ b/src/Infrastructure.MongoDb/Repositories/AlbumReferenceRepository.cs
@@ -88,4 +88,9 @@ public async Task UpsertAsync(AlbumReferenceModel model)
return mapper.ToModel(entity);
}
+
+ public async Task DeleteAsync(string id)
+ {
+ await Collection.DeleteOneAsync(x => x.Id == id);
+ }
}
diff --git a/src/Infrastructure.MongoDb/Repositories/BookReferenceRepository.cs b/src/Infrastructure.MongoDb/Repositories/BookReferenceRepository.cs
index 82d34690..51502e62 100644
--- a/src/Infrastructure.MongoDb/Repositories/BookReferenceRepository.cs
+++ b/src/Infrastructure.MongoDb/Repositories/BookReferenceRepository.cs
@@ -90,4 +90,9 @@ public async Task UpsertAsync(BookReferenceModel model)
return mapper.ToModel(entity);
}
+
+ public async Task DeleteAsync(string id)
+ {
+ await Collection.DeleteOneAsync(x => x.Id == id);
+ }
}
diff --git a/src/Infrastructure.MongoDb/Repositories/CollectibleRepository.cs b/src/Infrastructure.MongoDb/Repositories/CollectibleRepository.cs
new file mode 100644
index 00000000..5dadafba
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Repositories/CollectibleRepository.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Linq.Expressions;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Keeptrack.Infrastructure.MongoDb.Mappers;
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+
+namespace Keeptrack.Infrastructure.MongoDb.Repositories;
+
+public class CollectibleRepository(IMongoDatabase mongoDatabase, ILogger logger, IStorageMapper mapper)
+ : MongoDbRepositoryBase(mongoDatabase, logger, mapper), ICollectibleRepository
+{
+ protected override string CollectionName => "collectible";
+
+ protected override Expression> SortTitleField => x => x.Title;
+
+ protected override FilterDefinition GetFilter(string ownerId, string? search, CollectibleModel input)
+ {
+ var builder = Builders.Filter;
+ var filter = builder.Eq(f => f.OwnerId, ownerId);
+ if (!string.IsNullOrEmpty(search)) filter &= builder.Where(f => f.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
+ || (f.Brand != null && f.Brand.Contains(search, StringComparison.CurrentCultureIgnoreCase)));
+ if (input.IsFavorite) filter &= builder.Eq(f => f.IsFavorite, true);
+ // "owned" means at least one owned version - see MovieRepository.GetFilter
+ if (input.IsOwned) filter &= builder.SizeGt(f => f.OwnedVersions, 0);
+ return filter;
+ }
+}
diff --git a/src/Infrastructure.MongoDb/Repositories/GearRepository.cs b/src/Infrastructure.MongoDb/Repositories/GearRepository.cs
new file mode 100644
index 00000000..fe59ac80
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Repositories/GearRepository.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using Keeptrack.Common.System;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Keeptrack.Infrastructure.MongoDb.Mappers;
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+
+namespace Keeptrack.Infrastructure.MongoDb.Repositories;
+
+public class GearRepository(IMongoDatabase mongoDatabase, ILogger logger, IStorageMapper mapper)
+ : MongoDbRepositoryBase(mongoDatabase, logger, mapper), IGearRepository
+{
+ protected override string CollectionName => "gear";
+
+ protected override Expression> SortTitleField => x => x.Title;
+
+ ///
+ /// "Bought" needs the max AcquiredAt across a gear item's
+ /// array (the most recently acquired copy), not a single scalar field, so it can't use the shared
+ /// SortSecondaryDateField hook - same shape as VideoGameRepository.GetSort 's "last
+ /// completed" override, relying on MongoDB's native max-on-descending-sort semantics for a dotted
+ /// array field rather than an aggregation pipeline.
+ ///
+ protected override SortDefinition GetSort(string? sort) =>
+ sort == ListSort.Bought
+ ? Builders.Sort.Descending("owned_versions.acquired_at").Descending("_id")
+ : base.GetSort(sort);
+
+ protected override FilterDefinition GetFilter(string ownerId, string? search, GearModel input)
+ {
+ var builder = Builders.Filter;
+ var filter = builder.Eq(f => f.OwnerId, ownerId);
+ if (!string.IsNullOrEmpty(search)) filter &= builder.Where(f => f.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
+ || (f.Brand != null && f.Brand.Contains(search, StringComparison.CurrentCultureIgnoreCase)));
+ if (input.IsFavorite) filter &= builder.Eq(f => f.IsFavorite, true);
+ if (!string.IsNullOrEmpty(input.Category)) filter &= builder.Eq(f => f.Category, input.Category);
+ // "owned" means at least one owned version - see MovieRepository.GetFilter
+ if (input.IsOwned) filter &= builder.SizeGt(f => f.OwnedVersions, 0);
+ return filter;
+ }
+
+ public async Task> FindDistinctCategoriesAsync(string ownerId)
+ {
+ var builder = Builders.Filter;
+ // "has a category" is the negation of TvShowRepository/MovieRepository's UnresolvedFilter shape
+ // (matches null OR empty string) - both generations of "unset" must be excluded here too.
+ var filter = builder.Eq(f => f.OwnerId, ownerId) & builder.Ne(f => f.Category, null) & builder.Ne(f => f.Category, string.Empty);
+ var cursor = await GetCollection().DistinctAsync(f => f.Category, filter);
+ var categories = await cursor.ToListAsync();
+ categories.Sort(StringComparer.OrdinalIgnoreCase);
+ return categories!;
+ }
+}
diff --git a/src/Infrastructure.MongoDb/Repositories/MovieReferenceRepository.cs b/src/Infrastructure.MongoDb/Repositories/MovieReferenceRepository.cs
index be1f86b9..6bb3e274 100644
--- a/src/Infrastructure.MongoDb/Repositories/MovieReferenceRepository.cs
+++ b/src/Infrastructure.MongoDb/Repositories/MovieReferenceRepository.cs
@@ -91,4 +91,9 @@ public async Task UpsertAsync(MovieReferenceModel model)
return mapper.ToModel(entity);
}
+
+ public async Task DeleteAsync(string id)
+ {
+ await Collection.DeleteOneAsync(x => x.Id == id);
+ }
}
diff --git a/src/Infrastructure.MongoDb/Repositories/TvShowReferenceRepository.cs b/src/Infrastructure.MongoDb/Repositories/TvShowReferenceRepository.cs
index 54e70882..ac058e26 100644
--- a/src/Infrastructure.MongoDb/Repositories/TvShowReferenceRepository.cs
+++ b/src/Infrastructure.MongoDb/Repositories/TvShowReferenceRepository.cs
@@ -86,4 +86,9 @@ public async Task UpsertAsync(TvShowReferenceModel model)
return mapper.ToModel(entity);
}
+
+ public async Task DeleteAsync(string id)
+ {
+ await Collection.DeleteOneAsync(x => x.Id == id);
+ }
}
diff --git a/src/Infrastructure.MongoDb/Repositories/UserPreferencesRepository.cs b/src/Infrastructure.MongoDb/Repositories/UserPreferencesRepository.cs
new file mode 100644
index 00000000..e06706b0
--- /dev/null
+++ b/src/Infrastructure.MongoDb/Repositories/UserPreferencesRepository.cs
@@ -0,0 +1,28 @@
+using System.Threading.Tasks;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.Infrastructure.MongoDb.Entities;
+using Keeptrack.Infrastructure.MongoDb.Mappers;
+using MongoDB.Driver;
+
+namespace Keeptrack.Infrastructure.MongoDb.Repositories;
+
+public class UserPreferencesRepository(IMongoDatabase mongoDatabase, UserPreferencesStorageMapper mapper) : IUserPreferencesRepository
+{
+ private const string CollectionName = "user_preference";
+
+ private IMongoCollection Collection => mongoDatabase.GetCollection(CollectionName);
+
+ public async Task FindByOwnerIdAsync(string ownerId)
+ {
+ var entity = await Collection.Find(x => x.OwnerId == ownerId).FirstOrDefaultAsync();
+ // the usual null guard before mapping - see MongoDbRepositoryBase.FindOneAsync's identical shape
+ return entity is null ? null : mapper.ToModel(entity);
+ }
+
+ public async Task UpsertAsync(UserPreferencesModel model)
+ {
+ var entity = mapper.ToEntity(model);
+ await Collection.ReplaceOneAsync(x => x.OwnerId == model.OwnerId, entity, new ReplaceOptions { IsUpsert = true });
+ }
+}
diff --git a/src/Infrastructure.MongoDb/Repositories/VideoGameReferenceRepository.cs b/src/Infrastructure.MongoDb/Repositories/VideoGameReferenceRepository.cs
index af6278bd..0d71b2a9 100644
--- a/src/Infrastructure.MongoDb/Repositories/VideoGameReferenceRepository.cs
+++ b/src/Infrastructure.MongoDb/Repositories/VideoGameReferenceRepository.cs
@@ -80,4 +80,9 @@ public async Task UpsertAsync(VideoGameReferenceModel m
return mapper.ToModel(entity);
}
+
+ public async Task DeleteAsync(string id)
+ {
+ await Collection.DeleteOneAsync(x => x.Id == id);
+ }
}
diff --git a/src/WebApi.Contracts/Dto/AlbumDto.cs b/src/WebApi.Contracts/Dto/AlbumDto.cs
index f8a3e929..882de0cf 100644
--- a/src/WebApi.Contracts/Dto/AlbumDto.cs
+++ b/src/WebApi.Contracts/Dto/AlbumDto.cs
@@ -46,11 +46,18 @@ public class AlbumDto : IHasId, IReferenceLinkedDto
public string? ReferenceId { get; set; }
///
- /// Cover/poster image URL from the linked reference document - read-only, hydrated server-side on
- /// list reads and never accepted from client input.
+ /// Cover image URL shown on the list page - when set, otherwise the linked
+ /// reference document's own cover. Read-only, hydrated server-side on list reads; never accepted from
+ /// client input (edit instead).
///
public string? ImageUrl { get; set; }
+ ///
+ /// Tenant-owned cover image override, freely editable - takes priority over the linked reference's
+ /// cover wherever one is shown. Null means "use the reference's cover, if any".
+ ///
+ public string? CustomImageUrl { get; set; }
+
public bool IsFavorite { get; set; }
///
diff --git a/src/WebApi.Contracts/Dto/AmazonImportCommitItemDto.cs b/src/WebApi.Contracts/Dto/AmazonImportCommitItemDto.cs
new file mode 100644
index 00000000..6d54e891
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/AmazonImportCommitItemDto.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// One row selected for import, carrying whatever the user edited in the review table.
+///
+public class AmazonImportCommitItemDto
+{
+ ///
+ /// The this came from. Not used for anything server-side
+ /// beyond echoing it back in error messages - the row's data is taken entirely from this DTO's own fields.
+ ///
+ public required string RowId { get; set; }
+
+ ///
+ /// Amazon's own product id for this order line and the order number itself - together they're the
+ /// server-derived owned-copy Reference (see AmazonImportMergeService.FormatOrderReference
+ /// in the WebApi project), which also doubles as the exact dedup key. Both are echoed back from the
+ /// preview row rather than accepting a client-supplied Reference string directly, so the format
+ /// can't drift out of sync with what a later re-preview checks against, and so a single order's several
+ /// different line items (same order id, different ASIN) are never conflated with one another.
+ ///
+ public required string OrderId { get; set; }
+
+ public required string Asin { get; set; }
+
+ public required string Title { get; set; }
+
+ ///
+ /// The title exactly as Amazon listed it, even if was edited in the review UI -
+ /// recorded in the created item's notes, since reference-data linking is expected to overwrite
+ /// later.
+ ///
+ public required string AmazonTitle { get; set; }
+
+ ///
+ /// Which trackable item type to create/merge this row as. Nullable (and validated as required
+ /// server-side, same as for a video game) rather than defaulting to
+ /// , so a row the "looks like a book" heuristic didn't suggest
+ /// can't be silently committed as a book just because the reviewer forgot to pick a type.
+ ///
+ public AmazonImportMediaType? MediaType { get; set; }
+
+ ///
+ /// Publication/release year, if the user happens to know it - Amazon's export has no source for this
+ /// (order date is the purchase year, not the item's), so it's never auto-filled.
+ ///
+ public int? Year { get; set; }
+
+ /// Book-only - ignored for every other .
+ public string? Isbn { get; set; }
+
+ ///
+ /// VideoGame-only (PS5/Xbox/PC/Switch...) - required and validated server-side when
+ /// is , ignored otherwise.
+ /// Amazon's export has no signal for this at all.
+ ///
+ public string? Platform { get; set; }
+
+ public DateOnly? AcquiredAt { get; set; }
+
+ public decimal? Price { get; set; }
+
+ public string? Vendor { get; set; }
+
+ public CopyType CopyType { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/AmazonImportCommitRequestDto.cs b/src/WebApi.Contracts/Dto/AmazonImportCommitRequestDto.cs
new file mode 100644
index 00000000..cd709c35
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/AmazonImportCommitRequestDto.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// The set of Amazon order-history rows the user picked in the review UI, ready to be created as books.
+///
+public class AmazonImportCommitRequestDto
+{
+ public required List Items { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/AmazonImportCommitResultDto.cs b/src/WebApi.Contracts/Dto/AmazonImportCommitResultDto.cs
new file mode 100644
index 00000000..03978605
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/AmazonImportCommitResultDto.cs
@@ -0,0 +1,62 @@
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// Outcome of committing a selected set of Amazon order rows, broken down per trackable item type - only
+/// the types actually present in the commit request end up non-zero.
+///
+public class AmazonImportCommitResultDto
+{
+ /// Brand new books created.
+ public int BooksCreated { get; set; }
+
+ /// Existing books (including ones created earlier in this same commit) that received an additional owned version.
+ public int BooksMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing owned version - not duplicated.
+ public int BooksSkipped { get; set; }
+
+ /// Brand new movies created.
+ public int MoviesCreated { get; set; }
+
+ /// Existing movies (including ones created earlier in this same commit) that received an additional owned version.
+ public int MoviesMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing owned version - not duplicated.
+ public int MoviesSkipped { get; set; }
+
+ /// Brand new TV shows created.
+ public int TvShowsCreated { get; set; }
+
+ /// Existing TV shows (including ones created earlier in this same commit) that received an additional owned version.
+ public int TvShowsMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing owned version - not duplicated.
+ public int TvShowsSkipped { get; set; }
+
+ /// Brand new video games created.
+ public int VideoGamesCreated { get; set; }
+
+ /// Existing video games (including ones created earlier in this same commit) that received an additional platform entry.
+ public int VideoGamesMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing platform entry - not duplicated.
+ public int VideoGamesSkipped { get; set; }
+
+ /// Brand new gear items created.
+ public int GearCreated { get; set; }
+
+ /// Existing gear items (including ones created earlier in this same commit) that received an additional owned version.
+ public int GearMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing owned version - not duplicated.
+ public int GearSkipped { get; set; }
+
+ /// Brand new collectibles created.
+ public int CollectiblesCreated { get; set; }
+
+ /// Existing collectibles (including ones created earlier in this same commit) that received an additional owned version.
+ public int CollectiblesMergedInto { get; set; }
+
+ /// Rows whose order reference already matched an existing owned version - not duplicated.
+ public int CollectiblesSkipped { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/AmazonImportMediaType.cs b/src/WebApi.Contracts/Dto/AmazonImportMediaType.cs
new file mode 100644
index 00000000..609d2691
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/AmazonImportMediaType.cs
@@ -0,0 +1,15 @@
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// Which trackable item type an Amazon order-history row should be imported as - picked per row in the
+/// review UI, since Amazon's export has no category column to detect this automatically.
+///
+public enum AmazonImportMediaType
+{
+ Book,
+ Movie,
+ TvShow,
+ VideoGame,
+ Gear,
+ Collectible
+}
diff --git a/src/WebApi.Contracts/Dto/AmazonOrderPreviewRowDto.cs b/src/WebApi.Contracts/Dto/AmazonOrderPreviewRowDto.cs
new file mode 100644
index 00000000..78b711c4
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/AmazonOrderPreviewRowDto.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// One line item parsed from an uploaded Amazon order-history export, awaiting the user's review before
+/// anything is imported. See for what gets sent back once selected.
+///
+public class AmazonOrderPreviewRowDto
+{
+ ///
+ /// Correlates a selected/edited row back to this one at commit time. Stable within one export, never stored.
+ ///
+ public required string RowId { get; set; }
+
+ ///
+ /// Amazon's product name, with the export's mojibake repaired.
+ ///
+ public required string Title { get; set; }
+
+ ///
+ /// Amazon's own product identifier for this order line.
+ ///
+ public required string Asin { get; set; }
+
+ ///
+ /// Amazon's order number.
+ ///
+ public required string OrderId { get; set; }
+
+ public DateOnly? OrderDate { get; set; }
+
+ ///
+ /// What was actually paid for this item - tax and any per-item discount already netted in.
+ ///
+ public decimal? Price { get; set; }
+
+ ///
+ /// The storefront the order was placed on (e.g. "Amazon.fr").
+ ///
+ public required string Vendor { get; set; }
+
+ ///
+ /// Display only ("New", "Used"...) - not carried onto the created book.
+ ///
+ public string? Condition { get; set; }
+
+ ///
+ /// True when passes an ISBN-10 checksum - the review table's default filter.
+ ///
+ public bool LooksLikeBook { get; set; }
+
+ ///
+ /// again, only when is true.
+ ///
+ public string? SuggestedIsbn { get; set; }
+
+ ///
+ /// True when an existing book already has an owned version referencing this order. Defaults to
+ /// unchecked in the review UI, but stays editable/selectable in case the user wants to re-import anyway.
+ ///
+ public bool AlreadyImported { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/CarDto.cs b/src/WebApi.Contracts/Dto/CarDto.cs
index 1d9fb71a..996fe9cf 100644
--- a/src/WebApi.Contracts/Dto/CarDto.cs
+++ b/src/WebApi.Contracts/Dto/CarDto.cs
@@ -41,4 +41,9 @@ public class CarDto : IHasId
/// Energy type (Combustion, Hybrid, Electric).
///
public CarEnergyType? EnergyType { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/WebApi.Contracts/Dto/CollectibleDto.cs b/src/WebApi.Contracts/Dto/CollectibleDto.cs
new file mode 100644
index 00000000..93ec07ca
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/CollectibleDto.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// A collectible item the user owns (e.g. a Lego set, a figurine).
+///
+public class CollectibleDto : IHasId
+{
+ ///
+ /// Unique identifier.
+ ///
+ public string? Id { get; set; }
+
+ ///
+ /// Collectible name.
+ ///
+ /// Millennium Falcon
+ public string? Title { get; set; }
+
+ ///
+ /// Brand or manufacturer.
+ ///
+ /// Lego
+ public string? Brand { get; set; }
+
+ ///
+ /// Year.
+ ///
+ public int? Year { get; set; }
+
+ ///
+ /// Free-text notes (where it's stored, where it's displayed, or any other specifics).
+ ///
+ public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
+
+ public bool IsFavorite { get; set; }
+
+ ///
+ /// Every owned copy of this item - it counts as owned when this list is non-empty.
+ ///
+ public List OwnedVersions { get; set; } = [];
+
+ ///
+ /// Filter-only query parameter: matches items with at least one owned version. Never populated on a
+ /// returned item - see for the convention.
+ ///
+ public bool IsOwned { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/CollectionStatsDto.cs b/src/WebApi.Contracts/Dto/CollectionStatsDto.cs
index d53e2ab4..3220c33c 100644
--- a/src/WebApi.Contracts/Dto/CollectionStatsDto.cs
+++ b/src/WebApi.Contracts/Dto/CollectionStatsDto.cs
@@ -31,4 +31,10 @@ public class CollectionStatsDto
/// Number of houses.
public long Houses { get; set; }
+
+ /// Number of collectibles.
+ public long Collectibles { get; set; }
+
+ /// Number of gear items.
+ public long Gear { get; set; }
}
diff --git a/src/WebApi.Contracts/Dto/GearDto.cs b/src/WebApi.Contracts/Dto/GearDto.cs
new file mode 100644
index 00000000..56493a39
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/GearDto.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using Keeptrack.Common.System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// A piece of gear/equipment the user owns (e.g. a TV, a bike, a keyboard).
+///
+public class GearDto : IHasId
+{
+ ///
+ /// Unique identifier.
+ ///
+ public string? Id { get; set; }
+
+ ///
+ /// Gear name.
+ ///
+ /// Standing desk
+ public string? Title { get; set; }
+
+ ///
+ /// Brand or manufacturer.
+ ///
+ /// Sony
+ public string? Brand { get; set; }
+
+ ///
+ /// Free-text category (e.g. "Electronics", "Camping") - suggested from the tenant's other gear but
+ /// not a closed list. Unlike a blog's tags, one gear item has exactly one category.
+ ///
+ /// Electronics
+ public string? Category { get; set; }
+
+ ///
+ /// Year.
+ ///
+ public int? Year { get; set; }
+
+ ///
+ /// Free-text notes (where it's stored, where it's used, or any other specifics).
+ ///
+ public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
+
+ public bool IsFavorite { get; set; }
+
+ ///
+ /// Every owned copy of this item - it counts as owned when this list is non-empty.
+ ///
+ public List OwnedVersions { get; set; } = [];
+
+ ///
+ /// Filter-only query parameter: matches items with at least one owned version. Never populated on a
+ /// returned item - see for the convention.
+ ///
+ public bool IsOwned { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitItemDto.cs b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitItemDto.cs
new file mode 100644
index 00000000..2190e454
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitItemDto.cs
@@ -0,0 +1,59 @@
+using System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// One row selected for import, carrying whatever the user edited in the review table.
+///
+public class GenericVideoGameImportCommitItemDto
+{
+ ///
+ /// The this came from. Not used for anything
+ /// server-side beyond echoing it back in error messages - the row's data is taken entirely from this
+ /// DTO's own fields.
+ ///
+ public required string RowId { get; set; }
+
+ public required string Title { get; set; }
+
+ ///
+ /// The title exactly as the source export listed it, even if was edited in the
+ /// review UI - recorded in the created item's notes, since reference-data linking is expected to
+ /// overwrite later.
+ ///
+ public required string SourceTitle { get; set; }
+
+ ///
+ /// Required and validated server-side - the export always carries one, but a user-edited value could be
+ /// blanked out by mistake.
+ ///
+ public string? Platform { get; set; }
+
+ /// The store's own specific product/edition text - see .
+ public string? ProductName { get; set; }
+
+ ///
+ /// The storefront's own transaction id and order id - together they're the server-derived owned-copy
+ /// Reference (see GenericVideoGameImportService.FormatReference in the Domain project),
+ /// which also doubles as the exact dedup key. Both are echoed back from the preview row rather than
+ /// accepting a client-supplied Reference string directly, so the format can't drift out of sync
+ /// with what a later re-preview checks against.
+ ///
+ public required string TransactionId { get; set; }
+
+ public required string OrderId { get; set; }
+
+ public required string Vendor { get; set; }
+
+ ///
+ /// Publication/release year, if the user happens to know it - the export has no source for this, so it's
+ /// never auto-filled.
+ ///
+ public int? Year { get; set; }
+
+ public DateOnly? AcquiredAt { get; set; }
+
+ public decimal? Price { get; set; }
+
+ public CopyType CopyType { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitRequestDto.cs b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitRequestDto.cs
new file mode 100644
index 00000000..343ea569
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitRequestDto.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// The set of generic video game transaction rows the user picked in the review UI, ready to be created as
+/// video games.
+///
+public class GenericVideoGameImportCommitRequestDto
+{
+ public required List Items { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitResultDto.cs b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitResultDto.cs
new file mode 100644
index 00000000..9a6e54a3
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/GenericVideoGameImportCommitResultDto.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// Outcome of committing a selected set of generic video game transaction rows.
+///
+public class GenericVideoGameImportCommitResultDto
+{
+ /// Brand new video games created.
+ public int VideoGamesCreated { get; set; }
+
+ /// Existing video games (including ones created earlier in this same commit) that received an additional platform entry.
+ public int VideoGamesMergedInto { get; set; }
+
+ /// Rows whose transaction reference already matched an existing platform entry - not duplicated.
+ public int VideoGamesSkipped { get; set; }
+
+ ///
+ /// The true per-row count of rows that got a platform entry added, whether the row's own new video game
+ /// or an existing/already-created-this-batch one. /
+ /// count distinct video games, not rows - several selected rows sharing a title can consolidate into
+ /// one created video game, which makes those two counts add up to less than the number of rows selected
+ /// even though nothing was lost. RowsImported + VideoGamesSkipped always equals the number of
+ /// rows submitted - the reconciling total to show the user so they can trust nothing was silently dropped.
+ ///
+ public int RowsImported { get; set; }
+
+ /// The title of each row counted in , so the user can see exactly
+ /// which selected rows were treated as already-imported duplicates.
+ public List SkippedRowTitles { get; set; } = [];
+}
diff --git a/src/WebApi.Contracts/Dto/GenericVideoGameImportPreviewRowDto.cs b/src/WebApi.Contracts/Dto/GenericVideoGameImportPreviewRowDto.cs
new file mode 100644
index 00000000..6010b16a
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/GenericVideoGameImportPreviewRowDto.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// One line item parsed from an uploaded generic video game transaction-history export, awaiting the user's
+/// review before anything is imported. See for what gets
+/// sent back once selected.
+///
+public class GenericVideoGameImportPreviewRowDto
+{
+ ///
+ /// Correlates a selected/edited row back to this one at commit time. Stable within one export, never stored.
+ ///
+ public required string RowId { get; set; }
+
+ ///
+ /// The game title, cleaned of a trailing platform suffix (e.g. "(PS4)") when present.
+ ///
+ public required string Title { get; set; }
+
+ public required string Platform { get; set; }
+
+ ///
+ /// The store's own specific product/edition text (e.g. "Grand Theft Auto V : Édition Premium").
+ ///
+ public string? ProductName { get; set; }
+
+ ///
+ /// The storefront the transaction was made on (e.g. "PlayStation Store").
+ ///
+ public required string Vendor { get; set; }
+
+ public required string TransactionId { get; set; }
+
+ public required string OrderId { get; set; }
+
+ public DateOnly? TransactionDate { get; set; }
+
+ public decimal? Price { get; set; }
+
+ ///
+ /// True when an existing video game already has a platform entry referencing this transaction. Defaults
+ /// to unchecked/hidden in the review UI, but stays editable/selectable in case the user wants to
+ /// re-import anyway.
+ ///
+ public bool AlreadyImported { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/HealthProfileDto.cs b/src/WebApi.Contracts/Dto/HealthProfileDto.cs
index 3a8e86b5..95e7a543 100644
--- a/src/WebApi.Contracts/Dto/HealthProfileDto.cs
+++ b/src/WebApi.Contracts/Dto/HealthProfileDto.cs
@@ -21,4 +21,9 @@ public class HealthProfileDto : IHasId
/// Free-text notes (blood type, allergies, anything worth keeping at hand).
///
public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/WebApi.Contracts/Dto/HouseDto.cs b/src/WebApi.Contracts/Dto/HouseDto.cs
index 683583f0..d2e4ffe4 100644
--- a/src/WebApi.Contracts/Dto/HouseDto.cs
+++ b/src/WebApi.Contracts/Dto/HouseDto.cs
@@ -42,4 +42,9 @@ public class HouseDto : IHasId
/// Free-text notes.
///
public string? Notes { get; set; }
+
+ ///
+ /// Tenant-owned cover image URL, shown on the list thumbnail and detail page header. Optional.
+ ///
+ public string? ImageUrl { get; set; }
}
diff --git a/src/WebApi.Contracts/Dto/OwnedVersionDto.cs b/src/WebApi.Contracts/Dto/OwnedVersionDto.cs
index 48b46fd3..11c0b1a4 100644
--- a/src/WebApi.Contracts/Dto/OwnedVersionDto.cs
+++ b/src/WebApi.Contracts/Dto/OwnedVersionDto.cs
@@ -32,4 +32,10 @@ public class OwnedVersionDto : IOwnedCopyDto
/// Free-text reference for this copy: edition name, order number, barcode...
///
public string? Reference { get; set; }
+
+ ///
+ /// The store's own specific product/edition text for this copy (e.g. a retailer's listing title),
+ /// distinct from the item's own Title. Optional free text.
+ ///
+ public string? ProductName { get; set; }
}
diff --git a/src/WebApi.Contracts/Dto/UserPreferencesDto.cs b/src/WebApi.Contracts/Dto/UserPreferencesDto.cs
new file mode 100644
index 00000000..d37d9372
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/UserPreferencesDto.cs
@@ -0,0 +1,12 @@
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+///
+/// The caller's own preferences. Always "mine" - never referenced by id, never listed.
+///
+public class UserPreferencesDto
+{
+ ///
+ /// The caller's opt-in/opt-out feature toggles.
+ ///
+ public UserPreferencesFeaturesDto Features { get; set; } = new();
+}
diff --git a/src/WebApi.Contracts/Dto/UserPreferencesFeaturesDto.cs b/src/WebApi.Contracts/Dto/UserPreferencesFeaturesDto.cs
new file mode 100644
index 00000000..590adada
--- /dev/null
+++ b/src/WebApi.Contracts/Dto/UserPreferencesFeaturesDto.cs
@@ -0,0 +1,15 @@
+namespace Keeptrack.WebApi.Contracts.Dto;
+
+public class UserPreferencesFeaturesDto
+{
+ ///
+ /// Shows an "open in a new tab" link to chasse-aux-livres.fr next to a book's ISBN field.
+ ///
+ public bool ShowChasseAuxLivresLink { get; set; }
+
+ ///
+ /// Shows an "open on Amazon" link next to an owned copy's Reference field whenever it was created by
+ /// the Amazon order-history import (i.e. its Reference text carries an ASIN).
+ ///
+ public bool ShowAmazonProductLink { get; set; }
+}
diff --git a/src/WebApi.Contracts/Dto/VideoGameDto.cs b/src/WebApi.Contracts/Dto/VideoGameDto.cs
index 10e1585c..f76141a9 100644
--- a/src/WebApi.Contracts/Dto/VideoGameDto.cs
+++ b/src/WebApi.Contracts/Dto/VideoGameDto.cs
@@ -35,11 +35,18 @@ public class VideoGameDto : IHasId, IReferenceLinkedDto
public string? ReferenceId { get; set; }
///
- /// Cover/poster image URL from the linked reference document - read-only, hydrated server-side on
- /// list reads and never accepted from client input.
+ /// Cover image URL shown on the list page - when set, otherwise the linked
+ /// reference document's own cover. Read-only, hydrated server-side on list reads; never accepted from
+ /// client input (edit instead).
///
public string? ImageUrl { get; set; }
+ ///
+ /// Tenant-owned cover image override, freely editable - takes priority over the linked reference's
+ /// cover wherever one is shown. Null means "use the reference's cover, if any".
+ ///
+ public string? CustomImageUrl { get; set; }
+
///
/// Filter-only query parameter: matches games with at least one platform entry (a game's copies).
/// Never populated on a returned game - see for the convention.
diff --git a/src/WebApi.Contracts/Dto/VideoGamePlatformDto.cs b/src/WebApi.Contracts/Dto/VideoGamePlatformDto.cs
index 350fe40c..7a2b91fd 100644
--- a/src/WebApi.Contracts/Dto/VideoGamePlatformDto.cs
+++ b/src/WebApi.Contracts/Dto/VideoGamePlatformDto.cs
@@ -63,4 +63,10 @@ public class VideoGamePlatformDto : IOwnedCopyDto
/// Free-text reference for this copy: edition name, order number, barcode...
///
public string? Reference { get; set; }
+
+ ///
+ /// The store's own specific product/edition text for this copy (e.g. "Grand Theft Auto V : Édition Premium"),
+ /// as opposed to the game's own title.
+ ///
+ public string? ProductName { get; set; }
}
diff --git a/src/WebApi/Controllers/AlbumController.cs b/src/WebApi/Controllers/AlbumController.cs
index 7c4e8364..2c8722f2 100644
--- a/src/WebApi/Controllers/AlbumController.cs
+++ b/src/WebApi/Controllers/AlbumController.cs
@@ -21,10 +21,18 @@ public class AlbumController(
{
///
/// Hydrates each page item's cover image from its linked reference document - one batched lookup per
- /// page (see ), keyed by the id-bearing documents only.
+ /// page (see ), keyed by the id-bearing documents only. An album with
+ /// its own set overrides that afterward - see
+ /// .
///
- protected override Task OnListMappedAsync(List dtos)
- => ReferenceImageHydrator.HydrateAsync(dtos, referenceRepository.FindByIdsAsync, x => x.ImageUrl);
+ protected override async Task OnListMappedAsync(List dtos)
+ {
+ await ReferenceImageHydrator.HydrateAsync(dtos, referenceRepository.FindByIdsAsync, x => x.ImageUrl);
+ foreach (var dto in dtos.Where(d => !string.IsNullOrEmpty(d.CustomImageUrl)))
+ {
+ dto.ImageUrl = dto.CustomImageUrl;
+ }
+ }
///
/// Fires a best-effort background Discogs match for the new album - see .
@@ -65,4 +73,21 @@ public async Task> RefreshReference(string id)
model = await enrichmentService.TryLinkExistingAlbumReferenceAsync(model);
return Ok(Mapper.ToDto(model));
}
+
+ ///
+ /// Admin-only: clears this album's reference link and permanently deletes the shared reference
+ /// document - see .
+ ///
+ [HttpPost("{id}/unlink-reference")]
+ [Authorize(Policy = "AdminOnly")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ public async Task> UnlinkReference(string id)
+ {
+ var model = await dataRepository.FindOneAsync(id, this.GetUserId());
+ if (model is null) return NotFound();
+
+ model = await enrichmentService.UnlinkAlbumReferenceAsync(model);
+ return Ok(Mapper.ToDto(model));
+ }
}
diff --git a/src/WebApi/Controllers/AmazonImportController.cs b/src/WebApi/Controllers/AmazonImportController.cs
new file mode 100644
index 00000000..0c88ba58
--- /dev/null
+++ b/src/WebApi/Controllers/AmazonImportController.cs
@@ -0,0 +1,298 @@
+using System.Diagnostics.CodeAnalysis;
+using Keeptrack.Common.System;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.Domain.Services;
+using Keeptrack.WebApi.Mappers;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Keeptrack.WebApi.Controllers;
+
+///
+/// Previews an Amazon.fr order-history export and commits the rows the user selected/edited in the review
+/// UI as books, movies, TV shows, video games, gear, or collectibles (picked per row - see ).
+/// Synchronous on both ends: unlike the TV Time import, there is no external API call in the loop, so even
+/// a multi-year export completes well within a normal request.
+///
+[ApiController]
+[Authorize(Policy = "MemberOnly")]
+[Route("api/import/amazon")]
+public class AmazonImportController(
+ IBookRepository bookRepository,
+ IMovieRepository movieRepository,
+ ITvShowRepository tvShowRepository,
+ IVideoGameRepository videoGameRepository,
+ IGearRepository gearRepository,
+ ICollectibleRepository collectibleRepository,
+ AmazonOrderPreviewRowDtoMapper previewMapper) : ControllerBase
+{
+ ///
+ /// Parses the uploaded order-history CSV and returns every line item for review - nothing is persisted
+ /// by this call. Amazon's export carries no category column, so every row is returned; the review UI
+ /// defaults to showing only rows.
+ ///
+ [HttpPost("preview")]
+ [RequestSizeLimit(20_000_000)]
+ [Consumes("multipart/form-data")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(400)]
+ [SuppressMessage("Security", "S5693:Make sure the content length limit is safe here",
+ Justification = "The limit IS set (20 MB), deliberately above Sonar's 8 MB default: a multi-year Amazon order-history " +
+ "export can be sizeable, and the endpoint is authenticated, member-only, admin-of-your-own-data.")]
+ public async Task>> Preview(IFormFile file)
+ {
+ if (file.Length == 0)
+ {
+ return BadRequest();
+ }
+
+ var ownerId = this.GetUserId();
+
+ // "Already imported" must be checked across every type, not just books - a row previously imported
+ // as a movie must still be flagged when the same export is uploaded again.
+ var existingBooks = await FindAllAsync(bookRepository, ownerId, new BookModel { OwnerId = ownerId, Title = string.Empty, Author = string.Empty });
+ var existingMovies = await FindAllAsync(movieRepository, ownerId, new MovieModel { OwnerId = ownerId, Title = string.Empty });
+ var existingTvShows = await FindAllAsync(tvShowRepository, ownerId, new TvShowModel { OwnerId = ownerId, Title = string.Empty });
+ var existingVideoGames = await FindAllAsync(videoGameRepository, ownerId, new VideoGameModel { OwnerId = ownerId, Title = string.Empty });
+ var existingGear = await FindAllAsync(gearRepository, ownerId, new GearModel { OwnerId = ownerId, Title = string.Empty });
+ var existingCollectibles = await FindAllAsync(collectibleRepository, ownerId, new CollectibleModel { OwnerId = ownerId, Title = string.Empty });
+
+ var alreadyImportedReferences = new HashSet();
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingBooks, b => b.OwnedVersions.Select(v => v.Reference)));
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingMovies, m => m.OwnedVersions.Select(v => v.Reference)));
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingTvShows, t => t.OwnedVersions.Select(v => v.Reference)));
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingVideoGames, g => g.Platforms.Select(p => p.Reference)));
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingGear, g => g.OwnedVersions.Select(v => v.Reference)));
+ alreadyImportedReferences.UnionWith(OwnedItemImportMergeService.FindImportedReferences(existingCollectibles, c => c.OwnedVersions.Select(v => v.Reference)));
+
+ await using var stream = file.OpenReadStream();
+ var rows = AmazonOrderPreviewService.BuildPreview(stream, alreadyImportedReferences);
+
+ return Ok(rows.Select(previewMapper.ToDto).ToList());
+ }
+
+ ///
+ /// Creates/updates items from the rows the user selected in the review UI, grouped by the media type
+ /// each row was assigned. A row whose (normalized) title matches an existing item of the same type - or
+ /// one created earlier in this same request - gets an additional owned copy instead of a duplicate
+ /// item; see .
+ ///
+ [HttpPost("commit")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(400)]
+ public async Task> Commit(AmazonImportCommitRequestDto request)
+ {
+ var ownerId = this.GetUserId();
+ var result = new AmazonImportCommitResultDto();
+
+ var itemMissingMediaType = request.Items.FirstOrDefault(item => item.MediaType is null);
+ if (itemMissingMediaType is not null)
+ {
+ throw new ArgumentException($"A media type is required to import '{itemMissingMediaType.Title}'.");
+ }
+
+ var bookItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.Book).ToList();
+ var movieItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.Movie).ToList();
+ var tvShowItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.TvShow).ToList();
+ var videoGameItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.VideoGame).ToList();
+ var gearItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.Gear).ToList();
+ var collectibleItems = request.Items.Where(i => i.MediaType == AmazonImportMediaType.Collectible).ToList();
+
+ var videoGameItemMissingPlatform = videoGameItems.FirstOrDefault(item => string.IsNullOrWhiteSpace(item.Platform));
+ if (videoGameItemMissingPlatform is not null)
+ {
+ throw new ArgumentException($"A platform is required to import '{videoGameItemMissingPlatform.Title}' as a video game.");
+ }
+
+ if (bookItems.Count > 0)
+ {
+ var existingBooks = await FindAllAsync(bookRepository, ownerId, new BookModel { OwnerId = ownerId, Title = string.Empty, Author = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ bookRepository, existingBooks, bookItems.Select(ToOwnedItemRequestItem).ToList(),
+ b => b.Title, b => b.OwnedVersions.Select(v => v.Reference),
+ i => i.Title, i => i.OwnedVersion.Reference,
+ item => new BookModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Author = string.Empty,
+ Year = item.Year,
+ Isbn = item.Isbn,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, item.Isbn),
+ OwnedVersions = [item.OwnedVersion]
+ },
+ (book, item) => book.OwnedVersions.Add(item.OwnedVersion), ownerId);
+ (result.BooksCreated, result.BooksMergedInto, result.BooksSkipped) = (created, mergedInto, skipped);
+ }
+
+ if (movieItems.Count > 0)
+ {
+ var existingMovies = await FindAllAsync(movieRepository, ownerId, new MovieModel { OwnerId = ownerId, Title = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ movieRepository, existingMovies, movieItems.Select(ToOwnedItemRequestItem).ToList(),
+ m => m.Title, m => m.OwnedVersions.Select(v => v.Reference),
+ i => i.Title, i => i.OwnedVersion.Reference,
+ item => new MovieModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, null),
+ OwnedVersions = [item.OwnedVersion]
+ },
+ (movie, item) => movie.OwnedVersions.Add(item.OwnedVersion), ownerId);
+ (result.MoviesCreated, result.MoviesMergedInto, result.MoviesSkipped) = (created, mergedInto, skipped);
+ }
+
+ if (tvShowItems.Count > 0)
+ {
+ var existingTvShows = await FindAllAsync(tvShowRepository, ownerId, new TvShowModel { OwnerId = ownerId, Title = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ tvShowRepository, existingTvShows, tvShowItems.Select(ToOwnedItemRequestItem).ToList(),
+ t => t.Title, t => t.OwnedVersions.Select(v => v.Reference),
+ i => i.Title, i => i.OwnedVersion.Reference,
+ item => new TvShowModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, null),
+ OwnedVersions = [item.OwnedVersion]
+ },
+ (tvShow, item) => tvShow.OwnedVersions.Add(item.OwnedVersion), ownerId);
+ (result.TvShowsCreated, result.TvShowsMergedInto, result.TvShowsSkipped) = (created, mergedInto, skipped);
+ }
+
+ if (videoGameItems.Count > 0)
+ {
+ var existingVideoGames = await FindAllAsync(videoGameRepository, ownerId, new VideoGameModel { OwnerId = ownerId, Title = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ videoGameRepository, existingVideoGames, videoGameItems.Select(ToVideoGameRequestItem).ToList(),
+ g => g.Title, g => g.Platforms.Select(p => p.Reference),
+ i => i.Title, i => i.Platform.Reference,
+ item => new VideoGameModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, null),
+ Platforms = [item.Platform]
+ },
+ (game, item) => game.Platforms.Add(item.Platform), ownerId);
+ (result.VideoGamesCreated, result.VideoGamesMergedInto, result.VideoGamesSkipped) = (created, mergedInto, skipped);
+ }
+
+ if (gearItems.Count > 0)
+ {
+ var existingGear = await FindAllAsync(gearRepository, ownerId, new GearModel { OwnerId = ownerId, Title = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ gearRepository, existingGear, gearItems.Select(ToOwnedItemRequestItem).ToList(),
+ g => g.Title, g => g.OwnedVersions.Select(v => v.Reference),
+ i => i.Title, i => i.OwnedVersion.Reference,
+ item => new GearModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, null),
+ OwnedVersions = [item.OwnedVersion]
+ },
+ (gear, item) => gear.OwnedVersions.Add(item.OwnedVersion), ownerId);
+ (result.GearCreated, result.GearMergedInto, result.GearSkipped) = (created, mergedInto, skipped);
+ }
+
+ if (collectibleItems.Count > 0)
+ {
+ var existingCollectibles = await FindAllAsync(collectibleRepository, ownerId, new CollectibleModel { OwnerId = ownerId, Title = string.Empty });
+ var (created, mergedInto, skipped) = await CommitAsync(
+ collectibleRepository, existingCollectibles, collectibleItems.Select(ToOwnedItemRequestItem).ToList(),
+ c => c.Title, c => c.OwnedVersions.Select(v => v.Reference),
+ i => i.Title, i => i.OwnedVersion.Reference,
+ item => new CollectibleModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = AmazonImportMergeService.BuildAmazonProvenanceNotes(item.AmazonTitle, null),
+ OwnedVersions = [item.OwnedVersion]
+ },
+ (collectible, item) => collectible.OwnedVersions.Add(item.OwnedVersion), ownerId);
+ (result.CollectiblesCreated, result.CollectiblesMergedInto, result.CollectiblesSkipped) = (created, mergedInto, skipped);
+ }
+
+ return Ok(result);
+ }
+
+ private static AmazonOwnedItemImportRequestItem ToOwnedItemRequestItem(AmazonImportCommitItemDto item) => new()
+ {
+ Title = item.Title,
+ AmazonTitle = item.AmazonTitle,
+ Year = item.Year,
+ Isbn = item.Isbn,
+ OwnedVersion = ToOwnedVersion(item)
+ };
+
+ private static AmazonVideoGameImportRequestItem ToVideoGameRequestItem(AmazonImportCommitItemDto item) => new()
+ {
+ Title = item.Title,
+ AmazonTitle = item.AmazonTitle,
+ Year = item.Year,
+ Platform = new VideoGamePlatformModel
+ {
+ Platform = item.Platform!,
+ CopyType = ToDomainCopyType(item.CopyType),
+ Price = item.Price,
+ Vendor = item.Vendor,
+ AcquiredAt = item.AcquiredAt,
+ Reference = AmazonImportMergeService.FormatOrderReference(item.OrderId, item.Asin)
+ }
+ };
+
+ private static OwnedVersionModel ToOwnedVersion(AmazonImportCommitItemDto item) => new()
+ {
+ CopyType = ToDomainCopyType(item.CopyType),
+ Price = item.Price,
+ Vendor = item.Vendor,
+ AcquiredAt = item.AcquiredAt,
+ // Derived server-side from the order id + ASIN the preview row reported, never from a client-supplied
+ // Reference string - this is what disambiguates two different items sharing one Amazon order.
+ Reference = AmazonImportMergeService.FormatOrderReference(item.OrderId, item.Asin)
+ };
+
+ private static Keeptrack.Domain.Models.CopyType ToDomainCopyType(Keeptrack.WebApi.Contracts.Dto.CopyType copyType) =>
+ Enum.Parse(copyType.ToString());
+
+ private static async Task<(int Created, int MergedInto, int Skipped)> CommitAsync(
+ IDataRepository repository,
+ List existingItems,
+ List requestItems,
+ Func getExistingTitle,
+ Func> getExistingReferences,
+ Func getItemTitle,
+ Func getItemReference,
+ Func createNew,
+ Action appendOwnedCopy,
+ string ownerId)
+ where TModel : class, IHasIdAndOwnerId
+ {
+ var plan = OwnedItemImportMergeService.ComputeCommitPlan(
+ existingItems, requestItems, getExistingTitle, getExistingReferences, getItemTitle, getItemReference, createNew, appendOwnedCopy);
+
+ foreach (var item in plan.ItemsToCreate)
+ {
+ await repository.CreateAsync(item);
+ }
+
+ foreach (var item in plan.ItemsToUpdate)
+ {
+ await repository.UpdateAsync(item.Id!, item, ownerId);
+ }
+
+ return (plan.ItemsToCreate.Count, plan.ItemsToUpdate.Count, plan.OwnedCopiesSkipped);
+ }
+
+ private static async Task> FindAllAsync(IDataRepository repository, string ownerId, TModel blankSample)
+ where TModel : IHasIdAndOwnerId =>
+ (await repository.FindAllAsync(ownerId, 1, int.MaxValue, null, blankSample)).Items;
+}
diff --git a/src/WebApi/Controllers/BookController.cs b/src/WebApi/Controllers/BookController.cs
index ee55bd58..b978c64e 100644
--- a/src/WebApi/Controllers/BookController.cs
+++ b/src/WebApi/Controllers/BookController.cs
@@ -74,4 +74,21 @@ public async Task> RefreshReference(string id)
model = await enrichmentService.TryLinkExistingBookReferenceAsync(model);
return Ok(Mapper.ToDto(model));
}
+
+ ///
+ /// Admin-only: clears this book's reference link and permanently deletes the shared reference
+ /// document - see .
+ ///
+ [HttpPost("{id}/unlink-reference")]
+ [Authorize(Policy = "AdminOnly")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ public async Task> UnlinkReference(string id)
+ {
+ var model = await dataRepository.FindOneAsync(id, this.GetUserId());
+ if (model is null) return NotFound();
+
+ model = await enrichmentService.UnlinkBookReferenceAsync(model);
+ return Ok(Mapper.ToDto(model));
+ }
}
diff --git a/src/WebApi/Controllers/CollectibleController.cs b/src/WebApi/Controllers/CollectibleController.cs
new file mode 100644
index 00000000..cba1b80a
--- /dev/null
+++ b/src/WebApi/Controllers/CollectibleController.cs
@@ -0,0 +1,13 @@
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.WebApi.Mappers;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Keeptrack.WebApi.Controllers;
+
+[ApiController]
+[Authorize(Policy = "MemberOnly")]
+[Route("api/collectibles")]
+public class CollectibleController(IDtoMapper mapper, ICollectibleRepository dataRepository)
+ : DataCrudControllerBase(mapper, dataRepository);
diff --git a/src/WebApi/Controllers/GearController.cs b/src/WebApi/Controllers/GearController.cs
new file mode 100644
index 00000000..5a7b0d6f
--- /dev/null
+++ b/src/WebApi/Controllers/GearController.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.WebApi.Mappers;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Keeptrack.WebApi.Controllers;
+
+[ApiController]
+[Authorize(Policy = "MemberOnly")]
+[Route("api/gear")]
+public class GearController(IDtoMapper mapper, IGearRepository dataRepository)
+ : DataCrudControllerBase(mapper, dataRepository)
+{
+ ///
+ /// Distinct categories already used across this tenant's gear - feeds the list page's category
+ /// filter buttons and the detail page's suggested values. See
+ /// .
+ ///
+ [HttpGet("categories")]
+ [ProducesResponseType(200)]
+ public async Task>> GetCategories()
+ {
+ var categories = await dataRepository.FindDistinctCategoriesAsync(this.GetUserId());
+ return Ok(categories);
+ }
+}
diff --git a/src/WebApi/Controllers/GenericVideoGameImportController.cs b/src/WebApi/Controllers/GenericVideoGameImportController.cs
new file mode 100644
index 00000000..c9be506f
--- /dev/null
+++ b/src/WebApi/Controllers/GenericVideoGameImportController.cs
@@ -0,0 +1,137 @@
+using System.Diagnostics.CodeAnalysis;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.Domain.Services;
+using Keeptrack.WebApi.Mappers;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Keeptrack.WebApi.Controllers;
+
+///
+/// Previews a generic video game transaction-history export and commits the rows the user selected/edited in
+/// the review UI as video games. Unlike , every row is always a video
+/// game (the export is store purchase history for games specifically), so there's no per-row media-type
+/// picker and no generic multi-type commit branching - just one direct call into the shared
+/// engine.
+///
+[ApiController]
+[Authorize(Policy = "MemberOnly")]
+[Route("api/import/video-games")]
+public class GenericVideoGameImportController(
+ IVideoGameRepository videoGameRepository,
+ GenericVideoGameImportPreviewRowDtoMapper previewMapper) : ControllerBase
+{
+ ///
+ /// Parses the uploaded transaction-history CSV and returns every line item for review - nothing is
+ /// persisted by this call.
+ ///
+ [HttpPost("preview")]
+ [RequestSizeLimit(20_000_000)]
+ [Consumes("multipart/form-data")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(400)]
+ [SuppressMessage("Security", "S5693:Make sure the content length limit is safe here",
+ Justification = "The limit IS set (20 MB), deliberately above Sonar's 8 MB default: a multi-year " +
+ "transaction-history export can be sizeable, and the endpoint is authenticated, member-only, admin-of-your-own-data.")]
+ public async Task>> Preview(IFormFile file)
+ {
+ if (file.Length == 0)
+ {
+ return BadRequest();
+ }
+
+ var ownerId = this.GetUserId();
+
+ var existingVideoGames = await FindAllAsync(ownerId);
+ var alreadyImportedReferences = OwnedItemImportMergeService.FindImportedReferences(existingVideoGames, g => g.Platforms.Select(p => p.Reference));
+
+ await using var stream = file.OpenReadStream();
+ var rows = GenericVideoGameImportService.BuildPreview(stream, alreadyImportedReferences);
+
+ return Ok(rows.Select(previewMapper.ToDto).ToList());
+ }
+
+ ///
+ /// Creates/updates video games from the rows the user selected in the review UI. A row whose (normalized)
+ /// title matches an existing video game - or one created earlier in this same request - gets an
+ /// additional platform entry instead of a duplicate item; see
+ /// .
+ ///
+ [HttpPost("commit")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(400)]
+ public async Task> Commit(GenericVideoGameImportCommitRequestDto request)
+ {
+ var ownerId = this.GetUserId();
+
+ var itemMissingPlatform = request.Items.FirstOrDefault(item => string.IsNullOrWhiteSpace(item.Platform));
+ if (itemMissingPlatform is not null)
+ {
+ throw new ArgumentException($"A platform is required to import '{itemMissingPlatform.Title}'.");
+ }
+
+ var existingVideoGames = await FindAllAsync(ownerId);
+ var requestItems = request.Items.Select(ToRequestItem).ToList();
+
+ var plan = OwnedItemImportMergeService.ComputeCommitPlan(
+ existingVideoGames, requestItems,
+ g => g.Title, g => g.Platforms.Select(p => p.Reference),
+ i => i.Title, i => i.Platform.Reference,
+ item => new VideoGameModel
+ {
+ OwnerId = ownerId,
+ Title = item.Title,
+ Year = item.Year,
+ Notes = GenericVideoGameImportService.BuildProvenanceNotes(item.Platform.Vendor!, item.SourceTitle),
+ Platforms = [item.Platform]
+ },
+ (game, item) => game.Platforms.Add(item.Platform));
+
+ foreach (var item in plan.ItemsToCreate)
+ {
+ await videoGameRepository.CreateAsync(item);
+ }
+
+ foreach (var item in plan.ItemsToUpdate)
+ {
+ await videoGameRepository.UpdateAsync(item.Id!, item, ownerId);
+ }
+
+ return Ok(new GenericVideoGameImportCommitResultDto
+ {
+ VideoGamesCreated = plan.ItemsToCreate.Count,
+ VideoGamesMergedInto = plan.ItemsToUpdate.Count,
+ VideoGamesSkipped = plan.OwnedCopiesSkipped,
+ RowsImported = plan.OwnedCopiesAdded,
+ SkippedRowTitles = plan.SkippedTitles
+ });
+ }
+
+ private static GenericVideoGameImportRequestItem ToRequestItem(GenericVideoGameImportCommitItemDto item) => new()
+ {
+ Title = item.Title,
+ SourceTitle = item.SourceTitle,
+ Year = item.Year,
+ Platform = new VideoGamePlatformModel
+ {
+ Platform = item.Platform!,
+ CopyType = ToDomainCopyType(item.CopyType),
+ ProductName = item.ProductName,
+ Price = item.Price,
+ Vendor = item.Vendor,
+ AcquiredAt = item.AcquiredAt,
+ // Derived server-side from the transaction id + order id + product name the preview row
+ // reported, never from a client-supplied Reference string - the product name is what
+ // disambiguates two different items sharing one transaction/order (a single transaction can
+ // bundle several products), and the whole string doubles as the exact-match dedup key on a re-import.
+ Reference = GenericVideoGameImportService.FormatReference(item.TransactionId, item.OrderId, item.ProductName, item.SourceTitle)
+ }
+ };
+
+ private static Keeptrack.Domain.Models.CopyType ToDomainCopyType(Keeptrack.WebApi.Contracts.Dto.CopyType copyType) =>
+ Enum.Parse(copyType.ToString());
+
+ private async Task> FindAllAsync(string ownerId) =>
+ (await videoGameRepository.FindAllAsync(ownerId, 1, int.MaxValue, null, new VideoGameModel { OwnerId = ownerId, Title = string.Empty })).Items;
+}
diff --git a/src/WebApi/Controllers/MovieController.cs b/src/WebApi/Controllers/MovieController.cs
index 0de482d1..733ffe91 100644
--- a/src/WebApi/Controllers/MovieController.cs
+++ b/src/WebApi/Controllers/MovieController.cs
@@ -67,4 +67,21 @@ public async Task> RefreshReference(string id)
model = await enrichmentService.TryLinkExistingMovieReferenceAsync(model);
return Ok(Mapper.ToDto(model));
}
+
+ ///
+ /// Admin-only: clears this movie's reference link and permanently deletes the shared reference
+ /// document - see .
+ ///
+ [HttpPost("{id}/unlink-reference")]
+ [Authorize(Policy = "AdminOnly")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ public async Task> UnlinkReference(string id)
+ {
+ var model = await dataRepository.FindOneAsync(id, this.GetUserId());
+ if (model is null) return NotFound();
+
+ model = await enrichmentService.UnlinkMovieReferenceAsync(model);
+ return Ok(Mapper.ToDto(model));
+ }
}
diff --git a/src/WebApi/Controllers/StatsController.cs b/src/WebApi/Controllers/StatsController.cs
index 544f96c3..6eda553a 100644
--- a/src/WebApi/Controllers/StatsController.cs
+++ b/src/WebApi/Controllers/StatsController.cs
@@ -22,7 +22,9 @@ public class StatsController(
IPlaylistRepository playlistRepository,
IVideoGameRepository videoGameRepository,
ICarRepository carRepository,
- IHouseRepository houseRepository) : ControllerBase
+ IHouseRepository houseRepository,
+ ICollectibleRepository collectibleRepository,
+ IGearRepository gearRepository) : ControllerBase
{
///
/// How many items the caller has in each collection.
@@ -43,7 +45,9 @@ public async Task> Get()
Playlists = await playlistRepository.CountAsync(ownerId),
VideoGames = await videoGameRepository.CountAsync(ownerId),
Cars = await carRepository.CountAsync(ownerId),
- Houses = await houseRepository.CountAsync(ownerId)
+ Houses = await houseRepository.CountAsync(ownerId),
+ Collectibles = await collectibleRepository.CountAsync(ownerId),
+ Gear = await gearRepository.CountAsync(ownerId)
});
}
}
diff --git a/src/WebApi/Controllers/SystemStatusController.cs b/src/WebApi/Controllers/SystemStatusController.cs
index e497e86d..d0f55008 100644
--- a/src/WebApi/Controllers/SystemStatusController.cs
+++ b/src/WebApi/Controllers/SystemStatusController.cs
@@ -41,7 +41,7 @@ public async Task> Get()
{
InstanceName = Environment.MachineName,
IsReferenceSyncEnabled = appConfiguration.IsReferenceSyncEnabled,
- // the *default* provider used for automatic/background resolution - see BookReferenceClientRegistry;
+ // the *default* provider used for automatic/background resolution - see BookReferenceClientRegistry.
// an admin can search/link with any registered provider regardless of this value (GET book-providers)
BookProvider = string.IsNullOrEmpty(appConfiguration.BookReferenceProvider) ? "googlebooks" : appConfiguration.BookReferenceProvider,
ReferenceSyncLease = lease is null
diff --git a/src/WebApi/Controllers/TvShowController.cs b/src/WebApi/Controllers/TvShowController.cs
index bf5de1cc..b7f50e3a 100644
--- a/src/WebApi/Controllers/TvShowController.cs
+++ b/src/WebApi/Controllers/TvShowController.cs
@@ -69,4 +69,26 @@ public async Task> RefreshReference(string id)
model = await enrichmentService.TryLinkExistingTvShowReferenceAsync(model);
return Ok(Mapper.ToDto(model));
}
+
+ ///
+ /// Admin-only: clears this show's reference link and permanently deletes the shared reference document
+ /// itself, rather than merely detaching this one tenant's link - the admin has determined the match was
+ /// wrong, so the document behind it is bad data. Unlike (open to any
+ /// owner, harmless/idempotent), this mutates shared data other tenants could theoretically point at, so
+ /// it's gated to admins via a method-level policy on top of the controller's own plain [Authorize] .
+ /// Clearing ReferenceId is also what makes the Blazor detail page's InlineReferenceLinker
+ /// search card reappear, letting the admin immediately pick the correct match.
+ ///
+ [HttpPost("{id}/unlink-reference")]
+ [Authorize(Policy = "AdminOnly")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ public async Task> UnlinkReference(string id)
+ {
+ var model = await dataRepository.FindOneAsync(id, this.GetUserId());
+ if (model is null) return NotFound();
+
+ model = await enrichmentService.UnlinkTvShowReferenceAsync(model);
+ return Ok(Mapper.ToDto(model));
+ }
}
diff --git a/src/WebApi/Controllers/UserPreferencesController.cs b/src/WebApi/Controllers/UserPreferencesController.cs
new file mode 100644
index 00000000..8c3d66a9
--- /dev/null
+++ b/src/WebApi/Controllers/UserPreferencesController.cs
@@ -0,0 +1,47 @@
+using System.Threading.Tasks;
+using Keeptrack.Domain.Models;
+using Keeptrack.Domain.Repositories;
+using Keeptrack.WebApi.Contracts.Dto;
+using Keeptrack.WebApi.Mappers;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Keeptrack.WebApi.Controllers;
+
+///
+/// The caller's own opt-in/opt-out feature toggles - a singleton-per-owner resource (no id in the route,
+/// never listed), so a plain like rather than
+/// . Available to every authenticated user (no
+/// "MemberOnly" policy) - these are UI preferences, not a quota-relevant feature.
+///
+[ApiController]
+[Authorize]
+[Route("api/user-preferences")]
+public class UserPreferencesController(IUserPreferencesRepository repository, IDtoMapper mapper) : ControllerBase
+{
+ ///
+ /// The caller's preferences, defaulting to all-off when nothing has been saved yet.
+ ///
+ [HttpGet]
+ [ProducesResponseType(200)]
+ public async Task> Get()
+ {
+ var ownerId = this.GetUserId();
+ var model = await repository.FindByOwnerIdAsync(ownerId) ?? new UserPreferencesModel { OwnerId = ownerId };
+ return Ok(mapper.ToDto(model));
+ }
+
+ ///
+ /// Replaces the caller's preferences.
+ ///
+ [HttpPut]
+ [ProducesResponseType(204)]
+ public async Task Put(UserPreferencesDto dto)
+ {
+ var ownerId = this.GetUserId();
+ var model = mapper.ToModel(dto);
+ model.OwnerId = ownerId;
+ await repository.UpsertAsync(model);
+ return NoContent();
+ }
+}
diff --git a/src/WebApi/Controllers/VideoGameController.cs b/src/WebApi/Controllers/VideoGameController.cs
index b7d6d958..e8e9485c 100644
--- a/src/WebApi/Controllers/VideoGameController.cs
+++ b/src/WebApi/Controllers/VideoGameController.cs
@@ -21,10 +21,18 @@ public class VideoGameController(
{
///
/// Hydrates each page item's cover image from its linked reference document - one batched lookup per
- /// page (see ), keyed by the id-bearing documents only.
+ /// page (see ), keyed by the id-bearing documents only. A game with
+ /// its own set overrides that afterward - see
+ /// .
///
- protected override Task OnListMappedAsync(List dtos)
- => ReferenceImageHydrator.HydrateAsync(dtos, referenceRepository.FindByIdsAsync, x => x.ImageUrl);
+ protected override async Task OnListMappedAsync(List dtos)
+ {
+ await ReferenceImageHydrator.HydrateAsync(dtos, referenceRepository.FindByIdsAsync, x => x.ImageUrl);
+ foreach (var dto in dtos.Where(d => !string.IsNullOrEmpty(d.CustomImageUrl)))
+ {
+ dto.ImageUrl = dto.CustomImageUrl;
+ }
+ }
///
/// Fires a best-effort background RAWG match for the new game - see .
@@ -64,4 +72,21 @@ public async Task> RefreshReference(string id)
model = await enrichmentService.TryLinkExistingVideoGameReferenceAsync(model);
return Ok(Mapper.ToDto(model));
}
+
+ ///
+ /// Admin-only: clears this game's reference link and permanently deletes the shared reference
+ /// document - see .
+ ///
+ [HttpPost("{id}/unlink-reference")]
+ [Authorize(Policy = "AdminOnly")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ public async Task> UnlinkReference(string id)
+ {
+ var model = await dataRepository.FindOneAsync(id, this.GetUserId());
+ if (model is null) return NotFound();
+
+ model = await enrichmentService.UnlinkVideoGameReferenceAsync(model);
+ return Ok(Mapper.ToDto(model));
+ }
}
diff --git a/src/WebApi/DependencyInjection/InfrastructureServiceCollectionExtensions.cs b/src/WebApi/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
index 133c8efe..ed5773ec 100644
--- a/src/WebApi/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
+++ b/src/WebApi/DependencyInjection/InfrastructureServiceCollectionExtensions.cs
@@ -41,9 +41,12 @@ internal static void AddMongoDbInfrastructure(this IServiceCollection services,
services.AddSingleton, HouseHistoryStorageMapper>();
services.AddSingleton, HealthProfileStorageMapper>();
services.AddSingleton, HealthRecordStorageMapper>();
+ services.AddSingleton, CollectibleStorageMapper>();
+ services.AddSingleton, GearStorageMapper>();
services.AddSingleton();
services.AddSingleton