From b05e415acfb088add39c2b327d0dd650129ce1b3 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 7 Dec 2025 13:38:23 +0000 Subject: [PATCH] Apply editorconfig --- CLAUDE.md | 4 +- docs/.vitepress/config.mts | 112 +- docs/package-lock.json | 3590 ++--- docs/package.json | 2 +- docs/public/logo.svg | 28 +- frontend/eslint.config.mjs | 174 +- frontend/local-client-cli/src/args.test.ts | 358 +- frontend/local-client-cli/src/args.ts | 232 +- frontend/local-client-cli/src/cli.ts | 396 +- frontend/local-client-cli/src/healthcheck.ts | 84 +- .../local-client-cli/src/logger-formatter.ts | 104 +- .../src/node-filesystem.test.ts | 206 +- .../local-client-cli/src/node-filesystem.ts | 360 +- frontend/obsidian-plugin/.hotreload | 1 + frontend/obsidian-plugin/README.md | 5 - frontend/obsidian-plugin/manifest.json | 18 +- .../src/obsidian-file-system.ts | 272 +- .../src/views/cursors/file-explorer.scss | 2 +- .../cursors/get-selections-from-editor.ts | 18 +- .../cursors/local-cursor-update-listener.ts | 76 +- .../src/views/cursors/remote-cursor-theme.ts | 98 +- .../src/views/cursors/remote-cursor-widget.ts | 72 +- .../views/cursors/remote-cursors-plugin.ts | 438 +- .../editor-status-display-manager.scss | 70 +- .../editor-status-display-manager.ts | 152 +- .../src/views/history/history-view.scss | 98 +- .../src/views/history/history-view.ts | 400 +- .../src/views/logs/logs-view.scss | 120 +- .../src/views/logs/logs-view.ts | 308 +- .../src/views/settings/settings-tab.scss | 222 +- .../src/views/settings/settings-tab.ts | 1002 +- .../src/views/status-bar/status-bar.scss | 20 +- .../src/views/status-bar/status-bar.ts | 112 +- .../status-description.scss | 44 +- .../status-description/status-description.ts | 244 +- frontend/obsidian-plugin/webpack.config.js | 216 +- frontend/package-lock.json | 12259 ++++++++++------ frontend/package.json | 60 +- .../file-operations/file-not-found-error.ts | 14 +- .../file-operations/file-operations.test.ts | 390 +- .../src/file-operations/file-operations.ts | 488 +- .../file-operations/filesystem-operations.ts | 46 +- .../safe-filesystem-operations.ts | 280 +- frontend/sync-client/src/index.ts | 34 +- .../sync-client/src/persistence/database.ts | 592 +- .../src/persistence/persistence.ts | 4 +- .../src/services/authentication-error.ts | 8 +- .../src/services/fetch-controller.test.ts | 274 +- .../src/services/fetch-controller.ts | 244 +- .../sync-client/src/services/server-config.ts | 130 +- .../services/server-version-mismatch-error.ts | 8 +- .../src/services/sync-reset-error.ts | 8 +- .../sync-client/src/services/sync-service.ts | 712 +- .../src/services/types/ClientCursors.ts | 6 +- .../services/types/CreateDocumentVersion.ts | 18 +- .../types/CursorPositionFromClient.ts | 2 +- .../types/CursorPositionFromServer.ts | 2 +- .../src/services/types/CursorSpan.ts | 4 +- .../services/types/DeleteDocumentVersion.ts | 2 +- .../services/types/DocumentUpdateResponse.ts | 4 +- .../src/services/types/DocumentVersion.ts | 16 +- .../types/DocumentVersionWithoutContent.ts | 16 +- .../src/services/types/DocumentWithCursors.ts | 8 +- .../types/FetchLatestDocumentsResponse.ts | 10 +- .../src/services/types/PingResponse.ts | 36 +- .../src/services/types/SerializedError.ts | 6 +- .../services/types/UpdateDocumentVersion.ts | 6 +- .../types/UpdateTextDocumentVersion.ts | 6 +- .../services/types/WebSocketClientMessage.ts | 4 +- .../src/services/types/WebSocketHandshake.ts | 6 +- .../services/types/WebSocketServerMessage.ts | 4 +- .../services/types/WebSocketVaultUpdate.ts | 4 +- .../src/services/websocket-manager.test.ts | 448 +- frontend/sync-client/src/sync-client.ts | 22 +- .../sync-client/src/sync-operations/syncer.ts | 8 +- .../sync-client/src/tracing/sync-history.ts | 10 +- .../src/types/document-sync-status.ts | 6 +- .../src/types/document-up-to-dateness.ts | 6 +- .../types/maybe-outdated-client-cursors.ts | 2 +- .../src/types/network-connection-status.ts | 6 +- .../src/utils/assert-set-contains-exactly.ts | 18 +- .../sync-client/src/utils/await-all.test.ts | 68 +- frontend/sync-client/src/utils/await-all.ts | 30 +- .../sync-client/src/utils/create-client-id.ts | 18 +- .../sync-client/src/utils/create-promise.ts | 28 +- .../utils/data-structures/event-listeners.ts | 42 +- .../data-structures/fix-sized-cache.test.ts | 434 +- .../utils/data-structures/fix-sized-cache.ts | 182 +- .../src/utils/data-structures/locks.test.ts | 366 +- .../src/utils/data-structures/locks.ts | 242 +- .../utils/data-structures/min-covered.test.ts | 128 +- .../src/utils/data-structures/min-covered.ts | 72 +- .../src/utils/debugging/log-to-console.ts | 34 +- .../src/utils/debugging/slow-fetch-factory.ts | 28 +- .../debugging/slow-web-socket-factory.ts | 130 +- .../src/utils/find-matching-file.ts | 12 +- .../sync-client/src/utils/get-random-color.ts | 14 +- .../src/utils/globs-to-regexes.test.ts | 10 +- .../sync-client/src/utils/globs-to-regexes.ts | 32 +- frontend/sync-client/src/utils/hash.ts | 14 +- frontend/sync-client/src/utils/is-binary.ts | 22 +- .../src/utils/is-file-type-mergable.test.ts | 126 +- .../src/utils/is-file-type-mergable.ts | 10 +- .../utils/line-and-column-to-position.test.ts | 64 +- .../src/utils/line-and-column-to-position.ts | 32 +- .../utils/position-to-line-and-column.test.ts | 146 +- .../src/utils/position-to-line-and-column.ts | 34 +- .../sync-client/src/utils/rate-limit.test.ts | 86 +- frontend/sync-client/src/utils/rate-limit.ts | 76 +- .../sync-client/src/utils/set-up-telemetry.ts | 60 +- frontend/sync-client/src/utils/sleep.ts | 2 +- frontend/sync-client/tsconfig.json | 34 +- frontend/sync-client/webpack.config.js | 122 +- frontend/test-client/src/agent/mock-agent.ts | 612 +- frontend/test-client/src/agent/mock-client.ts | 330 +- frontend/test-client/src/cli.ts | 290 +- frontend/test-client/src/utils/assert.ts | 6 +- frontend/test-client/src/utils/choose.ts | 2 +- .../src/utils/random-casing.test.ts | 14 +- .../test-client/src/utils/random-casing.ts | 16 +- frontend/test-client/src/utils/sleep.ts | 2 +- .../test-client/src/utils/with-timeout.ts | 26 +- frontend/test-client/tsconfig.json | 32 +- frontend/test-client/webpack.config.js | 50 +- manifest.json | 18 +- scripts/build-sync-server-binaries.sh | 16 +- sync-server/rust-toolchain.toml | 4 +- sync-server/src/app_state/database.rs | 4 +- sync-server/src/config/user_config.rs | 2 +- .../src/server/fetch_document_version.rs | 2 +- .../server/fetch_document_version_content.rs | 2 +- 131 files changed, 16404 insertions(+), 13617 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6f1bff23..c77b091b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,7 +87,7 @@ Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/b ## Testing ### Running Tests -- Server: `cargo test --verbose` +- Server: `cargo test --verbose` - Frontend: `npm run test` (runs Jest across all workspaces) - E2E: `scripts/e2e.sh` @@ -107,4 +107,4 @@ Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/b ### TypeScript - Prettier configuration: 4-space tabs, trailing commas removed, LF line endings - ESLint with unused imports plugin -- Consistent across all three frontend packages \ No newline at end of file +- Consistent across all three frontend packages diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d009127a..6428314e 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,60 +1,60 @@ import { defineConfig } from "vitepress" export default defineConfig({ - title: "VaultLink", - description: "Self-hosted real-time synchronisation for Obsidian", - base: "/vault-link/", - themeConfig: { - logo: "/logo.svg", - nav: [ - { text: "Home", link: "/" }, - { text: "Guide", link: "/guide/getting-started" }, - { text: "Architecture", link: "/architecture/" }, - { text: "GitHub", link: "https://github.com/schmelczer/vault-link" } - ], - sidebar: [ - { - text: "Introduction", - items: [ - { text: "What is VaultLink?", link: "/guide/what-is-vaultlink" }, - { text: "Getting Started", link: "/guide/getting-started" }, - { text: "Limitations", link: "/guide/limitations" }, - { text: "Comparison with Alternatives", link: "/guide/alternatives" } - ] - }, - { - text: "Setup", - items: [ - { text: "Server Setup", link: "/guide/server-setup" }, - { text: "Obsidian Plugin", link: "/guide/obsidian-plugin" }, - { text: "CLI Client", link: "/guide/cli-client" } - ] - }, - { - text: "Configuration", - items: [ - { text: "Server Configuration", link: "/config/server" }, - { text: "Authentication", link: "/config/authentication" }, - { text: "Advanced Options", link: "/config/advanced" } - ] - }, - { - text: "Architecture", - items: [ - { text: "Overview", link: "/architecture/" }, - { text: "Sync Algorithm", link: "/architecture/sync-algorithm" }, - { text: "Data Flow", link: "/architecture/data-flow" } - ] - } - ], - socialLinks: [{ icon: "github", link: "https://github.com/schmelczer/vault-link" }], - footer: { - message: "Released under the MIT License.", - copyright: "Copyright © 2024-present Andras Schmelczer" - }, - search: { - provider: "local" - } - }, - head: [["link", { rel: "icon", type: "image/svg+xml", href: "/vault-link/logo.svg" }]] + title: "VaultLink", + description: "Self-hosted real-time synchronisation for Obsidian", + base: "/vault-link/", + themeConfig: { + logo: "/logo.svg", + nav: [ + { text: "Home", link: "/" }, + { text: "Guide", link: "/guide/getting-started" }, + { text: "Architecture", link: "/architecture/" }, + { text: "GitHub", link: "https://github.com/schmelczer/vault-link" } + ], + sidebar: [ + { + text: "Introduction", + items: [ + { text: "What is VaultLink?", link: "/guide/what-is-vaultlink" }, + { text: "Getting Started", link: "/guide/getting-started" }, + { text: "Limitations", link: "/guide/limitations" }, + { text: "Comparison with Alternatives", link: "/guide/alternatives" } + ] + }, + { + text: "Setup", + items: [ + { text: "Server Setup", link: "/guide/server-setup" }, + { text: "Obsidian Plugin", link: "/guide/obsidian-plugin" }, + { text: "CLI Client", link: "/guide/cli-client" } + ] + }, + { + text: "Configuration", + items: [ + { text: "Server Configuration", link: "/config/server" }, + { text: "Authentication", link: "/config/authentication" }, + { text: "Advanced Options", link: "/config/advanced" } + ] + }, + { + text: "Architecture", + items: [ + { text: "Overview", link: "/architecture/" }, + { text: "Sync Algorithm", link: "/architecture/sync-algorithm" }, + { text: "Data Flow", link: "/architecture/data-flow" } + ] + } + ], + socialLinks: [{ icon: "github", link: "https://github.com/schmelczer/vault-link" }], + footer: { + message: "Released under the MIT License.", + copyright: "Copyright © 2024-present Andras Schmelczer" + }, + search: { + provider: "local" + } + }, + head: [["link", { rel: "icon", type: "image/svg+xml", href: "/vault-link/logo.svg" }]] }) diff --git a/docs/package-lock.json b/docs/package-lock.json index ee287688..dcd4f3b0 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5,332 +5,332 @@ "requires": true, "packages": { "": { - "name": "docs", - "version": "1.0.0", - "license": "ISC", - "devDependencies": { + "name": "docs", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { "@cspell/dict-en-gb": "^5.0.19", "cspell": "^9.3.2", "prettier": "^3.6.2", "vitepress": "^1.6.4", "vue": "^3.5.24" - } + } }, "node_modules/@algolia/abtesting": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.10.0.tgz", - "integrity": "sha512-mQT3jwuTgX8QMoqbIR7mPlWkqQqBPQaPabQzm37xg2txMlaMogK/4hCiiESGdg39MlHZOVHeV+0VJuE7f5UK8A==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.10.0.tgz", + "integrity": "sha512-mQT3jwuTgX8QMoqbIR7mPlWkqQqBPQaPabQzm37xg2txMlaMogK/4hCiiESGdg39MlHZOVHeV+0VJuE7f5UK8A==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", - "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" - } + } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", - "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { + }, + "peerDependencies": { "search-insights": ">= 1 < 3" - } + } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", - "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { + }, + "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" - } + } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", - "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", - "dev": true, - "license": "MIT", - "peerDependencies": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" - } + } }, "node_modules/@algolia/client-abtesting": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.44.0.tgz", - "integrity": "sha512-KY5CcrWhRTUo/lV7KcyjrZkPOOF9bjgWpMj9z98VA+sXzVpZtkuskBLCKsWYFp2sbwchZFTd3wJM48H0IGgF7g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.44.0.tgz", + "integrity": "sha512-KY5CcrWhRTUo/lV7KcyjrZkPOOF9bjgWpMj9z98VA+sXzVpZtkuskBLCKsWYFp2sbwchZFTd3wJM48H0IGgF7g==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-analytics": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.44.0.tgz", - "integrity": "sha512-LKOCE8S4ewI9bN3ot9RZoYASPi8b78E918/DVPW3HHjCMUe6i+NjbNG6KotU4RpP6AhRWZjjswbOkWelUO+OoA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.44.0.tgz", + "integrity": "sha512-LKOCE8S4ewI9bN3ot9RZoYASPi8b78E918/DVPW3HHjCMUe6i+NjbNG6KotU4RpP6AhRWZjjswbOkWelUO+OoA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-common": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.44.0.tgz", - "integrity": "sha512-1yyJm4OYC2cztbS28XYVWwLXdwpLsMG4LoZLOltVglQ2+hc/i9q9fUDZyjRa2Bqt4DmkIfezagfMrokhyH4uxQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.44.0.tgz", + "integrity": "sha512-1yyJm4OYC2cztbS28XYVWwLXdwpLsMG4LoZLOltVglQ2+hc/i9q9fUDZyjRa2Bqt4DmkIfezagfMrokhyH4uxQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-insights": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.44.0.tgz", - "integrity": "sha512-wVQWK6jYYsbEOjIMI+e5voLGPUIbXrvDj392IckXaCPvQ6vCMTXakQqOYCd+znQdL76S+3wHDo77HZWiAYKrtA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.44.0.tgz", + "integrity": "sha512-wVQWK6jYYsbEOjIMI+e5voLGPUIbXrvDj392IckXaCPvQ6vCMTXakQqOYCd+znQdL76S+3wHDo77HZWiAYKrtA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-personalization": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.44.0.tgz", - "integrity": "sha512-lkgRjOjOkqmIkebHjHpU9rLJcJNUDMm+eVSW/KJQYLjGqykEZxal+nYJJTBbLceEU2roByP/+27ZmgIwCdf0iA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.44.0.tgz", + "integrity": "sha512-lkgRjOjOkqmIkebHjHpU9rLJcJNUDMm+eVSW/KJQYLjGqykEZxal+nYJJTBbLceEU2roByP/+27ZmgIwCdf0iA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.44.0.tgz", - "integrity": "sha512-sYfhgwKu6NDVmZHL1WEKVLsOx/jUXCY4BHKLUOcYa8k4COCs6USGgz6IjFkUf+niwq8NCECMmTC4o/fVQOalsA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.44.0.tgz", + "integrity": "sha512-sYfhgwKu6NDVmZHL1WEKVLsOx/jUXCY4BHKLUOcYa8k4COCs6USGgz6IjFkUf+niwq8NCECMmTC4o/fVQOalsA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/client-search": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.44.0.tgz", - "integrity": "sha512-/FRKUM1G4xn3vV8+9xH1WJ9XknU8rkBGlefruq9jDhYUAvYozKimhrmC2pRqw/RyHhPivmgZCRuC8jHP8piz4Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.44.0.tgz", + "integrity": "sha512-/FRKUM1G4xn3vV8+9xH1WJ9XknU8rkBGlefruq9jDhYUAvYozKimhrmC2pRqw/RyHhPivmgZCRuC8jHP8piz4Q==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/ingestion": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.44.0.tgz", - "integrity": "sha512-5+S5ynwMmpTpCLXGjTDpeIa81J+R4BLH0lAojOhmeGSeGEHQTqacl/4sbPyDTcidvnWhaqtyf8m42ue6lvISAw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.44.0.tgz", + "integrity": "sha512-5+S5ynwMmpTpCLXGjTDpeIa81J+R4BLH0lAojOhmeGSeGEHQTqacl/4sbPyDTcidvnWhaqtyf8m42ue6lvISAw==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/monitoring": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.44.0.tgz", - "integrity": "sha512-xhaTN8pXJjR6zkrecg4Cc9YZaQK2LKm2R+LkbAq+AYGBCWJxtSGlNwftozZzkUyq4AXWoyoc0x2SyBtq5LRtqQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.44.0.tgz", + "integrity": "sha512-xhaTN8pXJjR6zkrecg4Cc9YZaQK2LKm2R+LkbAq+AYGBCWJxtSGlNwftozZzkUyq4AXWoyoc0x2SyBtq5LRtqQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/recommend": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.44.0.tgz", - "integrity": "sha512-GNcite/uOIS7wgRU1MT7SdNIupGSW+vbK9igIzMePvD2Dl8dy0O3urKPKIbTuZQqiVH1Cb84y5cgLvwNrdCj/Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.44.0.tgz", + "integrity": "sha512-GNcite/uOIS7wgRU1MT7SdNIupGSW+vbK9igIzMePvD2Dl8dy0O3urKPKIbTuZQqiVH1Cb84y5cgLvwNrdCj/Q==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0", "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.44.0.tgz", - "integrity": "sha512-YZHBk72Cd7pcuNHzbhNzF/FbbYszlc7JhZlDyQAchnX5S7tcemSS96F39Sy8t4O4WQLpFvUf1MTNedlitWdOsQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.44.0.tgz", + "integrity": "sha512-YZHBk72Cd7pcuNHzbhNzF/FbbYszlc7JhZlDyQAchnX5S7tcemSS96F39Sy8t4O4WQLpFvUf1MTNedlitWdOsQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/requester-fetch": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.44.0.tgz", - "integrity": "sha512-B9WHl+wQ7uf46t9cq+vVM/ypVbOeuldVDq9OtKsX2ApL2g/htx6ImB9ugDOOJmB5+fE31/XPTuCcYz/j03+idA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.44.0.tgz", + "integrity": "sha512-B9WHl+wQ7uf46t9cq+vVM/ypVbOeuldVDq9OtKsX2ApL2g/htx6ImB9ugDOOJmB5+fE31/XPTuCcYz/j03+idA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/@algolia/requester-node-http": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.44.0.tgz", - "integrity": "sha512-MULm0qeAIk4cdzZ/ehJnl1o7uB5NMokg83/3MKhPq0Pk7+I0uELGNbzIfAkvkKKEYcHALemKdArtySF9eKzh/A==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.44.0.tgz", + "integrity": "sha512-MULm0qeAIk4cdzZ/ehJnl1o7uB5NMokg83/3MKhPq0Pk7+I0uELGNbzIfAkvkKKEYcHALemKdArtySF9eKzh/A==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/client-common": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.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==", - "dev": true, - "license": "MIT", - "engines": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6.9.0" - } + } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6.9.0" - } + } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/types": "^7.28.5" - }, - "bin": { + }, + "bin": { "parser": "bin/babel-parser.js" - }, - "engines": { + }, + "engines": { "node": ">=6.0.0" - } + } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { + }, + "engines": { "node": ">=6.9.0" - } + } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.3.2.tgz", - "integrity": "sha512-OmKzq/0FATHU671GKMzBrTyLdm25Wnziva7h4ylumVn1wnwWsXGef5bgXD7iuApqfqH9SzxsU0NtTB8m8vwEHQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.3.2.tgz", + "integrity": "sha512-OmKzq/0FATHU671GKMzBrTyLdm25Wnziva7h4ylumVn1wnwWsXGef5bgXD7iuApqfqH9SzxsU0NtTB8m8vwEHQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/dict-ada": "^4.1.1", "@cspell/dict-al": "^1.1.1", "@cspell/dict-aws": "^4.0.16", @@ -390,869 +390,869 @@ "@cspell/dict-typescript": "^3.2.3", "@cspell/dict-vue": "^3.0.5", "@cspell/dict-zig": "^1.0.0" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.3.2.tgz", - "integrity": "sha512-YRgpeHN9uY8kUlIw9q+8zJ0tRTAJMbfBTGzCq9Puah09NeMWlRMFPUkXVrkdic6NA7etboZ+zEdoZwRO9EmhiA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.3.2.tgz", + "integrity": "sha512-YRgpeHN9uY8kUlIw9q+8zJ0tRTAJMbfBTGzCq9Puah09NeMWlRMFPUkXVrkdic6NA7etboZ+zEdoZwRO9EmhiA==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-types": "9.3.2" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/cspell-pipe": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.3.2.tgz", - "integrity": "sha512-REF7ibG79WLEynIMUss/IRDCdYEb1nlE1rj/gt2CbPFzLa6t5MRwW2lajEvXS6/WgbMtsTVHAWi3ALqJzCwxng==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.3.2.tgz", + "integrity": "sha512-REF7ibG79WLEynIMUss/IRDCdYEb1nlE1rj/gt2CbPFzLa6t5MRwW2lajEvXS6/WgbMtsTVHAWi3ALqJzCwxng==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/cspell-resolver": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.3.2.tgz", - "integrity": "sha512-jLN2Aa/vxm8+IBvTd884SwPEfjxnDwIEPBT3hmqgLlKuUHQ3FMG27lsM4Ik9L2KWBXMgV/wGz4BaxfhKI41Ttw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.3.2.tgz", + "integrity": "sha512-jLN2Aa/vxm8+IBvTd884SwPEfjxnDwIEPBT3hmqgLlKuUHQ3FMG27lsM4Ik9L2KWBXMgV/wGz4BaxfhKI41Ttw==", + "dev": true, + "license": "MIT", + "dependencies": { "global-directory": "^4.0.1" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/cspell-service-bus": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.3.2.tgz", - "integrity": "sha512-/rB8LazM0JzKL+AvZa5fEpLutmwy5QFMpzw8HJd+rDGkzb5r79hURWSRo84QArgaskUqA9XlOHSieDE9pt+WAA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.3.2.tgz", + "integrity": "sha512-/rB8LazM0JzKL+AvZa5fEpLutmwy5QFMpzw8HJd+rDGkzb5r79hURWSRo84QArgaskUqA9XlOHSieDE9pt+WAA==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/cspell-types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.3.2.tgz", - "integrity": "sha512-l4H8bMAmdzCbXHO8y1JZiAKszrPEiuLFKWrbhCacHF0iP+PIc/yuQp7cO70m0p70vArRfih6kgGyHFaCy47CfA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.3.2.tgz", + "integrity": "sha512-l4H8bMAmdzCbXHO8y1JZiAKszrPEiuLFKWrbhCacHF0iP+PIc/yuQp7cO70m0p70vArRfih6kgGyHFaCy47CfA==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/dict-ada": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.1.tgz", - "integrity": "sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==", - "dev": true, - "license": "MIT" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.1.tgz", + "integrity": "sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-al": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.1.tgz", - "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.1.tgz", + "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-aws": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.16.tgz", - "integrity": "sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==", - "dev": true, - "license": "MIT" + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.16.tgz", + "integrity": "sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-bash": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.2.tgz", - "integrity": "sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.2.tgz", + "integrity": "sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/dict-shell": "1.1.2" - } + } }, "node_modules/@cspell/dict-companies": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.7.tgz", - "integrity": "sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==", - "dev": true, - "license": "MIT" + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.7.tgz", + "integrity": "sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-cpp": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.14.tgz", - "integrity": "sha512-dkmpSwvVfVdtoZ4mW/CK2Ep1v8mJlp6uiKpMNbSMOdJl4kq28nQS4vKNIX3B2bJa0Ha5iHHu+1mNjiLeO3g7Xg==", - "dev": true, - "license": "MIT" + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.14.tgz", + "integrity": "sha512-dkmpSwvVfVdtoZ4mW/CK2Ep1v8mJlp6uiKpMNbSMOdJl4kq28nQS4vKNIX3B2bJa0Ha5iHHu+1mNjiLeO3g7Xg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-cryptocurrencies": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.5.tgz", - "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==", - "dev": true, - "license": "MIT" + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.5.tgz", + "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-csharp": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.7.tgz", - "integrity": "sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==", - "dev": true, - "license": "MIT" + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.7.tgz", + "integrity": "sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-css": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.18.tgz", - "integrity": "sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==", - "dev": true, - "license": "MIT" + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.18.tgz", + "integrity": "sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-dart": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.1.tgz", - "integrity": "sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==", - "dev": true, - "license": "MIT" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.1.tgz", + "integrity": "sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-data-science": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.12.tgz", - "integrity": "sha512-vI/mg6cI28IkFcpeINS7cm5M9HWemmXSTnxJiu3nmc4VAGx35SXIEyuLGBcsVzySvDablFYf4hsEpmg1XpVsUQ==", - "dev": true, - "license": "MIT" + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.12.tgz", + "integrity": "sha512-vI/mg6cI28IkFcpeINS7cm5M9HWemmXSTnxJiu3nmc4VAGx35SXIEyuLGBcsVzySvDablFYf4hsEpmg1XpVsUQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-django": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.5.tgz", - "integrity": "sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==", - "dev": true, - "license": "MIT" + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.5.tgz", + "integrity": "sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-docker": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.16.tgz", - "integrity": "sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==", - "dev": true, - "license": "MIT" + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.16.tgz", + "integrity": "sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-dotnet": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.10.tgz", - "integrity": "sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==", - "dev": true, - "license": "MIT" + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.10.tgz", + "integrity": "sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-elixir": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.8.tgz", - "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==", - "dev": true, - "license": "MIT" + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.8.tgz", + "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-en_us": { - "version": "4.4.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.24.tgz", - "integrity": "sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==", - "dev": true, - "license": "MIT" + "version": "4.4.24", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.24.tgz", + "integrity": "sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-en-common-misspellings": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.8.tgz", - "integrity": "sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==", - "dev": true, - "license": "CC BY-SA 4.0" + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.8.tgz", + "integrity": "sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==", + "dev": true, + "license": "CC BY-SA 4.0" }, "node_modules/@cspell/dict-en-gb": { - "version": "5.0.19", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-5.0.19.tgz", - "integrity": "sha512-/p+p/9q8XTzsE0GxbZZKcC1rTLYmCpilYw8aC9Q1xJbve8YqZnpxk8IxRyaHwfy1TeKMQNs6heZZRtzPag0rCw==", - "dev": true, - "license": "LGPL-3.0" + "version": "5.0.19", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-5.0.19.tgz", + "integrity": "sha512-/p+p/9q8XTzsE0GxbZZKcC1rTLYmCpilYw8aC9Q1xJbve8YqZnpxk8IxRyaHwfy1TeKMQNs6heZZRtzPag0rCw==", + "dev": true, + "license": "LGPL-3.0" }, "node_modules/@cspell/dict-en-gb-mit": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.14.tgz", - "integrity": "sha512-b+vEerlHP6rnNf30tmTJb7JZnOq4WAslYUvexOz/L3gDna9YJN3bAnwRJ3At3bdcOcMG7PTv3Pi+C73IR22lNg==", - "dev": true, - "license": "MIT" + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.14.tgz", + "integrity": "sha512-b+vEerlHP6rnNf30tmTJb7JZnOq4WAslYUvexOz/L3gDna9YJN3bAnwRJ3At3bdcOcMG7PTv3Pi+C73IR22lNg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-filetypes": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.14.tgz", - "integrity": "sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==", - "dev": true, - "license": "MIT" + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.14.tgz", + "integrity": "sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-flutter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.1.tgz", - "integrity": "sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.1.tgz", + "integrity": "sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-fonts": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.5.tgz", - "integrity": "sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==", - "dev": true, - "license": "MIT" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.5.tgz", + "integrity": "sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-fsharp": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.1.tgz", - "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.1.tgz", + "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-fullstack": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz", - "integrity": "sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==", - "dev": true, - "license": "MIT" + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz", + "integrity": "sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-gaming-terms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.2.tgz", - "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==", - "dev": true, - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.2.tgz", + "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-git": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.7.tgz", - "integrity": "sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==", - "dev": true, - "license": "MIT" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.7.tgz", + "integrity": "sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-golang": { - "version": "6.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.24.tgz", - "integrity": "sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==", - "dev": true, - "license": "MIT" + "version": "6.0.24", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.24.tgz", + "integrity": "sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-google": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.9.tgz", - "integrity": "sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==", - "dev": true, - "license": "MIT" + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.9.tgz", + "integrity": "sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-haskell": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.6.tgz", - "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==", - "dev": true, - "license": "MIT" + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.6.tgz", + "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-html": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.12.tgz", - "integrity": "sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==", - "dev": true, - "license": "MIT" + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.12.tgz", + "integrity": "sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-html-symbol-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz", - "integrity": "sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==", - "dev": true, - "license": "MIT" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz", + "integrity": "sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-java": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.12.tgz", - "integrity": "sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==", - "dev": true, - "license": "MIT" + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.12.tgz", + "integrity": "sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-julia": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.1.tgz", - "integrity": "sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.1.tgz", + "integrity": "sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-k8s": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.12.tgz", - "integrity": "sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==", - "dev": true, - "license": "MIT" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.12.tgz", + "integrity": "sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-kotlin": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.1.tgz", - "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.1.tgz", + "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-latex": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.4.tgz", - "integrity": "sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==", - "dev": true, - "license": "MIT" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.4.tgz", + "integrity": "sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-lorem-ipsum": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.5.tgz", - "integrity": "sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==", - "dev": true, - "license": "MIT" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.5.tgz", + "integrity": "sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-lua": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.8.tgz", - "integrity": "sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==", - "dev": true, - "license": "MIT" + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.8.tgz", + "integrity": "sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-makefile": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.5.tgz", - "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==", - "dev": true, - "license": "MIT" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.5.tgz", + "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-markdown": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.12.tgz", - "integrity": "sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==", - "dev": true, - "license": "MIT", - "peerDependencies": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.12.tgz", + "integrity": "sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==", + "dev": true, + "license": "MIT", + "peerDependencies": { "@cspell/dict-css": "^4.0.18", "@cspell/dict-html": "^4.0.12", "@cspell/dict-html-symbol-entities": "^4.0.4", "@cspell/dict-typescript": "^3.2.3" - } + } }, "node_modules/@cspell/dict-monkeyc": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.11.tgz", - "integrity": "sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==", - "dev": true, - "license": "MIT" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.11.tgz", + "integrity": "sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-node": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.8.tgz", - "integrity": "sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==", - "dev": true, - "license": "MIT" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.8.tgz", + "integrity": "sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-npm": { - "version": "5.2.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.23.tgz", - "integrity": "sha512-cnlPGzhNkbXFLFURfjzwML2LjHMofqJkemR7lLo9Jwa9IptvzeTn4nOtJMSGfkxNrZPf/IvQ7rH5hamsUQLQ3A==", - "dev": true, - "license": "MIT" + "version": "5.2.23", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.23.tgz", + "integrity": "sha512-cnlPGzhNkbXFLFURfjzwML2LjHMofqJkemR7lLo9Jwa9IptvzeTn4nOtJMSGfkxNrZPf/IvQ7rH5hamsUQLQ3A==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-php": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.0.tgz", - "integrity": "sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==", - "dev": true, - "license": "MIT" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.0.tgz", + "integrity": "sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-powershell": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.15.tgz", - "integrity": "sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==", - "dev": true, - "license": "MIT" + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.15.tgz", + "integrity": "sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.15.tgz", - "integrity": "sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==", - "dev": true, - "license": "MIT" + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.15.tgz", + "integrity": "sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-python": { - "version": "4.2.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.22.tgz", - "integrity": "sha512-rgF7DuleVK2lkzlw33jjEfxS2a0CU5kwAhOqf5B6XkuaPbqZ/0g0LBCdwglAGccYu7sBuvxRS8Yubk+ytSAFTg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.2.22", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.22.tgz", + "integrity": "sha512-rgF7DuleVK2lkzlw33jjEfxS2a0CU5kwAhOqf5B6XkuaPbqZ/0g0LBCdwglAGccYu7sBuvxRS8Yubk+ytSAFTg==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/dict-data-science": "^2.0.12" - } + } }, "node_modules/@cspell/dict-r": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.1.tgz", - "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==", - "dev": true, - "license": "MIT" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.1.tgz", + "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.9.tgz", - "integrity": "sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==", - "dev": true, - "license": "MIT" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.9.tgz", + "integrity": "sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-rust": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.12.tgz", - "integrity": "sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==", - "dev": true, - "license": "MIT" + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.12.tgz", + "integrity": "sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-scala": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.8.tgz", - "integrity": "sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==", - "dev": true, - "license": "MIT" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.8.tgz", + "integrity": "sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-shell": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.2.tgz", - "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==", - "dev": true, - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.2.tgz", + "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-software-terms": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.14.tgz", - "integrity": "sha512-Eu9h090hxHJiqzVFS0WxOZbYXnmb7F1RFIUEg4Nru+D/78bXVDH4b8BiKGVFNRljaieNQRAHaryzdaKJRCH6ZA==", - "dev": true, - "license": "MIT" + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.14.tgz", + "integrity": "sha512-Eu9h090hxHJiqzVFS0WxOZbYXnmb7F1RFIUEg4Nru+D/78bXVDH4b8BiKGVFNRljaieNQRAHaryzdaKJRCH6ZA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-sql": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.1.tgz", - "integrity": "sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==", - "dev": true, - "license": "MIT" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.1.tgz", + "integrity": "sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-svelte": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.7.tgz", - "integrity": "sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==", - "dev": true, - "license": "MIT" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.7.tgz", + "integrity": "sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-swift": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.6.tgz", - "integrity": "sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==", - "dev": true, - "license": "MIT" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.6.tgz", + "integrity": "sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-terraform": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.3.tgz", - "integrity": "sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==", - "dev": true, - "license": "MIT" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.3.tgz", + "integrity": "sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-typescript": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", - "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", - "dev": true, - "license": "MIT" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", + "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-vue": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.5.tgz", - "integrity": "sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==", - "dev": true, - "license": "MIT" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.5.tgz", + "integrity": "sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dict-zig": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-zig/-/dict-zig-1.0.0.tgz", - "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==", - "dev": true, - "license": "MIT" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-zig/-/dict-zig-1.0.0.tgz", + "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cspell/dynamic-import": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.3.2.tgz", - "integrity": "sha512-au7FyuIHUNI2r9sO3pUBKVTeD/v7c9x/nPUStaAK1bG4rdKt4w+/jUY2IaldAraW5w29z528BboXbiV87SM1kw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.3.2.tgz", + "integrity": "sha512-au7FyuIHUNI2r9sO3pUBKVTeD/v7c9x/nPUStaAK1bG4rdKt4w+/jUY2IaldAraW5w29z528BboXbiV87SM1kw==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/url": "9.3.2", "import-meta-resolve": "^4.2.0" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/filetypes": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.3.2.tgz", - "integrity": "sha512-0bUxQlmJPRHZrRQD7adbc4lFizO8tGD/6+1cBgU3kV3+NVrpr12y4jU8twCSChhYibZyPr7bnvhkM3cQgb8RzA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.3.2.tgz", + "integrity": "sha512-0bUxQlmJPRHZrRQD7adbc4lFizO8tGD/6+1cBgU3kV3+NVrpr12y4jU8twCSChhYibZyPr7bnvhkM3cQgb8RzA==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/strong-weak-map": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.3.2.tgz", - "integrity": "sha512-pFcmOTWCoFMRETb9PCkCmaiZiLb5i2qOZmGH/p/tFEH8kIYhMGfhaulnXwKwS+Ke6PKceQd2YL98bGmo8hL4aQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.3.2.tgz", + "integrity": "sha512-pFcmOTWCoFMRETb9PCkCmaiZiLb5i2qOZmGH/p/tFEH8kIYhMGfhaulnXwKwS+Ke6PKceQd2YL98bGmo8hL4aQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@cspell/url": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.3.2.tgz", - "integrity": "sha512-TobUlZl7Z7VehhNOMNAg1ABuGizieseftlG94OZJ934JptOhK8TC/1o2ldKrbDH50jyt6E7rPTMV2BW/vWuTzQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.3.2.tgz", + "integrity": "sha512-TobUlZl7Z7VehhNOMNAg1ABuGizieseftlG94OZJ934JptOhK8TC/1o2ldKrbDH50jyt6E7rPTMV2BW/vWuTzQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/@docsearch/css": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", - "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", - "dev": true, - "license": "MIT" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" }, "node_modules/@docsearch/js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", - "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" - } + } }, "node_modules/@docsearch/react": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", - "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" - }, - "peerDependencies": { + }, + "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "@types/react": { - "optional": true + "optional": true }, "react": { - "optional": true + "optional": true }, "react-dom": { - "optional": true + "optional": true }, "search-insights": { - "optional": true + "optional": true } - } + } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ "linux" - ], - "engines": { + ], + "engines": { "node": ">=12" - } + } }, "node_modules/@iconify-json/simple-icons": { - "version": "1.2.59", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.59.tgz", - "integrity": "sha512-fYx/InyQsWFW4wVxWka3CGDJ6m/fXoTqWBSl+oA3FBXO5RhPAb6S3Y5bRgCPnrYevErH8VjAL0TZevIqlN2PhQ==", - "dev": true, - "license": "CC0-1.0", - "dependencies": { + "version": "1.2.59", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.59.tgz", + "integrity": "sha512-fYx/InyQsWFW4wVxWka3CGDJ6m/fXoTqWBSl+oA3FBXO5RhPAb6S3Y5bRgCPnrYevErH8VjAL0TZevIqlN2PhQ==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { "@iconify/types": "*" - } + } }, "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "dev": true, - "license": "MIT" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", - "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", - "cpu": [ + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ "linux" - ] + ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", - "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", - "cpu": [ + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ "linux" - ] + ] }, "node_modules/@shikijs/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", - "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" - } + } }, "node_modules/@shikijs/engine-javascript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", - "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" - } + } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", - "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" - } + } }, "node_modules/@shikijs/langs": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", - "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/types": "2.5.0" - } + } }, "node_modules/@shikijs/themes": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", - "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/types": "2.5.0" - } + } }, "node_modules/@shikijs/transformers": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", - "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" - } + } }, "node_modules/@shikijs/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", - "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" - } + } }, "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "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" + "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/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "*" - } + } }, "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" - } + } }, "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "*" - } + } }, "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/web-bluetooth": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", - "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", - "dev": true, - "license": "MIT" + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { + }, + "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" - } + } }, "node_modules/@vue/compiler-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", - "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.24", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" - } + } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", - "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/compiler-core": "3.5.24", "@vue/shared": "3.5.24" - } + } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", - "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "dev": true, + "license": "MIT", + "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.24", "@vue/compiler-dom": "3.5.24", @@ -1262,36 +1262,36 @@ "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" - } + } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", - "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/shared": "3.5.24" - } + } }, "node_modules/@vue/devtools-api": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", - "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/devtools-kit": "^7.7.9" - } + } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", - "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", @@ -1299,104 +1299,104 @@ "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" - } + } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", - "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dev": true, + "license": "MIT", + "dependencies": { "rfdc": "^1.4.1" - } + } }, "node_modules/@vue/reactivity": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", - "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", + "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/shared": "3.5.24" - } + } }, "node_modules/@vue/runtime-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", - "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", + "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/reactivity": "3.5.24", "@vue/shared": "3.5.24" - } + } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", - "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", + "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/reactivity": "3.5.24", "@vue/runtime-core": "3.5.24", "@vue/shared": "3.5.24", "csstype": "^3.1.3" - } + } }, "node_modules/@vue/server-renderer": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", - "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", + "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/compiler-ssr": "3.5.24", "@vue/shared": "3.5.24" - }, - "peerDependencies": { + }, + "peerDependencies": { "vue": "3.5.24" - } + } }, "node_modules/@vue/shared": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", - "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", - "dev": true, - "license": "MIT" + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "dev": true, + "license": "MIT" }, "node_modules/@vueuse/core": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", - "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/antfu" - } + } }, "node_modules/@vueuse/integrations": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", - "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { + }, + "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", @@ -1409,76 +1409,76 @@ "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "async-validator": { - "optional": true + "optional": true }, "axios": { - "optional": true + "optional": true }, "change-case": { - "optional": true + "optional": true }, "drauu": { - "optional": true + "optional": true }, "focus-trap": { - "optional": true + "optional": true }, "fuse.js": { - "optional": true + "optional": true }, "idb-keyval": { - "optional": true + "optional": true }, "jwt-decode": { - "optional": true + "optional": true }, "nprogress": { - "optional": true + "optional": true }, "qrcode": { - "optional": true + "optional": true }, "sortablejs": { - "optional": true + "optional": true }, "universal-cookie": { - "optional": true + "optional": true } - } + } }, "node_modules/@vueuse/metadata": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", - "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", - "dev": true, - "license": "MIT", - "funding": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { "url": "https://github.com/sponsors/antfu" - } + } }, "node_modules/@vueuse/shared": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", - "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { "vue": "^3.5.13" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/antfu" - } + } }, "node_modules/algoliasearch": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.44.0.tgz", - "integrity": "sha512-f8IpsbdQjzTjr/4mJ/jv5UplrtyMnnciGax6/B0OnLCs2/GJTK13O4Y7Ff1AvJVAaztanH+m5nzPoUq6EAy+aA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.44.0.tgz", + "integrity": "sha512-f8IpsbdQjzTjr/4mJ/jv5UplrtyMnnciGax6/B0OnLCs2/GJTK13O4Y7Ff1AvJVAaztanH+m5nzPoUq6EAy+aA==", + "dev": true, + "license": "MIT", + "dependencies": { "@algolia/abtesting": "1.10.0", "@algolia/client-abtesting": "5.44.0", "@algolia/client-analytics": "5.44.0", @@ -1493,183 +1493,183 @@ "@algolia/requester-browser-xhr": "5.44.0", "@algolia/requester-fetch": "5.44.0", "@algolia/requester-node-http": "5.44.0" - }, - "engines": { + }, + "engines": { "node": ">= 14.0.0" - } + } }, "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true, - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" }, "node_modules/birpc": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz", - "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz", + "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", + "dev": true, + "license": "MIT", + "funding": { "url": "https://github.com/sponsors/antfu" - } + } }, "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6" - } + } }, "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { + }, + "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" - } + } }, "node_modules/chalk-template": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.2.tgz", - "integrity": "sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.2.tgz", + "integrity": "sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==", + "dev": true, + "license": "MIT", + "dependencies": { "chalk": "^5.2.0" - }, - "engines": { + }, + "engines": { "node": ">=14.16" - }, - "funding": { + }, + "funding": { "url": "https://github.com/chalk/chalk-template?sponsor=1" - } + } }, "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/clear-module": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", - "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", + "dev": true, + "license": "MIT", + "dependencies": { "parent-module": "^2.0.0", "resolve-from": "^5.0.0" - }, - "engines": { + }, + "engines": { "node": ">=8" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/sindresorhus" - } + } }, "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/comment-json": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", - "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "license": "MIT", + "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", "esprima": "^4.0.1" - }, - "engines": { + }, + "engines": { "node": ">= 6" - } + } }, "node_modules/copy-anything": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", - "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { "is-what": "^5.2.0" - }, - "engines": { + }, + "engines": { "node": ">=18" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/mesqueeb" - } + } }, "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" }, "node_modules/cspell": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.3.2.tgz", - "integrity": "sha512-3xFyVSTYrYa/QJzLfzsCRMkMXqOsytP8E26DuGrVMJQoLPFmbOXNNtnMu4wrtr17QVloxpvutW77U4vb2L/LDQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.3.2.tgz", + "integrity": "sha512-3xFyVSTYrYa/QJzLfzsCRMkMXqOsytP8E26DuGrVMJQoLPFmbOXNNtnMu4wrtr17QVloxpvutW77U4vb2L/LDQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-json-reporter": "9.3.2", "@cspell/cspell-pipe": "9.3.2", "@cspell/cspell-types": "9.3.2", @@ -1688,120 +1688,120 @@ "flatted": "^3.3.3", "semver": "^7.7.3", "tinyglobby": "^0.2.15" - }, - "bin": { + }, + "bin": { "cspell": "bin.mjs", "cspell-esm": "bin.mjs" - }, - "engines": { + }, + "engines": { "node": ">=20" - }, - "funding": { + }, + "funding": { "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" - } + } }, "node_modules/cspell-config-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.3.2.tgz", - "integrity": "sha512-zXhmA4rqgWQRTVijI+g/mgiep76TvTO4d+P3CHwcqLG57BKVzoW+jkO4qDLC+Neh4b8+CcNWEIr3w16BfuEJAA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.3.2.tgz", + "integrity": "sha512-zXhmA4rqgWQRTVijI+g/mgiep76TvTO4d+P3CHwcqLG57BKVzoW+jkO4qDLC+Neh4b8+CcNWEIr3w16BfuEJAA==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-types": "9.3.2", "comment-json": "^4.4.1", "smol-toml": "^1.5.2", "yaml": "^2.8.1" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-dictionary": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.3.2.tgz", - "integrity": "sha512-E3YhOhZzZt1a+AEbFV2B3THCyZ576PDg0mDNUDrU1Y65SyIhf4DC6itfPoAb6R3FI/DI218RqWZg/FTT8lJ2gA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.3.2.tgz", + "integrity": "sha512-E3YhOhZzZt1a+AEbFV2B3THCyZ576PDg0mDNUDrU1Y65SyIhf4DC6itfPoAb6R3FI/DI218RqWZg/FTT8lJ2gA==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-pipe": "9.3.2", "@cspell/cspell-types": "9.3.2", "cspell-trie-lib": "9.3.2", "fast-equals": "^5.3.3" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-gitignore": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.3.2.tgz", - "integrity": "sha512-G2bLR+Dfb9GX4Sdm75GfCCa9V/sQYkRbLckuCuVmJxvcDB0xfczAtb6TfAXIziF3oUI6cOB1g+PoNLWBelcK5w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.3.2.tgz", + "integrity": "sha512-G2bLR+Dfb9GX4Sdm75GfCCa9V/sQYkRbLckuCuVmJxvcDB0xfczAtb6TfAXIziF3oUI6cOB1g+PoNLWBelcK5w==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/url": "9.3.2", "cspell-glob": "9.3.2", "cspell-io": "9.3.2" - }, - "bin": { + }, + "bin": { "cspell-gitignore": "bin.mjs" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-glob": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.3.2.tgz", - "integrity": "sha512-TuSupENEKyOCupOUZ3vnPxaTOghxY/rD1JIkb8e5kjzRprYVilO/rYqEk/52iLwJVd+4Npe8fNhR3KhU7u/UUg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.3.2.tgz", + "integrity": "sha512-TuSupENEKyOCupOUZ3vnPxaTOghxY/rD1JIkb8e5kjzRprYVilO/rYqEk/52iLwJVd+4Npe8fNhR3KhU7u/UUg==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/url": "9.3.2", "picomatch": "^4.0.3" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-grammar": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.3.2.tgz", - "integrity": "sha512-ysonrFu9vJvF/derDlEjUfmvLeCfNOWPh00t6Yh093AKrJFoWQiyaS/5bEN/uB5/n1sa4k3ItnWvuTp3+YuZsA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.3.2.tgz", + "integrity": "sha512-ysonrFu9vJvF/derDlEjUfmvLeCfNOWPh00t6Yh093AKrJFoWQiyaS/5bEN/uB5/n1sa4k3ItnWvuTp3+YuZsA==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-pipe": "9.3.2", "@cspell/cspell-types": "9.3.2" - }, - "bin": { + }, + "bin": { "cspell-grammar": "bin.mjs" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-io": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.3.2.tgz", - "integrity": "sha512-ahoULCp0j12TyXXmIcdO/7x65A/2mzUQO1IkOC65OXEbNT+evt0yswSO5Nr1F6kCHDuEKc46EZWwsYAzj78pMg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.3.2.tgz", + "integrity": "sha512-ahoULCp0j12TyXXmIcdO/7x65A/2mzUQO1IkOC65OXEbNT+evt0yswSO5Nr1F6kCHDuEKc46EZWwsYAzj78pMg==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-service-bus": "9.3.2", "@cspell/url": "9.3.2" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.3.2.tgz", - "integrity": "sha512-kdk11kib68zNANNICuOA8h4oA9kENQUAdeX/uvT4+7eHbHHV8WSgjXm4k4o/pRIbg164UJTX/XxKb/65ftn5jw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.3.2.tgz", + "integrity": "sha512-kdk11kib68zNANNICuOA8h4oA9kENQUAdeX/uvT4+7eHbHHV8WSgjXm4k4o/pRIbg164UJTX/XxKb/65ftn5jw==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-bundled-dicts": "9.3.2", "@cspell/cspell-pipe": "9.3.2", "@cspell/cspell-resolver": "9.3.2", @@ -1824,104 +1824,104 @@ "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.1.0", "xdg-basedir": "^5.1.0" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/cspell-trie-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.3.2.tgz", - "integrity": "sha512-1Af7Mq9jIccFQyJl/ZCcqQbtJwuDqpQVkk8xfs/92x4OI6gW1iTVRMtsrh0RTw1HZoR8aQD7tRRCiLPf/D+UiQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.3.2.tgz", + "integrity": "sha512-1Af7Mq9jIccFQyJl/ZCcqQbtJwuDqpQVkk8xfs/92x4OI6gW1iTVRMtsrh0RTw1HZoR8aQD7tRRCiLPf/D+UiQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@cspell/cspell-pipe": "9.3.2", "@cspell/cspell-types": "9.3.2", "gensequence": "^8.0.8" - }, - "engines": { + }, + "engines": { "node": ">=20" - } + } }, "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" }, "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6" - } + } }, "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { "dequal": "^2.0.0" - }, - "funding": { + }, + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/emoji-regex-xs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", - "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true, - "license": "MIT" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" }, "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": { + "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": { + }, + "funding": { "url": "https://github.com/fb55/entities?sponsor=1" - } + } }, "node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "dev": true, - "license": "MIT", - "engines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/sindresorhus" - } + } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { "esbuild": "bin/esbuild" - }, - "engines": { + }, + "engines": { "node": ">=12" - }, - "optionalDependencies": { + }, + "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", @@ -1945,129 +1945,129 @@ "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" - } + } }, "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" - }, - "engines": { + }, + "engines": { "node": ">=4" - } + } }, "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" }, "node_modules/fast-equals": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz", - "integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz", + "integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=6.0.0" - } + } }, "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" + "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/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=12.0.0" - }, - "peerDependencies": { + }, + "peerDependencies": { "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "picomatch": { - "optional": true + "optional": true } - } + } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/focus-trap": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.6.tgz", - "integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.6.tgz", + "integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==", + "dev": true, + "license": "MIT", + "dependencies": { "tabbable": "^6.3.0" - } + } }, "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": [ + "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": { + ], + "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } + } }, "node_modules/gensequence": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-8.0.8.tgz", - "integrity": "sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==", - "dev": true, - "license": "MIT", - "engines": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-8.0.8.tgz", + "integrity": "sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=20" - } + } }, "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { "ini": "4.1.1" - }, - "engines": { + }, + "engines": { "node": ">=18" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/sindresorhus" - } + } }, "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", @@ -2079,142 +2079,142 @@ "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true, - "license": "MIT" + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" }, "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "engines": { + }, + "engines": { "node": ">=6" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/sindresorhus" - } + } }, "node_modules/import-fresh/node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { "callsites": "^3.0.0" - }, - "engines": { + }, + "engines": { "node": ">=6" - } + } }, "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=4" - } + } }, "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true, - "license": "ISC", - "engines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + } }, "node_modules/is-what": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", - "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=18" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/mesqueeb" - } + } }, "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" - } + } }, "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "dev": true, - "license": "MIT" + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" }, "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", @@ -2224,319 +2224,319 @@ "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ], - "license": "MIT", - "dependencies": { + ], + "license": "MIT", + "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" - } + } }, "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ], + "license": "MIT" }, "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ], - "license": "MIT", - "dependencies": { + ], + "license": "MIT", + "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" - } + } }, "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ], + "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "dev": true, - "funding": [ + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ], - "license": "MIT" + ], + "license": "MIT" }, "node_modules/minisearch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", - "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", - "dev": true, - "license": "MIT" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" }, "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ + "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" + "type": "github", + "url": "https://github.com/sponsors/ai" } - ], - "license": "MIT", - "bin": { + ], + "license": "MIT", + "bin": { "nanoid": "bin/nanoid.cjs" - }, - "engines": { + }, + "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + } }, "node_modules/oniguruma-to-es": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", - "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" - } + } }, "node_modules/parent-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", - "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", + "dev": true, + "license": "MIT", + "dependencies": { "callsites": "^3.1.0" - }, - "engines": { + }, + "engines": { "node": ">=8" - } + } }, "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, - "license": "MIT" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" }, "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=12" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/jonschlinkert" - } + } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "github", + "url": "https://github.com/sponsors/ai" } - ], - "license": "MIT", - "dependencies": { + ], + "license": "MIT", + "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" - }, - "engines": { + }, + "engines": { "node": "^10 || ^12 || >=14" - } + } }, "node_modules/preact": { - "version": "10.27.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", - "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "10.27.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.2.tgz", + "integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==", + "dev": true, + "license": "MIT", + "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" - } + } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { "prettier": "bin/prettier.cjs" - }, - "engines": { + }, + "engines": { "node": ">=14" - }, - "funding": { + }, + "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" - } + } }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, - "license": "MIT", - "funding": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", + "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "dev": true, + "license": "MIT", + "dependencies": { "regex-utilities": "^2.3.0" - } + } }, "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { "regex-utilities": "^2.3.0" - } + } }, "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, - "license": "MIT" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=8" - } + } }, "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" }, "node_modules/rollup": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", - "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/estree": "1.0.8" - }, - "bin": { + }, + "bin": { "rollup": "dist/bin/rollup" - }, - "engines": { + }, + "engines": { "node": ">=18.0.0", "npm": ">=8.0.0" - }, - "optionalDependencies": { + }, + "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", @@ -2560,36 +2560,36 @@ "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" - } + } }, "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "dev": true, - "license": "MIT", - "peer": true + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { "semver": "bin/semver.js" - }, - "engines": { + }, + "engines": { "node": ">=10" - } + } }, "node_modules/shiki": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", - "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", @@ -2598,242 +2598,242 @@ "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" - } + } }, "node_modules/smol-toml": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", - "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", + "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { "node": ">= 18" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/cyyynthia" - } + } }, "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": { + "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/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { "node": ">=0.10.0" - } + } }, "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" - }, - "funding": { + }, + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/superjson": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.5.tgz", - "integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.5.tgz", + "integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==", + "dev": true, + "license": "MIT", + "dependencies": { "copy-anything": "^4" - }, - "engines": { + }, + "engines": { "node": ">=16" - } + } }, "node_modules/tabbable": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", - "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", - "dev": true, - "license": "MIT" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", + "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "dev": true, + "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" - }, - "engines": { + }, + "engines": { "node": ">=12.0.0" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/SuperchupuDev" - } + } }, "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "license": "MIT", - "funding": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } }, "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" - }, - "funding": { + }, + "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" - } + } }, "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" - }, - "bin": { + }, + "bin": { "vite": "bin/vite.js" - }, - "engines": { + }, + "engines": { "node": "^18.0.0 || >=20.0.0" - }, - "funding": { + }, + "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { + }, + "optionalDependencies": { "fsevents": "~2.3.3" - }, - "peerDependencies": { + }, + "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", @@ -2842,41 +2842,41 @@ "stylus": "*", "sugarss": "*", "terser": "^5.4.0" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "@types/node": { - "optional": true + "optional": true }, "less": { - "optional": true + "optional": true }, "lightningcss": { - "optional": true + "optional": true }, "sass": { - "optional": true + "optional": true }, "sass-embedded": { - "optional": true + "optional": true }, "stylus": { - "optional": true + "optional": true }, "sugarss": { - "optional": true + "optional": true }, "terser": { - "optional": true + "optional": true } - } + } }, "node_modules/vitepress": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", - "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", @@ -2895,95 +2895,95 @@ "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" - }, - "bin": { + }, + "bin": { "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { + }, + "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "markdown-it-mathjax3": { - "optional": true + "optional": true }, "postcss": { - "optional": true + "optional": true } - } + } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "dev": true, - "license": "MIT" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "dev": true, - "license": "MIT" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" }, "node_modules/vue": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", - "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", - "dev": true, - "license": "MIT", - "dependencies": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", + "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", + "dev": true, + "license": "MIT", + "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", "@vue/runtime-dom": "3.5.24", "@vue/server-renderer": "3.5.24", "@vue/shared": "3.5.24" - }, - "peerDependencies": { + }, + "peerDependencies": { "typescript": "*" - }, - "peerDependenciesMeta": { + }, + "peerDependenciesMeta": { "typescript": { - "optional": true + "optional": true } - } + } }, "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "dev": true, - "license": "MIT", - "engines": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { "node": ">=12" - }, - "funding": { + }, + "funding": { "url": "https://github.com/sponsors/sindresorhus" - } + } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, - "license": "ISC", - "bin": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { "yaml": "bin.mjs" - }, - "engines": { + }, + "engines": { "node": ">= 14.6" - } + } }, "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" - } + } } } } diff --git a/docs/package.json b/docs/package.json index b73a0a20..2da4b6cb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -22,4 +22,4 @@ "vitepress": "^1.6.4", "vue": "^3.5.24" } -} \ No newline at end of file +} diff --git a/docs/public/logo.svg b/docs/public/logo.svg index cccc6fd8..31770331 100644 --- a/docs/public/logo.svg +++ b/docs/public/logo.svg @@ -1,8 +1,8 @@ - - + + @@ -25,23 +25,23 @@ - - - - - - + + + + + + - - - + + + - - - + + + diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index d88a042f..1e33ac41 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -3,92 +3,92 @@ import tseslint from "typescript-eslint"; import unusedImports from "eslint-plugin-unused-imports"; export default [ - { - ignores: [ - "sync-client/src/services/types.ts", - "**/dist/", - "**/*.mjs", - "**/*.js" - ] - }, - ...tseslint.config({ - plugins: { - "unused-imports": unusedImports - }, - extends: [eslint.configs.recommended, tseslint.configs.all], - rules: { - "no-unused-vars": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-floating-promises": [ - "error", - { - allowForKnownSafeCalls: [ - { from: "package", name: ["suite", "test"], package: "node:test" }, - ], - }, - ], - "@typescript-eslint/parameter-properties": "off", - "@typescript-eslint/require-await": "off", - "@typescript-eslint/class-methods-use-this": "off", - "@typescript-eslint/consistent-return": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/max-params": "off", - "@typescript-eslint/no-magic-numbers": "off", - "@typescript-eslint/prefer-readonly-parameter-types": "off", - "@typescript-eslint/naming-convention": "off", - "no-restricted-properties": [ - "error", - { - object: "Promise", - property: "all", - message: "Use `awaitAll` instead of Promise.all to always await all promises." - }, + { + ignores: [ + "sync-client/src/services/types.ts", + "**/dist/", + "**/*.mjs", + "**/*.js" + ] + }, + ...tseslint.config({ + plugins: { + "unused-imports": unusedImports + }, + extends: [eslint.configs.recommended, tseslint.configs.all], + rules: { + "no-unused-vars": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-floating-promises": [ + "error", { - object: "Promise", - property: "allSettled", - message: "Use `awaitAll` instead of Promise.allSettled to always await all promises and throw on errors." - }, - { - object: "String", - property: "replace", - message: "Use replaceAll instead of replace to replace all occurrences of a substring." - } - ], - "no-restricted-syntax": [ - "error", - { - selector: "CallExpression[callee.property.name='splice'][arguments.length=2][arguments.1.type='Literal'][arguments.1.value=1]", - message: "Use `removeFromArray(array, item)` instead of manually using indexOf + splice(index, 1). Import from 'sync-client/src/utils/remove-from-array'." - }, - { - selector: "CallExpression[callee.property.name='filter'] > ArrowFunctionExpression[body.type='BinaryExpression'][body.operator='!==']", - message: "Use `removeFromArray(array, item)` instead of filter(x => x !== item) for better performance. Import from 'sync-client/src/utils/remove-from-array'." - }, - { - selector: "CallExpression[callee.property.name='filter'] > ArrowFunctionExpression > BlockStatement > ReturnStatement > BinaryExpression[operator='!==']", - message: "Use `removeFromArray(array, item)` instead of filter(x => { return x !== item }) for better performance. Import from 'sync-client/src/utils/remove-from-array'." - }, - { - selector: "CallExpression[callee.property.name='filter'] > FunctionExpression[body.type='BlockStatement'] > BlockStatement > ReturnStatement > BinaryExpression[operator='!==']", - message: "Use `removeFromArray(array, item)` instead of filter(function(x) { return x !== item }) for better performance. Import from 'sync-client/src/utils/remove-from-array'." - } - ], - "unused-imports/no-unused-vars": [ - "warn", - { - vars: "all", - varsIgnorePattern: "^_", - args: "after-used", - argsIgnorePattern: "^_" - } - ] - }, - languageOptions: { - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname - } - } - }) + allowForKnownSafeCalls: [ + { from: "package", name: ["suite", "test"], package: "node:test" }, + ], + }, + ], + "@typescript-eslint/parameter-properties": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/class-methods-use-this": "off", + "@typescript-eslint/consistent-return": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/max-params": "off", + "@typescript-eslint/no-magic-numbers": "off", + "@typescript-eslint/prefer-readonly-parameter-types": "off", + "@typescript-eslint/naming-convention": "off", + "no-restricted-properties": [ + "error", + { + object: "Promise", + property: "all", + message: "Use `awaitAll` instead of Promise.all to always await all promises." + }, + { + object: "Promise", + property: "allSettled", + message: "Use `awaitAll` instead of Promise.allSettled to always await all promises and throw on errors." + }, + { + object: "String", + property: "replace", + message: "Use replaceAll instead of replace to replace all occurrences of a substring." + } + ], + "no-restricted-syntax": [ + "error", + { + selector: "CallExpression[callee.property.name='splice'][arguments.length=2][arguments.1.type='Literal'][arguments.1.value=1]", + message: "Use `removeFromArray(array, item)` instead of manually using indexOf + splice(index, 1). Import from 'sync-client/src/utils/remove-from-array'." + }, + { + selector: "CallExpression[callee.property.name='filter'] > ArrowFunctionExpression[body.type='BinaryExpression'][body.operator='!==']", + message: "Use `removeFromArray(array, item)` instead of filter(x => x !== item) for better performance. Import from 'sync-client/src/utils/remove-from-array'." + }, + { + selector: "CallExpression[callee.property.name='filter'] > ArrowFunctionExpression > BlockStatement > ReturnStatement > BinaryExpression[operator='!==']", + message: "Use `removeFromArray(array, item)` instead of filter(x => { return x !== item }) for better performance. Import from 'sync-client/src/utils/remove-from-array'." + }, + { + selector: "CallExpression[callee.property.name='filter'] > FunctionExpression[body.type='BlockStatement'] > BlockStatement > ReturnStatement > BinaryExpression[operator='!==']", + message: "Use `removeFromArray(array, item)` instead of filter(function(x) { return x !== item }) for better performance. Import from 'sync-client/src/utils/remove-from-array'." + } + ], + "unused-imports/no-unused-vars": [ + "warn", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_" + } + ] + }, + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + } + }) ]; diff --git a/frontend/local-client-cli/src/args.test.ts b/frontend/local-client-cli/src/args.test.ts index 206e39b7..eb195538 100644 --- a/frontend/local-client-cli/src/args.test.ts +++ b/frontend/local-client-cli/src/args.test.ts @@ -4,227 +4,227 @@ import { parseArgs } from "./args"; import { LogLevel } from "sync-client"; test("parseArgs - parse basic arguments", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default" + ]); - assert.equal(args.localPath, "/path/to/vault"); - assert.equal(args.remoteUri, "https://sync.example.com"); - assert.equal(args.token, "mytoken"); - assert.equal(args.vaultName, "default"); + assert.equal(args.localPath, "/path/to/vault"); + assert.equal(args.remoteUri, "https://sync.example.com"); + assert.equal(args.token, "mytoken"); + assert.equal(args.vaultName, "default"); }); test("parseArgs - parse long form arguments", () => { - const args = parseArgs([ - "node", - "cli.js", - "--local-path", - "/path/to/vault", - "--remote-uri", - "https://sync.example.com", - "--token", - "mytoken", - "--vault-name", - "default" - ]); + const args = parseArgs([ + "node", + "cli.js", + "--local-path", + "/path/to/vault", + "--remote-uri", + "https://sync.example.com", + "--token", + "mytoken", + "--vault-name", + "default" + ]); - assert.equal(args.localPath, "/path/to/vault"); - assert.equal(args.remoteUri, "https://sync.example.com"); - assert.equal(args.token, "mytoken"); - assert.equal(args.vaultName, "default"); + assert.equal(args.localPath, "/path/to/vault"); + assert.equal(args.remoteUri, "https://sync.example.com"); + assert.equal(args.token, "mytoken"); + assert.equal(args.vaultName, "default"); }); test("parseArgs - parse with optional arguments", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--sync-concurrency", - "5", - "--max-file-size-mb", - "20" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--sync-concurrency", + "5", + "--max-file-size-mb", + "20" + ]); - assert.equal(args.syncConcurrency, 5); - assert.equal(args.maxFileSizeMB, 20); + assert.equal(args.syncConcurrency, 5); + assert.equal(args.maxFileSizeMB, 20); }); test("parseArgs - parse with multiple ignore patterns", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--ignore-pattern", - ".git/**", - "*.tmp" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--ignore-pattern", + ".git/**", + "*.tmp" + ]); - assert.deepEqual(args.ignorePatterns, [".git/**", "*.tmp"]); + assert.deepEqual(args.ignorePatterns, [".git/**", "*.tmp"]); }); test("parseArgs - throws on missing required arguments", () => { - assert.throws(() => { - parseArgs(["node", "cli.js", "-r", "https://sync.example.com"]); - }, /required option/); + assert.throws(() => { + parseArgs(["node", "cli.js", "-r", "https://sync.example.com"]); + }, /required option/); }); test("parseArgs - throws on missing remote uri", () => { - assert.throws(() => { - parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-t", - "mytoken", - "-v", - "default" - ]); - }, /--remote-uri/); + assert.throws(() => { + parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-t", + "mytoken", + "-v", + "default" + ]); + }, /--remote-uri/); }); test("parseArgs - throws on missing token", () => { - assert.throws(() => { - parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-v", - "default" - ]); - }, /--token/); + assert.throws(() => { + parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-v", + "default" + ]); + }, /--token/); }); test("parseArgs - throws on missing vault name", () => { - assert.throws(() => { - parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken" - ]); - }, /--vault-name/); + assert.throws(() => { + parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken" + ]); + }, /--vault-name/); }); test("parseArgs - default log level is INFO", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default" + ]); - assert.equal(args.logLevel, LogLevel.INFO); + assert.equal(args.logLevel, LogLevel.INFO); }); test("parseArgs - parse DEBUG log level", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--log-level", - "DEBUG" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--log-level", + "DEBUG" + ]); - assert.equal(args.logLevel, LogLevel.DEBUG); + assert.equal(args.logLevel, LogLevel.DEBUG); }); test("parseArgs - parse ERROR log level", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--log-level", - "ERROR" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--log-level", + "ERROR" + ]); - assert.equal(args.logLevel, LogLevel.ERROR); + assert.equal(args.logLevel, LogLevel.ERROR); }); test("parseArgs - log level is case insensitive", () => { - const args = parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--log-level", - "debug" - ]); + const args = parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--log-level", + "debug" + ]); - assert.equal(args.logLevel, LogLevel.DEBUG); + assert.equal(args.logLevel, LogLevel.DEBUG); }); test("parseArgs - throws on invalid log level", () => { - assert.throws(() => { - parseArgs([ - "node", - "cli.js", - "-l", - "/path/to/vault", - "-r", - "https://sync.example.com", - "-t", - "mytoken", - "-v", - "default", - "--log-level", - "INVALID" - ]); - }, /Invalid log level/); + assert.throws(() => { + parseArgs([ + "node", + "cli.js", + "-l", + "/path/to/vault", + "-r", + "https://sync.example.com", + "-t", + "mytoken", + "-v", + "default", + "--log-level", + "INVALID" + ]); + }, /Invalid log level/); }); diff --git a/frontend/local-client-cli/src/args.ts b/frontend/local-client-cli/src/args.ts index fc2d4a95..615b9d71 100644 --- a/frontend/local-client-cli/src/args.ts +++ b/frontend/local-client-cli/src/args.ts @@ -3,134 +3,134 @@ import packageJson from "../package.json"; import { LogLevel } from "sync-client"; export interface CliArgs { - remoteUri: string; - token: string; - vaultName: string; - localPath: string; - syncConcurrency?: number; - maxFileSizeMB?: number; - ignorePatterns?: string[]; - webSocketRetryIntervalMs?: number; - logLevel: LogLevel; - health?: string; - enableTelemetry?: boolean; + remoteUri: string; + token: string; + vaultName: string; + localPath: string; + syncConcurrency?: number; + maxFileSizeMB?: number; + ignorePatterns?: string[]; + webSocketRetryIntervalMs?: number; + logLevel: LogLevel; + health?: string; + enableTelemetry?: boolean; } export function parseArgs(argv: string[]): CliArgs { - const program = new Command(); + const program = new Command(); - program - .name("vaultlink") - .description( - "VaultLink Local CLI - Sync your vault to the local filesystem" - ) - .version(packageJson.version) - .option("-l, --local-path ", "Local directory path to sync") - .option("-r, --remote-uri ", "Remote server URI") - .option("-t, --token ", "Authentication token") - .option("-v, --vault-name ", "Vault name") - .option( - "--sync-concurrency ", - "[OPTIONAL] Number of concurrent sync operations", - parseInt - ) - .option( - "--max-file-size-mb ", - "[OPTIONAL] Maximum file size in MB", - parseInt - ) - .option( - "--ignore-pattern ", - "[OPTIONAL] Patterns to ignore (can be specified multiple times)" - ) - .option( - "--websocket-retry-interval-ms ", - "[OPTIONAL] WebSocket retry interval in milliseconds", - parseInt - ) - .option( - "--log-level ", - "[OPTIONAL] Log level (DEBUG, INFO, WARNING, ERROR)", - "INFO" - ) - .option( - "--health ", - "[OPTIONAL] Path to health status file for Docker healthcheck" - ) - .option( - "--enable-telemetry", - "[OPTIONAL] Enable telemetry (disabled by default)" - ) - .addHelpText( - "after", - ` + program + .name("vaultlink") + .description( + "VaultLink Local CLI - Sync your vault to the local filesystem" + ) + .version(packageJson.version) + .option("-l, --local-path ", "Local directory path to sync") + .option("-r, --remote-uri ", "Remote server URI") + .option("-t, --token ", "Authentication token") + .option("-v, --vault-name ", "Vault name") + .option( + "--sync-concurrency ", + "[OPTIONAL] Number of concurrent sync operations", + parseInt + ) + .option( + "--max-file-size-mb ", + "[OPTIONAL] Maximum file size in MB", + parseInt + ) + .option( + "--ignore-pattern ", + "[OPTIONAL] Patterns to ignore (can be specified multiple times)" + ) + .option( + "--websocket-retry-interval-ms ", + "[OPTIONAL] WebSocket retry interval in milliseconds", + parseInt + ) + .option( + "--log-level ", + "[OPTIONAL] Log level (DEBUG, INFO, WARNING, ERROR)", + "INFO" + ) + .option( + "--health ", + "[OPTIONAL] Path to health status file for Docker healthcheck" + ) + .option( + "--enable-telemetry", + "[OPTIONAL] Enable telemetry (disabled by default)" + ) + .addHelpText( + "after", + ` Examples: $ vaultlink -l ./my-vault -r https://sync.example.com -t mytoken -v default $ vaultlink -l ./my-vault -r https://sync.example.com -t mytoken -v default \\ - --ignore-pattern ".git/**" --ignore-pattern "*.tmp" + --ignore-pattern ".git/**" --ignore-pattern "*.tmp" $ vaultlink -l ./my-vault -r https://sync.example.com -t mytoken -v default \\ - --log-level DEBUG + --log-level DEBUG ` - ); + ); - program.parse(argv); + program.parse(argv); - /* eslint-disable @typescript-eslint/no-unsafe-type-assertion */ - const opts = program.opts(); - const localPath = opts.localPath as string | undefined; - const remoteUri = opts.remoteUri as string | undefined; - const token = opts.token as string | undefined; - const vaultName = opts.vaultName as string | undefined; - const syncConcurrency = opts.syncConcurrency as number | undefined; - const maxFileSizeMb = opts.maxFileSizeMb as number | undefined; - const ignorePattern = opts.ignorePattern as string[] | undefined; - const websocketRetryIntervalMs = opts.websocketRetryIntervalMs as - | number - | undefined; - const logLevelStr = (opts.logLevel as string | undefined) ?? "INFO"; - const health = opts.health as string | undefined; - const enableTelemetry = opts.enableTelemetry as boolean | undefined; - /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ + /* eslint-disable @typescript-eslint/no-unsafe-type-assertion */ + const opts = program.opts(); + const localPath = opts.localPath as string | undefined; + const remoteUri = opts.remoteUri as string | undefined; + const token = opts.token as string | undefined; + const vaultName = opts.vaultName as string | undefined; + const syncConcurrency = opts.syncConcurrency as number | undefined; + const maxFileSizeMb = opts.maxFileSizeMb as number | undefined; + const ignorePattern = opts.ignorePattern as string[] | undefined; + const websocketRetryIntervalMs = opts.websocketRetryIntervalMs as + | number + | undefined; + const logLevelStr = (opts.logLevel as string | undefined) ?? "INFO"; + const health = opts.health as string | undefined; + const enableTelemetry = opts.enableTelemetry as boolean | undefined; + /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ - if (localPath === undefined) { - throw new Error( - "required option '-l, --local-path ' not specified" - ); - } - if (remoteUri === undefined) { - throw new Error("required option '--remote-uri ' not specified"); - } - if (token === undefined) { - throw new Error("required option '--token ' not specified"); - } - if (vaultName === undefined) { - throw new Error("required option '--vault-name ' not specified"); - } + if (localPath === undefined) { + throw new Error( + "required option '-l, --local-path ' not specified" + ); + } + if (remoteUri === undefined) { + throw new Error("required option '--remote-uri ' not specified"); + } + if (token === undefined) { + throw new Error("required option '--token ' not specified"); + } + if (vaultName === undefined) { + throw new Error("required option '--vault-name ' not specified"); + } - // Validate and parse log level - const logLevelUpper = logLevelStr.toUpperCase(); - const validLogLevels = Object.values(LogLevel); - const isLogLevel = (value: string): value is LogLevel => { - return (validLogLevels as readonly string[]).includes(value); - }; - if (!isLogLevel(logLevelUpper)) { - throw new Error( - `Invalid log level '${logLevelStr}'. Valid values are: ${validLogLevels.join(", ")}` - ); - } - const logLevel = logLevelUpper; + // Validate and parse log level + const logLevelUpper = logLevelStr.toUpperCase(); + const validLogLevels = Object.values(LogLevel); + const isLogLevel = (value: string): value is LogLevel => { + return (validLogLevels as readonly string[]).includes(value); + }; + if (!isLogLevel(logLevelUpper)) { + throw new Error( + `Invalid log level '${logLevelStr}'. Valid values are: ${validLogLevels.join(", ")}` + ); + } + const logLevel = logLevelUpper; - return { - localPath, - remoteUri, - token, - vaultName, - syncConcurrency, - maxFileSizeMB: maxFileSizeMb, - ignorePatterns: ignorePattern, - webSocketRetryIntervalMs: websocketRetryIntervalMs, - logLevel, - health, - enableTelemetry - }; + return { + localPath, + remoteUri, + token, + vaultName, + syncConcurrency, + maxFileSizeMB: maxFileSizeMb, + ignorePatterns: ignorePattern, + webSocketRetryIntervalMs: websocketRetryIntervalMs, + logLevel, + health, + enableTelemetry + }; } diff --git a/frontend/local-client-cli/src/cli.ts b/frontend/local-client-cli/src/cli.ts index 36449d8d..61582a0d 100644 --- a/frontend/local-client-cli/src/cli.ts +++ b/frontend/local-client-cli/src/cli.ts @@ -3,11 +3,11 @@ import * as fs from "fs/promises"; import * as fsSync from "fs"; import type { NetworkConnectionStatus } from "sync-client"; import { - SyncClient, - DEFAULT_SETTINGS, - LogLevel, - type SyncSettings, - type StoredDatabase + SyncClient, + DEFAULT_SETTINGS, + LogLevel, + type SyncSettings, + type StoredDatabase } from "sync-client"; import { parseArgs } from "./args"; import { NodeFileSystemOperations } from "./node-filesystem"; @@ -16,229 +16,229 @@ import { formatLogLine, colorize, styleText } from "./logger-formatter"; import packageJson from "../package.json"; function writeHealthStatus( - filePath: string, - connectionStatus: NetworkConnectionStatus + filePath: string, + connectionStatus: NetworkConnectionStatus ): void { - try { - fsSync.writeFileSync(filePath, JSON.stringify(connectionStatus)); - } catch (error) { - console.error( - `Failed to write health status to ${filePath}: ${error instanceof Error ? error.message : String(error)}` - ); - } + try { + fsSync.writeFileSync(filePath, JSON.stringify(connectionStatus)); + } catch (error) { + console.error( + `Failed to write health status to ${filePath}: ${error instanceof Error ? error.message : String(error)}` + ); + } } const LOG_LEVEL_ORDER = { - [LogLevel.DEBUG]: 0, - [LogLevel.INFO]: 1, - [LogLevel.WARNING]: 2, - [LogLevel.ERROR]: 3 + [LogLevel.DEBUG]: 0, + [LogLevel.INFO]: 1, + [LogLevel.WARNING]: 2, + [LogLevel.ERROR]: 3 }; async function main(): Promise { - const args = parseArgs(process.argv); - const absolutePath = path.resolve(args.localPath); + const args = parseArgs(process.argv); + const absolutePath = path.resolve(args.localPath); - try { - const stats = await fs.stat(absolutePath); - if (!stats.isDirectory()) { - console.error( - colorize(`Error: ${absolutePath} is not a directory`, "red") - ); - process.exit(1); - } - } catch (error) { - console.error( - colorize( - `Error: Cannot access directory ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`, - "red" - ) - ); - process.exit(1); - } + try { + const stats = await fs.stat(absolutePath); + if (!stats.isDirectory()) { + console.error( + colorize(`Error: ${absolutePath} is not a directory`, "red") + ); + process.exit(1); + } + } catch (error) { + console.error( + colorize( + `Error: Cannot access directory ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`, + "red" + ) + ); + process.exit(1); + } - console.log( - styleText("VaultLink Local CLI", "bold", "cyan") + - colorize(` v${packageJson.version}`, "dim") - ); - console.log(colorize("=".repeat(50), "dim")); - console.log( - `${colorize("Local path:", "dim")} ${colorize(absolutePath, "green")}` - ); - console.log( - `${colorize("Remote URI:", "dim")} ${colorize(args.remoteUri, "cyan")}` - ); - console.log( - `${colorize("Vault name:", "dim")} ${colorize(args.vaultName, "green")}` - ); - console.log(""); + console.log( + styleText("VaultLink Local CLI", "bold", "cyan") + + colorize(` v${packageJson.version}`, "dim") + ); + console.log(colorize("=".repeat(50), "dim")); + console.log( + `${colorize("Local path:", "dim")} ${colorize(absolutePath, "green")}` + ); + console.log( + `${colorize("Remote URI:", "dim")} ${colorize(args.remoteUri, "cyan")}` + ); + console.log( + `${colorize("Vault name:", "dim")} ${colorize(args.vaultName, "green")}` + ); + console.log(""); - const dataDir = path.join(absolutePath, ".vaultlink"); - const dataFile = path.join(dataDir, "sync-data.json"); + const dataDir = path.join(absolutePath, ".vaultlink"); + const dataFile = path.join(dataDir, "sync-data.json"); - await fs.mkdir(dataDir, { recursive: true }); + await fs.mkdir(dataDir, { recursive: true }); - const fileSystem = new NodeFileSystemOperations(absolutePath); + const fileSystem = new NodeFileSystemOperations(absolutePath); - const ignorePatterns = [ - ...(args.ignorePatterns ?? []), - ".vaultlink/**", - ".git/**" - ]; + const ignorePatterns = [ + ...(args.ignorePatterns ?? []), + ".vaultlink/**", + ".git/**" + ]; - const settings: SyncSettings = { - ...DEFAULT_SETTINGS, - remoteUri: args.remoteUri, - token: args.token, - vaultName: args.vaultName, - syncConcurrency: - args.syncConcurrency ?? DEFAULT_SETTINGS.syncConcurrency, - maxFileSizeMB: args.maxFileSizeMB ?? DEFAULT_SETTINGS.maxFileSizeMB, - ignorePatterns, - webSocketRetryIntervalMs: - args.webSocketRetryIntervalMs ?? - DEFAULT_SETTINGS.webSocketRetryIntervalMs, - isSyncEnabled: true, - enableTelemetry: - args.enableTelemetry ?? DEFAULT_SETTINGS.enableTelemetry - }; + const settings: SyncSettings = { + ...DEFAULT_SETTINGS, + remoteUri: args.remoteUri, + token: args.token, + vaultName: args.vaultName, + syncConcurrency: + args.syncConcurrency ?? DEFAULT_SETTINGS.syncConcurrency, + maxFileSizeMB: args.maxFileSizeMB ?? DEFAULT_SETTINGS.maxFileSizeMB, + ignorePatterns, + webSocketRetryIntervalMs: + args.webSocketRetryIntervalMs ?? + DEFAULT_SETTINGS.webSocketRetryIntervalMs, + isSyncEnabled: true, + enableTelemetry: + args.enableTelemetry ?? DEFAULT_SETTINGS.enableTelemetry + }; - const client = await SyncClient.create({ - fs: fileSystem, - persistence: { - load: async () => { - let database: Partial | undefined = undefined; - try { - const content = await fs.readFile(dataFile, "utf-8"); - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - database = JSON.parse(content) as Partial; - } catch { - console.error( - colorize( - `Cannot read data file at ${dataFile}`, - "yellow" - ) - ); - } + const client = await SyncClient.create({ + fs: fileSystem, + persistence: { + load: async () => { + let database: Partial | undefined = undefined; + try { + const content = await fs.readFile(dataFile, "utf-8"); + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + database = JSON.parse(content) as Partial; + } catch { + console.error( + colorize( + `Cannot read data file at ${dataFile}`, + "yellow" + ) + ); + } - return { - settings, - database - }; - }, - save: async ({ database: persistedDatabase }) => { - // settings can't be updated when running with this CLI - await fs.writeFile( - dataFile, - JSON.stringify(persistedDatabase, null, 2) - ); - } - }, - nativeLineEndings: process.platform === "win32" ? "\r\n" : "\n" - }); + return { + settings, + database + }; + }, + save: async ({ database: persistedDatabase }) => { + // settings can't be updated when running with this CLI + await fs.writeFile( + dataFile, + JSON.stringify(persistedDatabase, null, 2) + ); + } + }, + nativeLineEndings: process.platform === "win32" ? "\r\n" : "\n" + }); - if (args.health !== undefined) { - const healthFile = args.health; - const healthInterval = setInterval(() => { - void client.checkConnection().then((status) => { - writeHealthStatus(healthFile, status); - }); - }, 30 * 1000); // every 30 seconds - const clearHealthInterval = (): void => { - clearInterval(healthInterval); - }; - process.on("SIGINT", clearHealthInterval); - process.on("SIGTERM", clearHealthInterval); - process.on("exit", clearHealthInterval); - } + if (args.health !== undefined) { + const healthFile = args.health; + const healthInterval = setInterval(() => { + void client.checkConnection().then((status) => { + writeHealthStatus(healthFile, status); + }); + }, 30 * 1000); // every 30 seconds + const clearHealthInterval = (): void => { + clearInterval(healthInterval); + }; + process.on("SIGINT", clearHealthInterval); + process.on("SIGTERM", clearHealthInterval); + process.on("exit", clearHealthInterval); + } - // Add colored log formatter with level filtering - client.logger.addOnMessageListener((logLine) => { - // Only show messages at or above the configured log level - if (LOG_LEVEL_ORDER[logLine.level] >= LOG_LEVEL_ORDER[args.logLevel]) { - console.log(formatLogLine(logLine)); - } - }); + // Add colored log formatter with level filtering + client.logger.addOnMessageListener((logLine) => { + // Only show messages at or above the configured log level + if (LOG_LEVEL_ORDER[logLine.level] >= LOG_LEVEL_ORDER[args.logLevel]) { + console.log(formatLogLine(logLine)); + } + }); - client.logger.info("Starting sync client"); + client.logger.info("Starting sync client"); - const fileWatcher = new FileWatcher(absolutePath, client); + const fileWatcher = new FileWatcher(absolutePath, client); - client.addWebSocketStatusChangeListener(() => { - const isConnected = client.isWebSocketConnected; - client.logger.info( - `WebSocket status changed: ${isConnected ? "connected" : "disconnected"}` - ); - }); + client.addWebSocketStatusChangeListener(() => { + const isConnected = client.isWebSocketConnected; + client.logger.info( + `WebSocket status changed: ${isConnected ? "connected" : "disconnected"}` + ); + }); - client.addRemainingSyncOperationsListener((remaining) => { - if (remaining === 0) { - client.logger.info("All sync operations completed"); - } else { - client.logger.info(`${remaining} sync operations remaining`); - } - }); + client.addRemainingSyncOperationsListener((remaining) => { + if (remaining === 0) { + client.logger.info("All sync operations completed"); + } else { + client.logger.info(`${remaining} sync operations remaining`); + } + }); - const gracefulShutdown = async (signal: string): Promise => { - console.log( - colorize( - `\n${signal} received. Shutting down gracefully...`, - "yellow" - ) - ); + const gracefulShutdown = async (signal: string): Promise => { + console.log( + colorize( + `\n${signal} received. Shutting down gracefully...`, + "yellow" + ) + ); - fileWatcher.stop(); - await client.waitUntilFinished(); - await client.destroy(); - console.log(colorize("Shutdown complete", "green")); - process.exit(0); - }; + fileWatcher.stop(); + await client.waitUntilFinished(); + await client.destroy(); + console.log(colorize("Shutdown complete", "green")); + process.exit(0); + }; - process.on("SIGINT", () => { - void gracefulShutdown("SIGINT"); - }); - process.on("SIGTERM", () => { - void gracefulShutdown("SIGTERM"); - }); + process.on("SIGINT", () => { + void gracefulShutdown("SIGINT"); + }); + process.on("SIGTERM", () => { + void gracefulShutdown("SIGTERM"); + }); - try { - const connectionStatus = await client.checkConnection(); - if (!connectionStatus.isSuccessful) { - console.error( - colorize( - `Error: Cannot connect to server: ${connectionStatus.serverMessage}`, - "red" - ) - ); - process.exit(1); - } + try { + const connectionStatus = await client.checkConnection(); + if (!connectionStatus.isSuccessful) { + console.error( + colorize( + `Error: Cannot connect to server: ${connectionStatus.serverMessage}`, + "red" + ) + ); + process.exit(1); + } - console.log(`${colorize("✓", "green")} Server connection successful`); - console.log(colorize("Press Ctrl+C to stop", "dim")); - console.log(""); + console.log(`${colorize("✓", "green")} Server connection successful`); + console.log(colorize("Press Ctrl+C to stop", "dim")); + console.log(""); - await client.start(); - fileWatcher.start(); - } catch (error) { - console.error( - colorize( - `Fatal error: ${error instanceof Error ? error.message : String(error)}`, - "red" - ) - ); + await client.start(); + fileWatcher.start(); + } catch (error) { + console.error( + colorize( + `Fatal error: ${error instanceof Error ? error.message : String(error)}`, + "red" + ) + ); - fileWatcher.stop(); - await client.destroy(); - process.exit(1); - } + fileWatcher.stop(); + await client.destroy(); + process.exit(1); + } } main().catch((error: unknown) => { - console.error( - colorize( - `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, - "red" - ) - ); - process.exit(1); + console.error( + colorize( + `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, + "red" + ) + ); + process.exit(1); }); diff --git a/frontend/local-client-cli/src/healthcheck.ts b/frontend/local-client-cli/src/healthcheck.ts index 256cd2d8..2dd9e721 100644 --- a/frontend/local-client-cli/src/healthcheck.ts +++ b/frontend/local-client-cli/src/healthcheck.ts @@ -9,58 +9,58 @@ import * as fs from "fs"; import type { NetworkConnectionStatus } from "sync-client"; function isHealthStatus(value: unknown): value is NetworkConnectionStatus { - if (typeof value !== "object" || value === null) { - return false; - } + if (typeof value !== "object" || value === null) { + return false; + } - return ( - "isSuccessful" in value && - typeof value.isSuccessful === "boolean" && - "isWebSocketConnected" in value && - typeof value.isWebSocketConnected === "boolean" && - "serverMessage" in value && - typeof value.serverMessage === "string" - ); + return ( + "isSuccessful" in value && + typeof value.isSuccessful === "boolean" && + "isWebSocketConnected" in value && + typeof value.isWebSocketConnected === "boolean" && + "serverMessage" in value && + typeof value.serverMessage === "string" + ); } function main(): void { - if (process.argv.length < 3) { - console.error("Usage: healthcheck "); - process.exit(1); - } - const [, , healthFile] = process.argv; + if (process.argv.length < 3) { + console.error("Usage: healthcheck "); + process.exit(1); + } + const [, , healthFile] = process.argv; - try { - // Check if health file exists - if (!fs.existsSync(healthFile)) { - console.error(`Health file does not exist: ${healthFile}`); - process.exit(1); - } + try { + // Check if health file exists + if (!fs.existsSync(healthFile)) { + console.error(`Health file does not exist: ${healthFile}`); + process.exit(1); + } - // Read and parse health status - const content = fs.readFileSync(healthFile, "utf-8"); - const parsed: unknown = JSON.parse(content); + // Read and parse health status + const content = fs.readFileSync(healthFile, "utf-8"); + const parsed: unknown = JSON.parse(content); - // Validate the parsed object using type guard - if (!isHealthStatus(parsed)) { - throw new Error("Invalid health status format"); - } + // Validate the parsed object using type guard + if (!isHealthStatus(parsed)) { + throw new Error("Invalid health status format"); + } - const status = parsed; + const status = parsed; - if (!status.isSuccessful || !status.isWebSocketConnected) { - console.error("Not connected to server: " + status.serverMessage); - process.exit(1); - } + if (!status.isSuccessful || !status.isWebSocketConnected) { + console.error("Not connected to server: " + status.serverMessage); + process.exit(1); + } - console.log("Healthy: Connected to server"); - process.exit(0); - } catch (error) { - console.error( - `Health check failed: ${error instanceof Error ? error.message : String(error)}` - ); - process.exit(1); - } + console.log("Healthy: Connected to server"); + process.exit(0); + } catch (error) { + console.error( + `Health check failed: ${error instanceof Error ? error.message : String(error)}` + ); + process.exit(1); + } } main(); diff --git a/frontend/local-client-cli/src/logger-formatter.ts b/frontend/local-client-cli/src/logger-formatter.ts index 994adc74..9f237103 100644 --- a/frontend/local-client-cli/src/logger-formatter.ts +++ b/frontend/local-client-cli/src/logger-formatter.ts @@ -2,85 +2,85 @@ import { LogLevel, type LogLine } from "sync-client"; // ANSI color codes export const colors = { - reset: "\x1b[0m", - bold: "\x1b[1m", - dim: "\x1b[2m", + reset: "\x1b[0m", + bold: "\x1b[1m", + dim: "\x1b[2m", - // Foreground colors - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - gray: "\x1b[90m" + // Foreground colors + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + gray: "\x1b[90m" } as const; export function colorize(text: string, color: keyof typeof colors): string { - return `${colors[color]}${text}${colors.reset}`; + return `${colors[color]}${text}${colors.reset}`; } /** * Helper function to apply multiple color modifiers to text */ export function styleText( - text: string, - ...modifiers: (keyof typeof colors)[] + text: string, + ...modifiers: (keyof typeof colors)[] ): string { - const prefix = modifiers.map((m) => colors[m]).join(""); - return `${prefix}${text}${colors.reset}`; + const prefix = modifiers.map((m) => colors[m]).join(""); + return `${prefix}${text}${colors.reset}`; } function formatTimestamp(date: Date): string { - const [time] = date.toTimeString().split(" "); - const ms = date.getMilliseconds().toString().padStart(3, "0"); - return colorize(`${time}.${ms}`, "gray"); + const [time] = date.toTimeString().split(" "); + const ms = date.getMilliseconds().toString().padStart(3, "0"); + return colorize(`${time}.${ms}`, "gray"); } function formatLevel(level: LogLevel): string { - const levelStr = level.padEnd(7); - switch (level) { - case LogLevel.DEBUG: - return colorize(levelStr, "cyan"); - case LogLevel.INFO: - return colorize(levelStr, "green"); - case LogLevel.WARNING: - return colorize(levelStr, "yellow"); - case LogLevel.ERROR: - return colorize(levelStr, "red"); - } + const levelStr = level.padEnd(7); + switch (level) { + case LogLevel.DEBUG: + return colorize(levelStr, "cyan"); + case LogLevel.INFO: + return colorize(levelStr, "green"); + case LogLevel.WARNING: + return colorize(levelStr, "yellow"); + case LogLevel.ERROR: + return colorize(levelStr, "red"); + } } function formatMessage(message: string, level: LogLevel): string { - // Highlight important parts of the message - let formatted = message; + // Highlight important parts of the message + let formatted = message; - // Highlight file paths - formatted = formatted.replace( - /(['"])([^'"]*?\.(json|txt|md|js|ts))(['"])/g, - (_, q1, path, _ext, q2) => q1 + colorize(path, "magenta") + q2 - ); + // Highlight file paths + formatted = formatted.replace( + /(['"])([^'"]*?\.(json|txt|md|js|ts))(['"])/g, + (_, q1, path, _ext, q2) => q1 + colorize(path, "magenta") + q2 + ); - // Highlight numbers - formatted = formatted.replace(/\b(\d+)\b/g, (num) => colorize(num, "cyan")); + // Highlight numbers + formatted = formatted.replace(/\b(\d+)\b/g, (num) => colorize(num, "cyan")); - // Highlight patterns like /regex/ - formatted = formatted.replace(/(\/\^[^$]*\$\/)/g, (pattern) => - colorize(pattern, "yellow") - ); + // Highlight patterns like /regex/ + formatted = formatted.replace(/(\/\^[^$]*\$\/)/g, (pattern) => + colorize(pattern, "yellow") + ); - // Make error messages bold - if (level === LogLevel.ERROR) { - formatted = colorize(formatted, "bold"); - } + // Make error messages bold + if (level === LogLevel.ERROR) { + formatted = colorize(formatted, "bold"); + } - return formatted; + return formatted; } export function formatLogLine(logLine: LogLine): string { - const timestamp = formatTimestamp(logLine.timestamp); - const level = formatLevel(logLine.level); - const message = formatMessage(logLine.message, logLine.level); + const timestamp = formatTimestamp(logLine.timestamp); + const level = formatLevel(logLine.level); + const message = formatMessage(logLine.message, logLine.level); - return `${timestamp} ${level} ${message}`; + return `${timestamp} ${level} ${message}`; } diff --git a/frontend/local-client-cli/src/node-filesystem.test.ts b/frontend/local-client-cli/src/node-filesystem.test.ts index 4a72da94..85fb7a0f 100644 --- a/frontend/local-client-cli/src/node-filesystem.test.ts +++ b/frontend/local-client-cli/src/node-filesystem.test.ts @@ -6,157 +6,157 @@ import * as os from "os"; import { NodeFileSystemOperations } from "./node-filesystem"; test("NodeFileSystemOperations - read and write files", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - const content = new TextEncoder().encode("Hello, world!"); - await fsOps.write("test.txt", content); + try { + const content = new TextEncoder().encode("Hello, world!"); + await fsOps.write("test.txt", content); - const readContent = await fsOps.read("test.txt"); - assert.equal(new TextDecoder().decode(readContent), "Hello, world!"); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + const readContent = await fsOps.read("test.txt"); + assert.equal(new TextDecoder().decode(readContent), "Hello, world!"); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - create nested directories with forward slashes", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - const content = new TextEncoder().encode("Nested file"); - // Always use forward slashes in API - await fsOps.write("dir1/dir2/test.txt", content); + try { + const content = new TextEncoder().encode("Nested file"); + // Always use forward slashes in API + await fsOps.write("dir1/dir2/test.txt", content); - const readContent = await fsOps.read("dir1/dir2/test.txt"); - assert.equal(new TextDecoder().decode(readContent), "Nested file"); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + const readContent = await fsOps.read("dir1/dir2/test.txt"); + assert.equal(new TextDecoder().decode(readContent), "Nested file"); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - exists with forward slashes", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - assert.equal(await fsOps.exists("test.txt"), false); + try { + assert.equal(await fsOps.exists("test.txt"), false); - await fsOps.write("test.txt", new TextEncoder().encode("test")); + await fsOps.write("test.txt", new TextEncoder().encode("test")); - assert.equal(await fsOps.exists("test.txt"), true); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + assert.equal(await fsOps.exists("test.txt"), true); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - delete with forward slashes", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - await fsOps.write("test.txt", new TextEncoder().encode("test")); - assert.equal(await fsOps.exists("test.txt"), true); + try { + await fsOps.write("test.txt", new TextEncoder().encode("test")); + assert.equal(await fsOps.exists("test.txt"), true); - await fsOps.delete("test.txt"); - assert.equal(await fsOps.exists("test.txt"), false); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + await fsOps.delete("test.txt"); + assert.equal(await fsOps.exists("test.txt"), false); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - rename with forward slashes", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - const content = new TextEncoder().encode("test content"); - await fsOps.write("old.txt", content); + try { + const content = new TextEncoder().encode("test content"); + await fsOps.write("old.txt", content); - await fsOps.rename("old.txt", "new.txt"); + await fsOps.rename("old.txt", "new.txt"); - assert.equal(await fsOps.exists("old.txt"), false); - assert.equal(await fsOps.exists("new.txt"), true); + assert.equal(await fsOps.exists("old.txt"), false); + assert.equal(await fsOps.exists("new.txt"), true); - const readContent = await fsOps.read("new.txt"); - assert.equal(new TextDecoder().decode(readContent), "test content"); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + const readContent = await fsOps.read("new.txt"); + assert.equal(new TextDecoder().decode(readContent), "test content"); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - rename to nested path with forward slashes", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - const content = new TextEncoder().encode("test content"); - await fsOps.write("old.txt", content); + try { + const content = new TextEncoder().encode("test content"); + await fsOps.write("old.txt", content); - await fsOps.rename("old.txt", "dir1/dir2/new.txt"); + await fsOps.rename("old.txt", "dir1/dir2/new.txt"); - assert.equal(await fsOps.exists("old.txt"), false); - assert.equal(await fsOps.exists("dir1/dir2/new.txt"), true); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + assert.equal(await fsOps.exists("old.txt"), false); + assert.equal(await fsOps.exists("dir1/dir2/new.txt"), true); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - getFileSize", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - const content = new TextEncoder().encode("Hello!"); - await fsOps.write("test.txt", content); + try { + const content = new TextEncoder().encode("Hello!"); + await fsOps.write("test.txt", content); - const size = await fsOps.getFileSize("test.txt"); - assert.equal(size, content.length); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + const size = await fsOps.getFileSize("test.txt"); + assert.equal(size, content.length); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - atomicUpdateText", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - await fsOps.write("test.txt", new TextEncoder().encode("Hello")); + try { + await fsOps.write("test.txt", new TextEncoder().encode("Hello")); - const result = await fsOps.atomicUpdateText("test.txt", (current) => ({ - text: current.text + " World", - cursors: [] - })); + const result = await fsOps.atomicUpdateText("test.txt", (current) => ({ + text: current.text + " World", + cursors: [] + })); - assert.equal(result, "Hello World"); + assert.equal(result, "Hello World"); - const content = await fsOps.read("test.txt"); - assert.equal(new TextDecoder().decode(content), "Hello World"); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + const content = await fsOps.read("test.txt"); + assert.equal(new TextDecoder().decode(content), "Hello World"); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); test("NodeFileSystemOperations - handles paths with forward slashes on all platforms", async () => { - const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); - const fsOps = new NodeFileSystemOperations(tempDir); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "vaultlink-test-")); + const fsOps = new NodeFileSystemOperations(tempDir); - try { - // API should always accept forward slashes - const testPath = "deep/nested/directory/file.txt"; - const content = new TextEncoder().encode("test"); + try { + // API should always accept forward slashes + const testPath = "deep/nested/directory/file.txt"; + const content = new TextEncoder().encode("test"); - await fsOps.write(testPath, content); - assert.equal(await fsOps.exists(testPath), true); + await fsOps.write(testPath, content); + assert.equal(await fsOps.exists(testPath), true); - const readContent = await fsOps.read(testPath); - assert.equal(new TextDecoder().decode(readContent), "test"); + const readContent = await fsOps.read(testPath); + assert.equal(new TextDecoder().decode(readContent), "test"); - await fsOps.delete(testPath); - assert.equal(await fsOps.exists(testPath), false); - } finally { - await fs.rm(tempDir, { recursive: true, force: true }); - } + await fsOps.delete(testPath); + assert.equal(await fsOps.exists(testPath), false); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } }); diff --git a/frontend/local-client-cli/src/node-filesystem.ts b/frontend/local-client-cli/src/node-filesystem.ts index f40143c8..3da8fc3a 100644 --- a/frontend/local-client-cli/src/node-filesystem.ts +++ b/frontend/local-client-cli/src/node-filesystem.ts @@ -2,205 +2,205 @@ import * as fs from "fs/promises"; import type { Dirent } from "fs"; import * as path from "path"; import type { - FileSystemOperations, - RelativePath, - TextWithCursors + FileSystemOperations, + RelativePath, + TextWithCursors } from "sync-client"; export class NodeFileSystemOperations implements FileSystemOperations { - public constructor(private readonly basePath: string) {} + public constructor(private readonly basePath: string) {} - public async listFilesRecursively( - directory: RelativePath | undefined - ): Promise { - const files: RelativePath[] = []; - await this.walkDirectory( - directory !== undefined ? this.toNativePath(directory) : "", - files - ); - return files; - } + public async listFilesRecursively( + directory: RelativePath | undefined + ): Promise { + const files: RelativePath[] = []; + await this.walkDirectory( + directory !== undefined ? this.toNativePath(directory) : "", + files + ); + return files; + } - public async read(relativePath: RelativePath): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - try { - return await fs.readFile(fullPath); - } catch (error) { - throw new Error( - `Failed to read file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + public async read(relativePath: RelativePath): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + try { + return await fs.readFile(fullPath); + } catch (error) { + throw new Error( + `Failed to read file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async write( - relativePath: RelativePath, - content: Uint8Array - ): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - const dir = path.dirname(fullPath); + public async write( + relativePath: RelativePath, + content: Uint8Array + ): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + const dir = path.dirname(fullPath); - try { - await fs.mkdir(dir, { recursive: true }); - await fs.writeFile(fullPath, content); - } catch (error) { - throw new Error( - `Failed to write file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + try { + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile(fullPath, content); + } catch (error) { + throw new Error( + `Failed to write file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async atomicUpdateText( - relativePath: RelativePath, - updater: (current: TextWithCursors) => TextWithCursors - ): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); + public async atomicUpdateText( + relativePath: RelativePath, + updater: (current: TextWithCursors) => TextWithCursors + ): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); - try { - const currentContent = await fs.readFile(fullPath, "utf-8"); - const result = updater({ text: currentContent, cursors: [] }); - await fs.writeFile(fullPath, result.text, "utf-8"); - return result.text; - } catch (error) { - throw new Error( - `Failed to atomically update file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + try { + const currentContent = await fs.readFile(fullPath, "utf-8"); + const result = updater({ text: currentContent, cursors: [] }); + await fs.writeFile(fullPath, result.text, "utf-8"); + return result.text; + } catch (error) { + throw new Error( + `Failed to atomically update file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async getFileSize(relativePath: RelativePath): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - try { - const stats = await fs.stat(fullPath); - return stats.size; - } catch (error) { - throw new Error( - `Failed to get file size for ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + public async getFileSize(relativePath: RelativePath): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + try { + const stats = await fs.stat(fullPath); + return stats.size; + } catch (error) { + throw new Error( + `Failed to get file size for ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async exists(relativePath: RelativePath): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - try { - await fs.access(fullPath); - return true; - } catch { - return false; - } - } + public async exists(relativePath: RelativePath): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + try { + await fs.access(fullPath); + return true; + } catch { + return false; + } + } - public async createDirectory(relativePath: RelativePath): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - try { - await fs.mkdir(fullPath, { recursive: false }); - } catch (error) { - throw new Error( - `Failed to create directory ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + public async createDirectory(relativePath: RelativePath): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + try { + await fs.mkdir(fullPath, { recursive: false }); + } catch (error) { + throw new Error( + `Failed to create directory ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async delete(relativePath: RelativePath): Promise { - const fullPath = path.join( - this.basePath, - this.toNativePath(relativePath) - ); - try { - await fs.unlink(fullPath); - } catch (error) { - throw new Error( - `Failed to delete file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + public async delete(relativePath: RelativePath): Promise { + const fullPath = path.join( + this.basePath, + this.toNativePath(relativePath) + ); + try { + await fs.unlink(fullPath); + } catch (error) { + throw new Error( + `Failed to delete file ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - public async rename( - oldPath: RelativePath, - newPath: RelativePath - ): Promise { - const oldFullPath = path.join( - this.basePath, - this.toNativePath(oldPath) - ); - const newFullPath = path.join( - this.basePath, - this.toNativePath(newPath) - ); - const newDir = path.dirname(newFullPath); + public async rename( + oldPath: RelativePath, + newPath: RelativePath + ): Promise { + const oldFullPath = path.join( + this.basePath, + this.toNativePath(oldPath) + ); + const newFullPath = path.join( + this.basePath, + this.toNativePath(newPath) + ); + const newDir = path.dirname(newFullPath); - try { - await fs.mkdir(newDir, { recursive: true }); - await fs.rename(oldFullPath, newFullPath); - } catch (error) { - throw new Error( - `Failed to rename file from ${oldFullPath} to ${newFullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } - } + try { + await fs.mkdir(newDir, { recursive: true }); + await fs.rename(oldFullPath, newFullPath); + } catch (error) { + throw new Error( + `Failed to rename file from ${oldFullPath} to ${newFullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } - private async walkDirectory( - relativePath: string, - files: RelativePath[] - ): Promise { - const fullPath = path.join(this.basePath, relativePath); - let entries: Dirent[] = []; + private async walkDirectory( + relativePath: string, + files: RelativePath[] + ): Promise { + const fullPath = path.join(this.basePath, relativePath); + let entries: Dirent[] = []; - try { - entries = await fs.readdir(fullPath, { withFileTypes: true }); - } catch (error) { - throw new Error( - `Failed to read directory ${fullPath}: ${error instanceof Error ? error.message : String(error)}` - ); - } + try { + entries = await fs.readdir(fullPath, { withFileTypes: true }); + } catch (error) { + throw new Error( + `Failed to read directory ${fullPath}: ${error instanceof Error ? error.message : String(error)}` + ); + } - for (const entry of entries) { - const entryName = entry.name; - const entryRelativePath = path.join(relativePath, entryName); + for (const entry of entries) { + const entryName = entry.name; + const entryRelativePath = path.join(relativePath, entryName); - if (entry.isDirectory()) { - await this.walkDirectory(entryRelativePath, files); - } else if (entry.isFile()) { - // Always return forward slashes - files.push(this.toUnixPath(entryRelativePath)); - } - } - } + if (entry.isDirectory()) { + await this.walkDirectory(entryRelativePath, files); + } else if (entry.isFile()) { + // Always return forward slashes + files.push(this.toUnixPath(entryRelativePath)); + } + } + } - /** - * Convert a forward-slash path to native platform path separators - */ - private toNativePath(relativePath: string): string { - if (path.sep === "\\") { - return relativePath.replace(/\//g, "\\"); - } - return relativePath; - } + /** + * Convert a forward-slash path to native platform path separators + */ + private toNativePath(relativePath: string): string { + if (path.sep === "\\") { + return relativePath.replace(/\//g, "\\"); + } + return relativePath; + } - /** - * Convert a native platform path to forward slashes - */ - private toUnixPath(nativePath: string): string { - if (path.sep === "\\") { - return nativePath.replace(/\\/g, "/"); - } - return nativePath; - } + /** + * Convert a native platform path to forward slashes + */ + private toUnixPath(nativePath: string): string { + if (path.sep === "\\") { + return nativePath.replace(/\\/g, "/"); + } + return nativePath; + } } diff --git a/frontend/obsidian-plugin/.hotreload b/frontend/obsidian-plugin/.hotreload index e69de29b..8b137891 100644 --- a/frontend/obsidian-plugin/.hotreload +++ b/frontend/obsidian-plugin/.hotreload @@ -0,0 +1 @@ + diff --git a/frontend/obsidian-plugin/README.md b/frontend/obsidian-plugin/README.md index d7f694da..93c2cba7 100644 --- a/frontend/obsidian-plugin/README.md +++ b/frontend/obsidian-plugin/README.md @@ -85,8 +85,3 @@ If you have multiple URLs, you can also do: ## API Documentation See https://github.com/obsidianmd/obsidian-api - - - - - diff --git a/frontend/obsidian-plugin/manifest.json b/frontend/obsidian-plugin/manifest.json index 68d1568b..c8ee915b 100644 --- a/frontend/obsidian-plugin/manifest.json +++ b/frontend/obsidian-plugin/manifest.json @@ -1,10 +1,10 @@ { - "id": "vault-link", - "name": "VaultLink", - "version": "0.12.0", - "minAppVersion": "0.0.0", - "description": "Self-hosted synchronization and collaboration for your Vault.", - "author": "Andras Schmelczer", - "authorUrl": "https://schmelczer.dev", - "isDesktopOnly": false -} \ No newline at end of file + "id": "vault-link", + "name": "VaultLink", + "version": "0.12.0", + "minAppVersion": "0.0.0", + "description": "Self-hosted synchronization and collaboration for your Vault.", + "author": "Andras Schmelczer", + "authorUrl": "https://schmelczer.dev", + "isDesktopOnly": false +} diff --git a/frontend/obsidian-plugin/src/obsidian-file-system.ts b/frontend/obsidian-plugin/src/obsidian-file-system.ts index bc8265fd..ceb8bc2a 100644 --- a/frontend/obsidian-plugin/src/obsidian-file-system.ts +++ b/frontend/obsidian-plugin/src/obsidian-file-system.ts @@ -2,175 +2,175 @@ import type { Stat, Vault, Workspace } from "obsidian"; import { MarkdownView, normalizePath } from "obsidian"; import type { CursorPosition, TextWithCursors } from "sync-client"; import { - utils, - type FileSystemOperations, - type RelativePath + utils, + type FileSystemOperations, + type RelativePath } from "sync-client"; import { getSelectionsFromEditor } from "./views/cursors/get-selections-from-editor"; export class ObsidianFileSystemOperations implements FileSystemOperations { - public constructor( - private readonly vault: Vault, - private readonly workspace: Workspace - ) {} + public constructor( + private readonly vault: Vault, + private readonly workspace: Workspace + ) {} - public async listFilesRecursively( - root: RelativePath | undefined - ): Promise { - // Let's implement this by hand because vault.adapter.listAllFiles doesn't always return all files. - const allFiles = []; - const remainingFolders = [root ?? this.vault.getRoot().path]; + public async listFilesRecursively( + root: RelativePath | undefined + ): Promise { + // Let's implement this by hand because vault.adapter.listAllFiles doesn't always return all files. + const allFiles = []; + const remainingFolders = [root ?? this.vault.getRoot().path]; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - while (true) { - const folder = remainingFolders.pop(); - if (folder == undefined) { - break; - } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + while (true) { + const folder = remainingFolders.pop(); + if (folder == undefined) { + break; + } - // This would be a very bad idea to sync as it would mess with - // the integrity of the sync database. - if (folder.endsWith(".obsidian/plugins/vault-link/data.json")) { - continue; - } + // This would be a very bad idea to sync as it would mess with + // the integrity of the sync database. + if (folder.endsWith(".obsidian/plugins/vault-link/data.json")) { + continue; + } - const files = await this.vault.adapter.list(normalizePath(folder)); - allFiles.push(...files.files); - remainingFolders.push(...files.folders); - } + const files = await this.vault.adapter.list(normalizePath(folder)); + allFiles.push(...files.files); + remainingFolders.push(...files.folders); + } - return allFiles; - } + return allFiles; + } - public async read(path: RelativePath): Promise { - path = normalizePath(path); - const view = this.workspace.getActiveViewOfType(MarkdownView); - if (view?.file?.path === path) { - return new TextEncoder().encode(view.editor.getValue()); - } + public async read(path: RelativePath): Promise { + path = normalizePath(path); + const view = this.workspace.getActiveViewOfType(MarkdownView); + if (view?.file?.path === path) { + return new TextEncoder().encode(view.editor.getValue()); + } - return new Uint8Array(await this.vault.adapter.readBinary(path)); - } + return new Uint8Array(await this.vault.adapter.readBinary(path)); + } - public async write(path: RelativePath, content: Uint8Array): Promise { - path = normalizePath(path); + public async write(path: RelativePath, content: Uint8Array): Promise { + path = normalizePath(path); - const view = this.workspace.getActiveViewOfType(MarkdownView); - if (view?.file?.path === path) { - const position = view.editor.getCursor(); - view.editor.setValue(new TextDecoder().decode(content)); - view.editor.setCursor(position); - return; - } + const view = this.workspace.getActiveViewOfType(MarkdownView); + if (view?.file?.path === path) { + const position = view.editor.getCursor(); + view.editor.setValue(new TextDecoder().decode(content)); + view.editor.setCursor(position); + return; + } - return this.vault.adapter.writeBinary( - path, - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - content.buffer as ArrayBuffer - ); - } + return this.vault.adapter.writeBinary( + path, + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + content.buffer as ArrayBuffer + ); + } - public async atomicUpdateText( - path: RelativePath, - updater: (current: TextWithCursors) => TextWithCursors - ): Promise { - path = normalizePath(path); + public async atomicUpdateText( + path: RelativePath, + updater: (current: TextWithCursors) => TextWithCursors + ): Promise { + path = normalizePath(path); - const view = this.workspace.getActiveViewOfType(MarkdownView); + const view = this.workspace.getActiveViewOfType(MarkdownView); - if (view?.file?.path === path) { - const text = view.editor.getValue(); + if (view?.file?.path === path) { + const text = view.editor.getValue(); - const cursors: CursorPosition[] = getSelectionsFromEditor( - view.editor - ).flatMap(({ id, start: anchor, end: head }) => [ - { - id: 2 * id, - position: anchor - }, - { - id: 2 * id + 1, - position: head - } - ]); + const cursors: CursorPosition[] = getSelectionsFromEditor( + view.editor + ).flatMap(({ id, start: anchor, end: head }) => [ + { + id: 2 * id, + position: anchor + }, + { + id: 2 * id + 1, + position: head + } + ]); - const result = updater({ - text, - cursors - }); + const result = updater({ + text, + cursors + }); - if (result.text === text) { - return text; - } + if (result.text === text) { + return text; + } - view.editor.setValue(result.text); + view.editor.setValue(result.text); - const selections = []; - for (let i = 0; i < result.cursors.length / 2; i++) { - const from = result.cursors[2 * i]; - const to = result.cursors[2 * i + 1]; - const { line: fromLine, column: fromColumn } = - utils.positionToLineAndColumn(result.text, from.position); + const selections = []; + for (let i = 0; i < result.cursors.length / 2; i++) { + const from = result.cursors[2 * i]; + const to = result.cursors[2 * i + 1]; + const { line: fromLine, column: fromColumn } = + utils.positionToLineAndColumn(result.text, from.position); - const { line: toLine, column: toColumn } = - utils.positionToLineAndColumn(result.text, to.position); + const { line: toLine, column: toColumn } = + utils.positionToLineAndColumn(result.text, to.position); - selections.push({ - anchor: { line: fromLine, ch: fromColumn }, - head: { line: toLine, ch: toColumn } - }); - } - view.editor.setSelections(selections); + selections.push({ + anchor: { line: fromLine, ch: fromColumn }, + head: { line: toLine, ch: toColumn } + }); + } + view.editor.setSelections(selections); - return result.text; - } + return result.text; + } - return this.vault.adapter.process( - path, - (text) => - updater({ - text, - cursors: [] - }).text - ); - } + return this.vault.adapter.process( + path, + (text) => + updater({ + text, + cursors: [] + }).text + ); + } - public async getFileSize(path: RelativePath): Promise { - return (await this.statFile(path)).size; - } + public async getFileSize(path: RelativePath): Promise { + return (await this.statFile(path)).size; + } - public async getModificationTime(path: RelativePath): Promise { - return new Date((await this.statFile(path)).mtime); - } + public async getModificationTime(path: RelativePath): Promise { + return new Date((await this.statFile(path)).mtime); + } - public async exists(path: RelativePath): Promise { - return this.vault.adapter.exists(normalizePath(path)); - } + public async exists(path: RelativePath): Promise { + return this.vault.adapter.exists(normalizePath(path)); + } - public async createDirectory(path: RelativePath): Promise { - return this.vault.adapter.mkdir(normalizePath(path)); - } + public async createDirectory(path: RelativePath): Promise { + return this.vault.adapter.mkdir(normalizePath(path)); + } - public async delete(path: RelativePath): Promise { - if (!(await this.vault.adapter.trashSystem(normalizePath(path)))) { - return this.vault.adapter.remove(normalizePath(path)); - } - } + public async delete(path: RelativePath): Promise { + if (!(await this.vault.adapter.trashSystem(normalizePath(path)))) { + return this.vault.adapter.remove(normalizePath(path)); + } + } - public async rename( - oldPath: RelativePath, - newPath: RelativePath - ): Promise { - return this.vault.adapter.rename(oldPath, newPath); - } + public async rename( + oldPath: RelativePath, + newPath: RelativePath + ): Promise { + return this.vault.adapter.rename(oldPath, newPath); + } - private async statFile(path: string): Promise { - const file = await this.vault.adapter.stat(normalizePath(path)); + private async statFile(path: string): Promise { + const file = await this.vault.adapter.stat(normalizePath(path)); - if (!file) { - throw new Error(`File not found: ${path}`); - } + if (!file) { + throw new Error(`File not found: ${path}`); + } - return file; - } + return file; + } } diff --git a/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss b/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss index 90918b55..ebdd7730 100644 --- a/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss +++ b/frontend/obsidian-plugin/src/views/cursors/file-explorer.scss @@ -12,4 +12,4 @@ font-size: var(--font-smallest); font-style: italic; } -} \ No newline at end of file +} diff --git a/frontend/obsidian-plugin/src/views/cursors/get-selections-from-editor.ts b/frontend/obsidian-plugin/src/views/cursors/get-selections-from-editor.ts index 1635b930..3ddb60a3 100644 --- a/frontend/obsidian-plugin/src/views/cursors/get-selections-from-editor.ts +++ b/frontend/obsidian-plugin/src/views/cursors/get-selections-from-editor.ts @@ -2,16 +2,16 @@ import type { Editor } from "obsidian"; import { utils } from "sync-client"; export interface Selection { - id: number; - start: number; - end: number; + id: number; + start: number; + end: number; } export function getSelectionsFromEditor(editor: Editor): Selection[] { - const text = editor.getValue(); - return editor.listSelections().map(({ anchor, head }, i) => ({ - id: i, - start: utils.lineAndColumnToPosition(text, anchor.line, anchor.ch), - end: utils.lineAndColumnToPosition(text, head.line, head.ch) - })); + const text = editor.getValue(); + return editor.listSelections().map(({ anchor, head }, i) => ({ + id: i, + start: utils.lineAndColumnToPosition(text, anchor.line, anchor.ch), + end: utils.lineAndColumnToPosition(text, head.line, head.ch) + })); } diff --git a/frontend/obsidian-plugin/src/views/cursors/local-cursor-update-listener.ts b/frontend/obsidian-plugin/src/views/cursors/local-cursor-update-listener.ts index da67c70d..f1dba005 100644 --- a/frontend/obsidian-plugin/src/views/cursors/local-cursor-update-listener.ts +++ b/frontend/obsidian-plugin/src/views/cursors/local-cursor-update-listener.ts @@ -5,46 +5,46 @@ import type { Selection } from "./get-selections-from-editor"; import { getSelectionsFromEditor } from "./get-selections-from-editor"; export class LocalCursorUpdateListener { - private static readonly UPDATE_INTERVAL_MS = 50; - private readonly eventHandle: NodeJS.Timeout; + private static readonly UPDATE_INTERVAL_MS = 50; + private readonly eventHandle: NodeJS.Timeout; - public constructor( - private readonly client: SyncClient, - private readonly workspace: Workspace - ) { - this.eventHandle = setInterval(() => { - this.updateAllSelections(); - }, LocalCursorUpdateListener.UPDATE_INTERVAL_MS); - } + public constructor( + private readonly client: SyncClient, + private readonly workspace: Workspace + ) { + this.eventHandle = setInterval(() => { + this.updateAllSelections(); + }, LocalCursorUpdateListener.UPDATE_INTERVAL_MS); + } - public dispose(): void { - clearInterval(this.eventHandle); - } + public dispose(): void { + clearInterval(this.eventHandle); + } - private updateAllSelections(): void { - const currentCursors = this.getAllSelections(); - this.client - .updateLocalCursors(currentCursors) - .catch((error: unknown) => { - this.client.logger.error( - `Failed to update local cursors: ${error}` - ); - }); - } + private updateAllSelections(): void { + const currentCursors = this.getAllSelections(); + this.client + .updateLocalCursors(currentCursors) + .catch((error: unknown) => { + this.client.logger.error( + `Failed to update local cursors: ${error}` + ); + }); + } - private getAllSelections(): Record { - const cursors: Record = {}; - this.workspace - .getLeavesOfType("markdown") - .map((leaf) => leaf.view) - .filter((view) => view instanceof MarkdownView) - .forEach((view) => { - const { file } = view; - if (!file) { - return; - } - cursors[file.path] = getSelectionsFromEditor(view.editor); - }); - return cursors; - } + private getAllSelections(): Record { + const cursors: Record = {}; + this.workspace + .getLeavesOfType("markdown") + .map((leaf) => leaf.view) + .filter((view) => view instanceof MarkdownView) + .forEach((view) => { + const { file } = view; + if (!file) { + return; + } + cursors[file.path] = getSelectionsFromEditor(view.editor); + }); + return cursors; + } } diff --git a/frontend/obsidian-plugin/src/views/cursors/remote-cursor-theme.ts b/frontend/obsidian-plugin/src/views/cursors/remote-cursor-theme.ts index 3af2692d..e508b42f 100644 --- a/frontend/obsidian-plugin/src/views/cursors/remote-cursor-theme.ts +++ b/frontend/obsidian-plugin/src/views/cursors/remote-cursor-theme.ts @@ -4,60 +4,60 @@ const CARET_WIDTH = 2; const DOT_RADIUS = 4; export const remoteCursorsTheme = EditorView.baseTheme({ - ".selection-caret": { - position: "relative" - }, + ".selection-caret": { + position: "relative" + }, - ".selection-caret > *": { - position: "absolute", - backgroundColor: "inherit" - }, + ".selection-caret > *": { + position: "absolute", + backgroundColor: "inherit" + }, - ".selection-caret > .stick": { - left: 0, - top: 0, - transform: "translateX(-50%)", - width: `${CARET_WIDTH}px`, - height: "100%", - display: "block", - borderRadius: `${CARET_WIDTH / 2}px`, - animation: "blink-stick 1s steps(1) infinite" - }, + ".selection-caret > .stick": { + left: 0, + top: 0, + transform: "translateX(-50%)", + width: `${CARET_WIDTH}px`, + height: "100%", + display: "block", + borderRadius: `${CARET_WIDTH / 2}px`, + animation: "blink-stick 1s steps(1) infinite" + }, - "@keyframes blink-stick": { - "0%, 100%": { opacity: 1 }, - "50%": { opacity: 0 } - }, + "@keyframes blink-stick": { + "0%, 100%": { opacity: 1 }, + "50%": { opacity: 0 } + }, - ".selection-caret > .dot": { - borderRadius: "50%", - width: `${DOT_RADIUS * 2}px`, - height: `${DOT_RADIUS * 2}px`, - top: `-${DOT_RADIUS}px`, - left: `-${DOT_RADIUS}px`, - transition: "transform .3s ease-in-out", - transformOrigin: "bottom center", - boxSizing: "border-box" - }, + ".selection-caret > .dot": { + borderRadius: "50%", + width: `${DOT_RADIUS * 2}px`, + height: `${DOT_RADIUS * 2}px`, + top: `-${DOT_RADIUS}px`, + left: `-${DOT_RADIUS}px`, + transition: "transform .3s ease-in-out", + transformOrigin: "bottom center", + boxSizing: "border-box" + }, - ".selection-caret:hover > .dot": { - transform: "scale(0)" - }, + ".selection-caret:hover > .dot": { + transform: "scale(0)" + }, - ".selection-caret > .info": { - top: "-1.3em", - left: `-${CARET_WIDTH / 2}px`, - fontSize: "0.9em", - userSelect: "none", - color: "white", - padding: "0 2px", - transition: "opacity .3s ease-in-out", - opacity: 0, - whiteSpace: "nowrap", - borderRadius: "3px 3px 3px 0" - }, + ".selection-caret > .info": { + top: "-1.3em", + left: `-${CARET_WIDTH / 2}px`, + fontSize: "0.9em", + userSelect: "none", + color: "white", + padding: "0 2px", + transition: "opacity .3s ease-in-out", + opacity: 0, + whiteSpace: "nowrap", + borderRadius: "3px 3px 3px 0" + }, - ".selection-caret:hover > .info": { - opacity: 1 - } + ".selection-caret:hover > .info": { + opacity: 1 + } }); diff --git a/frontend/obsidian-plugin/src/views/cursors/remote-cursor-widget.ts b/frontend/obsidian-plugin/src/views/cursors/remote-cursor-widget.ts index e3273484..7f31ac00 100644 --- a/frontend/obsidian-plugin/src/views/cursors/remote-cursor-widget.ts +++ b/frontend/obsidian-plugin/src/views/cursors/remote-cursor-widget.ts @@ -1,46 +1,46 @@ import { AnnotationType, Annotation, RangeSet, Range } from "@codemirror/state"; import { - ViewUpdate, - ViewPlugin, - Decoration, - WidgetType + ViewUpdate, + ViewPlugin, + Decoration, + WidgetType } from "@codemirror/view"; import type { PluginValue, DecorationSet, EditorView } from "@codemirror/view"; export class RemoteCursorWidget extends WidgetType { - public constructor( - private readonly color: string, - private readonly name: string - ) { - super(); - } + public constructor( + private readonly color: string, + private readonly name: string + ) { + super(); + } - public toDOM(editor: EditorView): HTMLElement { - return editor.contentDOM.createEl( - "span", - { - cls: "selection-caret", - attr: { - style: `background-color: ${this.color}; border-color: ${this.color}` - } - }, - (span) => { - span.createEl("div", { - cls: "stick" - }); - span.createEl("div", { - cls: "dot" - }); - span.createEl("div", { - cls: "info", - text: this.name - }); - } - ); - } + public toDOM(editor: EditorView): HTMLElement { + return editor.contentDOM.createEl( + "span", + { + cls: "selection-caret", + attr: { + style: `background-color: ${this.color}; border-color: ${this.color}` + } + }, + (span) => { + span.createEl("div", { + cls: "stick" + }); + span.createEl("div", { + cls: "dot" + }); + span.createEl("div", { + cls: "info", + text: this.name + }); + } + ); + } - public eq(other: RemoteCursorWidget): boolean { - return other.color === this.color && other.name === this.name; - } + public eq(other: RemoteCursorWidget): boolean { + return other.color === this.color && other.name === this.name; + } } diff --git a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts index 5f867f90..1191d9a2 100644 --- a/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts +++ b/frontend/obsidian-plugin/src/views/cursors/remote-cursors-plugin.ts @@ -3,17 +3,17 @@ import { RangeSet } from "@codemirror/state"; import { ViewPlugin, Decoration } from "@codemirror/view"; import type { - PluginValue, - DecorationSet, - EditorView, - ViewUpdate + PluginValue, + DecorationSet, + EditorView, + ViewUpdate } from "@codemirror/view"; import { RemoteCursorWidget } from "./remote-cursor-widget"; import type { RelativePath } from "sync-client"; import { - utils, - type CursorSpan, - type MaybeOutdatedClientCursors + utils, + type CursorSpan, + type MaybeOutdatedClientCursors } from "sync-client"; import type { App } from "obsidian"; import { MarkdownView } from "obsidian"; @@ -25,241 +25,241 @@ import { reconcileWithHistory } from "reconcile-text"; const forceUpdate = StateEffect.define(); export class RemoteCursorsPluginValue implements PluginValue { - private static cursors: { - name: string; - path: string; - span: CursorSpan; - deviceId: string; - isOutdated: boolean; - }[] = []; + private static cursors: { + name: string; + path: string; + span: CursorSpan; + deviceId: string; + isOutdated: boolean; + }[] = []; - private static app?: App; - public decorations: DecorationSet = RangeSet.of([]); + private static app?: App; + public decorations: DecorationSet = RangeSet.of([]); - public static setCursors( - clients: MaybeOutdatedClientCursors[], - app: App - ): void { - RemoteCursorsPluginValue.app = app; - RemoteCursorsPluginValue.cursors = [ - ...RemoteCursorsPluginValue.cursors.filter(({ deviceId }) => - clients.some( - (client) => - client.deviceId === deviceId && client.isOutdated - ) - ), - ...clients - .filter( - ({ isOutdated, deviceId }) => - !isOutdated || - RemoteCursorsPluginValue.cursors.every( - (c) => deviceId !== c.deviceId - ) - ) - .flatMap((client) => { - const clientCursors = client.documentsWithCursors; - return clientCursors.flatMap((cursor) => - cursor.cursors.map((span) => ({ - name: client.userName, - path: cursor.relative_path, - deviceId: client.deviceId, - isOutdated: client.isOutdated, - span: { ...span } - })) - ); - }) - ]; + public static setCursors( + clients: MaybeOutdatedClientCursors[], + app: App + ): void { + RemoteCursorsPluginValue.app = app; + RemoteCursorsPluginValue.cursors = [ + ...RemoteCursorsPluginValue.cursors.filter(({ deviceId }) => + clients.some( + (client) => + client.deviceId === deviceId && client.isOutdated + ) + ), + ...clients + .filter( + ({ isOutdated, deviceId }) => + !isOutdated || + RemoteCursorsPluginValue.cursors.every( + (c) => deviceId !== c.deviceId + ) + ) + .flatMap((client) => { + const clientCursors = client.documentsWithCursors; + return clientCursors.flatMap((cursor) => + cursor.cursors.map((span) => ({ + name: client.userName, + path: cursor.relative_path, + deviceId: client.deviceId, + isOutdated: client.isOutdated, + span: { ...span } + })) + ); + }) + ]; - app.workspace - .getLeavesOfType("markdown") - .map((leaf) => leaf.view) - .filter((view) => view instanceof MarkdownView) - .forEach((view) => { - // @ts-expect-error, not typed - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const editor = view.editor.cm as EditorView; + app.workspace + .getLeavesOfType("markdown") + .map((leaf) => leaf.view) + .filter((view) => view instanceof MarkdownView) + .forEach((view) => { + // @ts-expect-error, not typed + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + const editor = view.editor.cm as EditorView; - editor.dispatch({ - effects: [forceUpdate.of(null)] - }); - }); - } + editor.dispatch({ + effects: [forceUpdate.of(null)] + }); + }); + } - private static findFileForEditor( - editor: EditorView - ): RelativePath | undefined { - return RemoteCursorsPluginValue.app?.workspace - .getLeavesOfType("markdown") - .map((leaf) => leaf.view) - .filter((view) => view instanceof MarkdownView) - .flatMap((view) => { - // @ts-expect-error, not typed - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - if ((view.editor.cm as EditorView) !== editor) { - return []; - } + private static findFileForEditor( + editor: EditorView + ): RelativePath | undefined { + return RemoteCursorsPluginValue.app?.workspace + .getLeavesOfType("markdown") + .map((leaf) => leaf.view) + .filter((view) => view instanceof MarkdownView) + .flatMap((view) => { + // @ts-expect-error, not typed + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + if ((view.editor.cm as EditorView) !== editor) { + return []; + } - const { file } = view; - if (!file) { - return; - } + const { file } = view; + if (!file) { + return; + } - return [file.path]; - }) - .first(); - } + return [file.path]; + }) + .first(); + } - private static interpolateRemoteCursorPositions( - original: string, - edited: string - ): void { - if ( - original === edited || - RemoteCursorsPluginValue.cursors.length === 0 - ) { - return; - } + private static interpolateRemoteCursorPositions( + original: string, + edited: string + ): void { + if ( + original === edited || + RemoteCursorsPluginValue.cursors.length === 0 + ) { + return; + } - const updatedPositions: number[] = []; - const reconciled = reconcileWithHistory( - original, - { - text: original, - cursors: RemoteCursorsPluginValue.cursors.flatMap( - ({ span }, i) => [ - { id: i * 2, position: span.start }, - { id: i * 2 + 1, position: span.end } - ] - ) - }, - edited - ); + const updatedPositions: number[] = []; + const reconciled = reconcileWithHistory( + original, + { + text: original, + cursors: RemoteCursorsPluginValue.cursors.flatMap( + ({ span }, i) => [ + { id: i * 2, position: span.start }, + { id: i * 2 + 1, position: span.end } + ] + ) + }, + edited + ); - reconciled.cursors.forEach(({ id, position }) => { - const whereToJump = RemoteCursorsPluginValue.findWhereToMoveCursor( - position, - reconciled.history - ); - if (whereToJump !== null) { - updatedPositions[id] = whereToJump; - } else { - updatedPositions[id] = position; - } - }); + reconciled.cursors.forEach(({ id, position }) => { + const whereToJump = RemoteCursorsPluginValue.findWhereToMoveCursor( + position, + reconciled.history + ); + if (whereToJump !== null) { + updatedPositions[id] = whereToJump; + } else { + updatedPositions[id] = position; + } + }); - RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { - span.start = updatedPositions[i * 2]; - span.end = updatedPositions[i * 2 + 1]; - }); - } + RemoteCursorsPluginValue.cursors.forEach(({ span }, i) => { + span.start = updatedPositions[i * 2]; + span.end = updatedPositions[i * 2 + 1]; + }); + } - private static findWhereToMoveCursor( - cursor: number, - spans: SpanWithHistory[] - ): number | null { - let position = 0; - for (const span of spans) { - // left and origin are the same - if (position === cursor && span.history === "AddedFromRight") { - return position + span.text.length; - } - position += span.text.length; - if (position === cursor && span.history === "RemovedFromRight") { - return position - span.text.length; - } - } + private static findWhereToMoveCursor( + cursor: number, + spans: SpanWithHistory[] + ): number | null { + let position = 0; + for (const span of spans) { + // left and origin are the same + if (position === cursor && span.history === "AddedFromRight") { + return position + span.text.length; + } + position += span.text.length; + if (position === cursor && span.history === "RemovedFromRight") { + return position - span.text.length; + } + } - return null; - } + return null; + } - public update(update: ViewUpdate): void { - const original = update.startState.doc.toString(); - const edited = update.state.doc.toString(); + public update(update: ViewUpdate): void { + const original = update.startState.doc.toString(); + const edited = update.state.doc.toString(); - RemoteCursorsPluginValue.interpolateRemoteCursorPositions( - original, - edited - ); + RemoteCursorsPluginValue.interpolateRemoteCursorPositions( + original, + edited + ); - const decorations: Range[] = []; - const relative_path = RemoteCursorsPluginValue.findFileForEditor( - update.view - ); - RemoteCursorsPluginValue.cursors - .filter(({ path }) => path == relative_path) - .forEach(({ name, span: { start, end } }) => { - const color = utils.getRandomColor(name); - const startLine = update.view.state.doc.lineAt(start); - const endLine = update.view.state.doc.lineAt(end); + const decorations: Range[] = []; + const relative_path = RemoteCursorsPluginValue.findFileForEditor( + update.view + ); + RemoteCursorsPluginValue.cursors + .filter(({ path }) => path == relative_path) + .forEach(({ name, span: { start, end } }) => { + const color = utils.getRandomColor(name); + const startLine = update.view.state.doc.lineAt(start); + const endLine = update.view.state.doc.lineAt(end); - const attributes = { - style: `background-color: ${color};` - }; + const attributes = { + style: `background-color: ${color};` + }; - if (startLine.number === endLine.number) { - // selected content in a single line. - decorations.push({ - from: start, - to: end, - value: Decoration.mark({ - attributes - }) - }); - } else { - // selected content in multiple lines - // first, render text-selection in the first line - decorations.push({ - from: start, - to: startLine.from + startLine.length, - value: Decoration.mark({ - attributes - }) - }); + if (startLine.number === endLine.number) { + // selected content in a single line. + decorations.push({ + from: start, + to: end, + value: Decoration.mark({ + attributes + }) + }); + } else { + // selected content in multiple lines + // first, render text-selection in the first line + decorations.push({ + from: start, + to: startLine.from + startLine.length, + value: Decoration.mark({ + attributes + }) + }); - // render text-selection in the lines between the first and last line - for ( - let i = startLine.number + 1; - i < endLine.number; - i++ - ) { - const currentLine = update.view.state.doc.line(i); - decorations.push({ - from: currentLine.from, - to: currentLine.to, - value: Decoration.mark({ - attributes - }) - }); - } + // render text-selection in the lines between the first and last line + for ( + let i = startLine.number + 1; + i < endLine.number; + i++ + ) { + const currentLine = update.view.state.doc.line(i); + decorations.push({ + from: currentLine.from, + to: currentLine.to, + value: Decoration.mark({ + attributes + }) + }); + } - // render text-selection in the last line - decorations.push({ - from: endLine.from, - to: end, - value: Decoration.mark({ - attributes - }) - }); - } + // render text-selection in the last line + decorations.push({ + from: endLine.from, + to: end, + value: Decoration.mark({ + attributes + }) + }); + } - decorations.push({ - from: end, - to: end, - value: Decoration.widget({ - side: end - start > 0 ? -1 : 1, // the local cursor should be rendered outside the remote selection - block: false, - widget: new RemoteCursorWidget(color, name) - }) - }); - }); + decorations.push({ + from: end, + to: end, + value: Decoration.widget({ + side: end - start > 0 ? -1 : 1, // the local cursor should be rendered outside the remote selection + block: false, + widget: new RemoteCursorWidget(color, name) + }) + }); + }); - this.decorations = Decoration.set(decorations, true); - } + this.decorations = Decoration.set(decorations, true); + } } export const remoteCursorsPlugin = ViewPlugin.fromClass( - RemoteCursorsPluginValue, - { - decorations: (v) => v.decorations - } + RemoteCursorsPluginValue, + { + decorations: (v) => v.decorations + } ); diff --git a/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.scss b/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.scss index a430ac3b..8cc530d2 100644 --- a/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.scss +++ b/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.scss @@ -1,43 +1,43 @@ .vault-link-sync-status { - position: absolute; - right: var(--size-4-4); - top: var(--size-4-2); - opacity: 0.7; - cursor: pointer; + position: absolute; + right: var(--size-4-4); + top: var(--size-4-2); + opacity: 0.7; + cursor: pointer; - > span { - opacity: 0; - position: absolute; - min-width: 200px; - text-align: right; - padding-right: var(--size-2-2); + > span { + opacity: 0; + position: absolute; + min-width: 200px; + text-align: right; + padding-right: var(--size-2-2); - top: 50%; - left: 0; - transform: translateY(-50%) translateX(-100%) translateY(-2px); - transition: opacity 200ms; - } + top: 50%; + left: 0; + transform: translateY(-50%) translateX(-100%) translateY(-2px); + transition: opacity 200ms; + } - &:hover { - > span { - opacity: 1; - } - } + &:hover { + > span { + opacity: 1; + } + } - > .icon { - line-height: 0; - } + > .icon { + line-height: 0; + } - &.loading > .icon { - animation: spin 2s linear infinite; + &.loading > .icon { + animation: spin 2s linear infinite; - @keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } - } - } + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + } } diff --git a/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.ts b/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.ts index 0725c1ea..1010c7e3 100644 --- a/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.ts +++ b/frontend/obsidian-plugin/src/views/editor-status-display-manager/editor-status-display-manager.ts @@ -7,91 +7,91 @@ import type VaultLinkPlugin from "src/vault-link-plugin"; import { HistoryView } from "../history/history-view"; export class EditorStatusDisplayManager { - private static readonly UPDATE_INTERVAL_IN_MS = 100; + private static readonly UPDATE_INTERVAL_IN_MS = 100; - private readonly intervalId: NodeJS.Timeout; - private readonly lastStatuses = new Map(); + private readonly intervalId: NodeJS.Timeout; + private readonly lastStatuses = new Map(); - public constructor( - private readonly plugin: VaultLinkPlugin, - private readonly workspace: Workspace, - private readonly client: SyncClient - ) { - this.intervalId = setInterval(() => { - this.updateEditorStatusDisplay(); - }, EditorStatusDisplayManager.UPDATE_INTERVAL_IN_MS); - } + public constructor( + private readonly plugin: VaultLinkPlugin, + private readonly workspace: Workspace, + private readonly client: SyncClient + ) { + this.intervalId = setInterval(() => { + this.updateEditorStatusDisplay(); + }, EditorStatusDisplayManager.UPDATE_INTERVAL_IN_MS); + } - public dispose(): void { - clearInterval(this.intervalId); - } + public dispose(): void { + clearInterval(this.intervalId); + } - private updateEditorStatusDisplay(): void { - this.workspace.iterateAllLeaves((leaf) => { - if (leaf.view instanceof FileView) { - const filePath = leaf.view.file?.path; - if (filePath == null) { - return; - } + private updateEditorStatusDisplay(): void { + this.workspace.iterateAllLeaves((leaf) => { + if (leaf.view instanceof FileView) { + const filePath = leaf.view.file?.path; + if (filePath == null) { + return; + } - const element = this.getElementFromLeaf(leaf.view); - if (element == null) { - return; - } + const element = this.getElementFromLeaf(leaf.view); + if (element == null) { + return; + } - const previousStatus = this.lastStatuses.get(filePath); - const currentStatus = - this.client.getDocumentSyncingStatus(filePath); - if (previousStatus === currentStatus) { - return; - } - this.lastStatuses.set(filePath, currentStatus); + const previousStatus = this.lastStatuses.get(filePath); + const currentStatus = + this.client.getDocumentSyncingStatus(filePath); + if (previousStatus === currentStatus) { + return; + } + this.lastStatuses.set(filePath, currentStatus); - if (currentStatus == DocumentSyncStatus.SYNCING_IS_DISABLED) { - element.remove(); - return; - } + if (currentStatus == DocumentSyncStatus.SYNCING_IS_DISABLED) { + element.remove(); + return; + } - if (currentStatus == DocumentSyncStatus.SYNCING) { - element.classList.add("loading"); - } else { - element.classList.remove("loading"); - } + if (currentStatus == DocumentSyncStatus.SYNCING) { + element.classList.add("loading"); + } else { + element.classList.remove("loading"); + } - const iconContainer = element.querySelector(".icon"); - if (iconContainer != null) { - setIcon( - iconContainer as HTMLElement, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - currentStatus == DocumentSyncStatus.SYNCING - ? "loader" - : "circle-check" - ); - } - } - }); - } + const iconContainer = element.querySelector(".icon"); + if (iconContainer != null) { + setIcon( + iconContainer as HTMLElement, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + currentStatus == DocumentSyncStatus.SYNCING + ? "loader" + : "circle-check" + ); + } + } + }); + } - private getElementFromLeaf(fileView: FileView): Element | undefined { - const parent = fileView.contentEl.querySelector(".cm-editor"); - if (parent == null) { - return; - } + private getElementFromLeaf(fileView: FileView): Element | undefined { + const parent = fileView.contentEl.querySelector(".cm-editor"); + if (parent == null) { + return; + } - return ( - parent.querySelector(".vault-link-sync-status") ?? - parent.createDiv( - { - cls: "vault-link-sync-status" - }, - (el) => { - el.createSpan({ text: "VaultLink sync state" }); - el.createDiv({ - cls: "icon" - }); - el.onclick = async (): Promise => - this.plugin.activateView(HistoryView.TYPE); - } - ) - ); - } + return ( + parent.querySelector(".vault-link-sync-status") ?? + parent.createDiv( + { + cls: "vault-link-sync-status" + }, + (el) => { + el.createSpan({ text: "VaultLink sync state" }); + el.createDiv({ + cls: "icon" + }); + el.onclick = async (): Promise => + this.plugin.activateView(HistoryView.TYPE); + } + ) + ); + } } diff --git a/frontend/obsidian-plugin/src/views/history/history-view.scss b/frontend/obsidian-plugin/src/views/history/history-view.scss index fb93fa30..4e8b2a96 100644 --- a/frontend/obsidian-plugin/src/views/history/history-view.scss +++ b/frontend/obsidian-plugin/src/views/history/history-view.scss @@ -1,61 +1,61 @@ .history-card { - padding: var(--size-4-4); - margin: var(--size-4-2); - background-color: var(--color-base-00); - border-radius: var(--radius-l); - container-type: inline-size; - word-break: break-word; + padding: var(--size-4-4); + margin: var(--size-4-2); + background-color: var(--color-base-00); + border-radius: var(--radius-l); + container-type: inline-size; + word-break: break-word; - &.clickable { - cursor: pointer; - } + &.clickable { + cursor: pointer; + } - &.success { - background-color: rgba(var(--color-green-rgb), 0.2); - } + &.success { + background-color: rgba(var(--color-green-rgb), 0.2); + } - &.error { - background-color: rgba(var(--color-red-rgb), 0.2); - } + &.error { + background-color: rgba(var(--color-red-rgb), 0.2); + } - &.skipped { - background-color: rgba(var(--color-green-rgb), 0.08); - } + &.skipped { + background-color: rgba(var(--color-green-rgb), 0.08); + } - .history-card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--size-4-2); - gap: var(--size-4-2); + .history-card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--size-4-2); + gap: var(--size-4-2); - @container (max-width: 300px) { - flex-direction: column; - align-items: flex-start; - } + @container (max-width: 300px) { + flex-direction: column; + align-items: flex-start; + } - .history-card-title { - font: var(--font-monospace); - display: flex; - align-items: center; - gap: var(--size-4-2); - margin: 0; + .history-card-title { + font: var(--font-monospace); + display: flex; + align-items: center; + gap: var(--size-4-2); + margin: 0; - > span { - margin-bottom: var(--size-4-1); - } - } + > span { + margin-bottom: var(--size-4-1); + } + } - .history-card-timestamp { - font-size: var(--font-ui-small); - font-style: italic; - color: var(--italic-color); - } - } + .history-card-timestamp { + font-size: var(--font-ui-small); + font-style: italic; + color: var(--italic-color); + } + } - .history-card-message { - font-size: var(--font-ui-medium); - color: var(--color-base-70); - margin: 0; - } + .history-card-message { + font-size: var(--font-ui-medium); + color: var(--color-base-70); + margin: 0; + } } diff --git a/frontend/obsidian-plugin/src/views/history/history-view.ts b/frontend/obsidian-plugin/src/views/history/history-view.ts index 1094e575..1fc2c91e 100644 --- a/frontend/obsidian-plugin/src/views/history/history-view.ts +++ b/frontend/obsidian-plugin/src/views/history/history-view.ts @@ -7,234 +7,234 @@ import type { HistoryEntry, SyncClient } from "sync-client"; import { SyncType } from "sync-client"; export class HistoryView extends ItemView { - public static readonly TYPE = "history-view"; - public static readonly ICON = "square-stack"; - private timer: NodeJS.Timeout | null = null; + public static readonly TYPE = "history-view"; + public static readonly ICON = "square-stack"; + private timer: NodeJS.Timeout | null = null; - private historyContainer: HTMLElement | undefined; - private readonly historyEntryToElement = new Map< - HistoryEntry, - HTMLElement - >(); + private historyContainer: HTMLElement | undefined; + private readonly historyEntryToElement = new Map< + HistoryEntry, + HTMLElement + >(); - public constructor( - private readonly client: SyncClient, - leaf: WorkspaceLeaf - ) { - super(leaf); - this.icon = HistoryView.ICON; + public constructor( + private readonly client: SyncClient, + leaf: WorkspaceLeaf + ) { + super(leaf); + this.icon = HistoryView.ICON; - this.client.addSyncHistoryUpdateListener(async () => - this.updateView().catch((error: unknown) => { - this.client.logger.error( - `Failed to update history view: ${error}` - ); - }) - ); - } + this.client.addSyncHistoryUpdateListener(async () => + this.updateView().catch((error: unknown) => { + this.client.logger.error( + `Failed to update history view: ${error}` + ); + }) + ); + } - private static getSyncTypeIcon(type: SyncType | undefined): IconName { - switch (type) { - case SyncType.CREATE: - return "file-plus"; - case SyncType.DELETE: - return "trash-2"; - case SyncType.UPDATE: - return "file-pen-line"; - case SyncType.MOVE: - return "move-right"; - case SyncType.SKIPPED: - return "circle-slash"; - case undefined: - default: - return ""; - } - } + private static getSyncTypeIcon(type: SyncType | undefined): IconName { + switch (type) { + case SyncType.CREATE: + return "file-plus"; + case SyncType.DELETE: + return "trash-2"; + case SyncType.UPDATE: + return "file-pen-line"; + case SyncType.MOVE: + return "move-right"; + case SyncType.SKIPPED: + return "circle-slash"; + case undefined: + default: + return ""; + } + } - private static renderSyncItemTitle( - element: HTMLElement, - entry: HistoryEntry - ): void { - const syncTypeIcon = HistoryView.getSyncTypeIcon(entry.details.type); - if (syncTypeIcon) { - setIcon(element.createDiv(), syncTypeIcon); - } + private static renderSyncItemTitle( + element: HTMLElement, + entry: HistoryEntry + ): void { + const syncTypeIcon = HistoryView.getSyncTypeIcon(entry.details.type); + if (syncTypeIcon) { + setIcon(element.createDiv(), syncTypeIcon); + } - let fileName = entry.details.relativePath.split("/").pop() ?? ""; - fileName = fileName.replace(/\.md$/, ""); + let fileName = entry.details.relativePath.split("/").pop() ?? ""; + fileName = fileName.replace(/\.md$/, ""); - element.createEl("span", { - text: - entry.details.type === SyncType.SKIPPED - ? `Skipped: ${fileName}` - : fileName - }); - } + element.createEl("span", { + text: + entry.details.type === SyncType.SKIPPED + ? `Skipped: ${fileName}` + : fileName + }); + } - private static updateTimeSince( - element: HTMLElement, - entry: HistoryEntry - ): void { - const timestampElement = element.querySelector( - ".history-card-timestamp" - ); + private static updateTimeSince( + element: HTMLElement, + entry: HistoryEntry + ): void { + const timestampElement = element.querySelector( + ".history-card-timestamp" + ); - if (timestampElement != null) { - timestampElement.textContent = - HistoryView.getTimestampAndAuthor(entry); - } - } + if (timestampElement != null) { + timestampElement.textContent = + HistoryView.getTimestampAndAuthor(entry); + } + } - private static getTimestampAndAuthor(entry: HistoryEntry): string { - let content = intlFormatDistance(entry.timestamp, new Date()); - if ("author" in entry && entry.author !== undefined) { - content += ` by ${entry.author}`; - } - return content; - } + private static getTimestampAndAuthor(entry: HistoryEntry): string { + let content = intlFormatDistance(entry.timestamp, new Date()); + if ("author" in entry && entry.author !== undefined) { + content += ` by ${entry.author}`; + } + return content; + } - public getViewType(): string { - return HistoryView.TYPE; - } + public getViewType(): string { + return HistoryView.TYPE; + } - public getDisplayText(): string { - return "VaultLink history"; - } + public getDisplayText(): string { + return "VaultLink history"; + } - public async onOpen(): Promise { - const container = this.containerEl.children[1]; - container.createEl("h4", { text: "VaultLink history" }); + public async onOpen(): Promise { + const container = this.containerEl.children[1]; + container.createEl("h4", { text: "VaultLink history" }); - this.historyContainer = container.createDiv({ cls: "logs-container" }); + this.historyContainer = container.createDiv({ cls: "logs-container" }); - await this.updateView(); - this.clearTimer(); - this.timer = setInterval( - () => - void this.updateView().catch((error: unknown) => { - this.client.logger.error( - `Failed to update history view: ${error}` - ); - }), - 1000 - ); - } + await this.updateView(); + this.clearTimer(); + this.timer = setInterval( + () => + void this.updateView().catch((error: unknown) => { + this.client.logger.error( + `Failed to update history view: ${error}` + ); + }), + 1000 + ); + } - public async onClose(): Promise { - this.clearTimer(); - } + public async onClose(): Promise { + this.clearTimer(); + } - private clearTimer(): void { - if (this.timer) { - clearInterval(this.timer); - this.timer = null; - } - } + private clearTimer(): void { + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + } - private async updateView(): Promise { - const container = this.historyContainer; - if (container === undefined) { - return; - } + private async updateView(): Promise { + const container = this.historyContainer; + if (container === undefined) { + return; + } - // entries are newest first, but we prepend new ones - const entries = this.client.getHistoryEntries().toReversed(); + // entries are newest first, but we prepend new ones + const entries = this.client.getHistoryEntries().toReversed(); - if (this.historyEntryToElement.size === 0 && entries.length > 0) { - // Clear the "No update has happened yet" message - container.empty(); - } + if (this.historyEntryToElement.size === 0 && entries.length > 0) { + // Clear the "No update has happened yet" message + container.empty(); + } - entries.forEach((entry) => { - const element = this.historyEntryToElement.get(entry); - if (element !== undefined) { - HistoryView.updateTimeSince(element, entry); - return; - } + entries.forEach((entry) => { + const element = this.historyEntryToElement.get(entry); + if (element !== undefined) { + HistoryView.updateTimeSince(element, entry); + return; + } - const newElement = this.createHistoryCard(container, entry); - container.prepend(newElement); - this.historyEntryToElement.set(entry, newElement); - }); + const newElement = this.createHistoryCard(container, entry); + container.prepend(newElement); + this.historyEntryToElement.set(entry, newElement); + }); - const newEntries = new Set(entries); - for (const [entry, element] of this.historyEntryToElement) { - if (!newEntries.has(entry)) { - element.remove(); - this.historyEntryToElement.delete(entry); - } - } + const newEntries = new Set(entries); + for (const [entry, element] of this.historyEntryToElement) { + if (!newEntries.has(entry)) { + element.remove(); + this.historyEntryToElement.delete(entry); + } + } - if (entries.length === 0) { - container.empty(); - container.createEl("p", { - text: "No update has happened yet." - }); - } - } + if (entries.length === 0) { + container.empty(); + container.createEl("p", { + text: "No update has happened yet." + }); + } + } - private createHistoryCard( - container: HTMLElement, - entry: HistoryEntry - ): HTMLElement { - return container.createDiv( - { - cls: ["history-card", entry.status.toLocaleLowerCase()] - }, - (card) => { - if ( - this.app.vault.getFileByPath(entry.details.relativePath) != - null - ) { - card.addEventListener("click", () => { - this.app.workspace - .openLinkText( - entry.details.relativePath, - entry.details.relativePath, - false - ) - .catch((error: unknown) => { - this.client.logger.error( - `Failed to open link for ${entry.details.relativePath}: ${error}` - ); - }); - }); + private createHistoryCard( + container: HTMLElement, + entry: HistoryEntry + ): HTMLElement { + return container.createDiv( + { + cls: ["history-card", entry.status.toLocaleLowerCase()] + }, + (card) => { + if ( + this.app.vault.getFileByPath(entry.details.relativePath) != + null + ) { + card.addEventListener("click", () => { + this.app.workspace + .openLinkText( + entry.details.relativePath, + entry.details.relativePath, + false + ) + .catch((error: unknown) => { + this.client.logger.error( + `Failed to open link for ${entry.details.relativePath}: ${error}` + ); + }); + }); - card.addClass("clickable"); - } + card.addClass("clickable"); + } - card.createDiv( - { - cls: "history-card-header" - }, - (header) => { - header.createEl( - "h5", - { - cls: "history-card-title" - }, - (title) => { - HistoryView.renderSyncItemTitle(title, entry); - } - ); + card.createDiv( + { + cls: "history-card-header" + }, + (header) => { + header.createEl( + "h5", + { + cls: "history-card-title" + }, + (title) => { + HistoryView.renderSyncItemTitle(title, entry); + } + ); - header.createSpan({ - text: HistoryView.getTimestampAndAuthor(entry), - cls: "history-card-timestamp" - }); - } - ); + header.createSpan({ + text: HistoryView.getTimestampAndAuthor(entry), + cls: "history-card-timestamp" + }); + } + ); - const body = - entry.details.type === SyncType.MOVE - ? `${entry.message}. Moved from '${entry.details.movedFrom}' to '${entry.details.relativePath}'` - : `${entry.message}.`; + const body = + entry.details.type === SyncType.MOVE + ? `${entry.message}. Moved from '${entry.details.movedFrom}' to '${entry.details.relativePath}'` + : `${entry.message}.`; - card.createEl("p", { - text: body, - cls: "history-card-message" - }); - } - ); - } + card.createEl("p", { + text: body, + cls: "history-card-message" + }); + } + ); + } } diff --git a/frontend/obsidian-plugin/src/views/logs/logs-view.scss b/frontend/obsidian-plugin/src/views/logs/logs-view.scss index 2bffe693..47b4cb29 100644 --- a/frontend/obsidian-plugin/src/views/logs/logs-view.scss +++ b/frontend/obsidian-plugin/src/views/logs/logs-view.scss @@ -1,74 +1,74 @@ .logs-view { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; - .verbosity-selector { - display: flex; - align-items: center; - justify-content: space-between; - font-weight: normal; - gap: var(--size-4-2); - margin: var(--size-4-4) var(--size-4-2); + .verbosity-selector { + display: flex; + align-items: center; + justify-content: space-between; + font-weight: normal; + gap: var(--size-4-2); + margin: var(--size-4-4) var(--size-4-2); - h4 { - margin: 0; - } + h4 { + margin: 0; + } - .logs-controls { - display: flex; - align-items: center; - gap: var(--size-4-2); + .logs-controls { + display: flex; + align-items: center; + gap: var(--size-4-2); - button { - display: flex; - align-items: center; - gap: var(--size-2-1); - padding: var(--size-2-2) var(--size-4-2); - cursor: pointer; - } + button { + display: flex; + align-items: center; + gap: var(--size-2-1); + padding: var(--size-2-2) var(--size-4-2); + cursor: pointer; + } - select { - cursor: pointer; - } - } - } + select { + cursor: pointer; + } + } + } - .logs-container { - max-width: 100%; - overflow-y: auto; + .logs-container { + max-width: 100%; + overflow-y: auto; - .log-message { - font: var(--font-monospace); - margin-bottom: var(--size-2-1); - overflow-wrap: break-word; - white-space: pre-wrap; - user-select: all; + .log-message { + font: var(--font-monospace); + margin-bottom: var(--size-2-1); + overflow-wrap: break-word; + white-space: pre-wrap; + user-select: all; - .timestamp { - padding: var(--size-2-1) var(--size-4-1); - border-radius: var(--radius-s); - background-color: var(--color-base-30); - font-size: var(--font-ui-small); - font-family: var(--font-monospace); - font-weight: var(--bold-weight); - margin-right: var(--size-4-1); - } + .timestamp { + padding: var(--size-2-1) var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--color-base-30); + font-size: var(--font-ui-small); + font-family: var(--font-monospace); + font-weight: var(--bold-weight); + margin-right: var(--size-4-1); + } - &.DEBUG { - color: var(--color-base-50); - } + &.DEBUG { + color: var(--color-base-50); + } - &.INFO { - color: var(--color-base-100); - } + &.INFO { + color: var(--color-base-100); + } - &.WARNING { - color: rgb(var(--color-yellow-rgb)); - } + &.WARNING { + color: rgb(var(--color-yellow-rgb)); + } - &.ERROR { - color: rgb(var(--color-red-rgb)); - } - } - } + &.ERROR { + color: rgb(var(--color-red-rgb)); + } + } + } } diff --git a/frontend/obsidian-plugin/src/views/logs/logs-view.ts b/frontend/obsidian-plugin/src/views/logs/logs-view.ts index 395cfe09..927dc9b7 100644 --- a/frontend/obsidian-plugin/src/views/logs/logs-view.ts +++ b/frontend/obsidian-plugin/src/views/logs/logs-view.ts @@ -6,189 +6,189 @@ import type { LogLine } from "sync-client"; import { LogLevel, type SyncClient } from "sync-client"; export class LogsView extends ItemView { - public static readonly TYPE = "logs-view"; - public static readonly ICON = "logs"; + public static readonly TYPE = "logs-view"; + public static readonly ICON = "logs"; - private static readonly MAX_OFFSET_FROM_BOTTOM_WITH_AUTO_SCROLL_PX = 300; + private static readonly MAX_OFFSET_FROM_BOTTOM_WITH_AUTO_SCROLL_PX = 300; - private logsContainer: HTMLElement | undefined; - private readonly logLineToElement = new Map(); - private minLogLevel: LogLevel = LogLevel.INFO; + private logsContainer: HTMLElement | undefined; + private readonly logLineToElement = new Map(); + private minLogLevel: LogLevel = LogLevel.INFO; - public constructor( - private readonly client: SyncClient, - leaf: WorkspaceLeaf - ) { - super(leaf); - this.icon = LogsView.ICON; - this.client.logger.addOnMessageListener(() => { - this.updateView(); - }); - } + public constructor( + private readonly client: SyncClient, + leaf: WorkspaceLeaf + ) { + super(leaf); + this.icon = LogsView.ICON; + this.client.logger.addOnMessageListener(() => { + this.updateView(); + }); + } - private static createLogLineElement( - container: HTMLElement, - logLine: LogLine - ): HTMLElement { - return container.createDiv( - { - cls: ["log-message", logLine.level] - }, - (messageContainer) => { - messageContainer.createEl("span", { - text: LogsView.formatTimestamp(logLine.timestamp), - cls: "timestamp" - }); - messageContainer.createEl("span", { - text: logLine.message - }); - } - ); - } + private static createLogLineElement( + container: HTMLElement, + logLine: LogLine + ): HTMLElement { + return container.createDiv( + { + cls: ["log-message", logLine.level] + }, + (messageContainer) => { + messageContainer.createEl("span", { + text: LogsView.formatTimestamp(logLine.timestamp), + cls: "timestamp" + }); + messageContainer.createEl("span", { + text: logLine.message + }); + } + ); + } - private static formatTimestamp(timestamp: Date): string { - return timestamp.toTimeString().split(" ")[0]; - } + private static formatTimestamp(timestamp: Date): string { + return timestamp.toTimeString().split(" ")[0]; + } - public getViewType(): string { - return LogsView.TYPE; - } + public getViewType(): string { + return LogsView.TYPE; + } - public getDisplayText(): string { - return "VaultLink logs"; - } + public getDisplayText(): string { + return "VaultLink logs"; + } - public async onOpen(): Promise { - const container = this.containerEl.children[1]; - container.addClass("logs-view"); + public async onOpen(): Promise { + const container = this.containerEl.children[1]; + container.addClass("logs-view"); - const logLevels = [ - { label: "Debug", value: LogLevel.DEBUG }, - { label: "Info", value: LogLevel.INFO }, - { label: "Warn", value: LogLevel.WARNING }, - { label: "Error", value: LogLevel.ERROR } - ]; + const logLevels = [ + { label: "Debug", value: LogLevel.DEBUG }, + { label: "Info", value: LogLevel.INFO }, + { label: "Warn", value: LogLevel.WARNING }, + { label: "Error", value: LogLevel.ERROR } + ]; - container.createDiv( - { - cls: "verbosity-selector" - }, - (verbositySection) => { - verbositySection.createEl("h4", { - text: "VaultLink logs" - }); + container.createDiv( + { + cls: "verbosity-selector" + }, + (verbositySection) => { + verbositySection.createEl("h4", { + text: "VaultLink logs" + }); - const controls = verbositySection.createDiv({ - cls: "logs-controls" - }); + const controls = verbositySection.createDiv({ + cls: "logs-controls" + }); - const copyButton = controls.createEl("button", { - text: "Copy logs", - cls: "clickable-icon" - }); - setIcon(copyButton, "clipboard-copy"); - copyButton.addEventListener("click", () => { - this.copyLogsToClipboard(); - }); + const copyButton = controls.createEl("button", { + text: "Copy logs", + cls: "clickable-icon" + }); + setIcon(copyButton, "clipboard-copy"); + copyButton.addEventListener("click", () => { + this.copyLogsToClipboard(); + }); - controls.createEl("select", {}, (dropdown) => { - logLevels.forEach(({ label, value }) => - dropdown.createEl("option", { text: label, value }) - ); + controls.createEl("select", {}, (dropdown) => { + logLevels.forEach(({ label, value }) => + dropdown.createEl("option", { text: label, value }) + ); - dropdown.value = this.minLogLevel; + dropdown.value = this.minLogLevel; - dropdown.addEventListener("change", () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - this.minLogLevel = dropdown.value as LogLevel; + dropdown.addEventListener("change", () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + this.minLogLevel = dropdown.value as LogLevel; - this.logsContainer?.empty(); - this.logLineToElement.clear(); - this.updateView(); - }); - }); - } - ); + this.logsContainer?.empty(); + this.logLineToElement.clear(); + this.updateView(); + }); + }); + } + ); - this.logsContainer = container.createDiv({ cls: "logs-container" }); + this.logsContainer = container.createDiv({ cls: "logs-container" }); - this.updateView(); - } + this.updateView(); + } - private copyLogsToClipboard(): void { - const logs = this.client.logger.getMessages(this.minLogLevel); + private copyLogsToClipboard(): void { + const logs = this.client.logger.getMessages(this.minLogLevel); - if (logs.length === 0) { - new Notice("No logs to copy"); - return; - } + if (logs.length === 0) { + new Notice("No logs to copy"); + return; + } - const formattedLogs = logs - .map((logLine) => { - const timestamp = logLine.timestamp.toLocaleString(); - const level = logLine.level.toUpperCase(); - return `[${timestamp}] ${level}: ${logLine.message}`; - }) - .join("\n"); + const formattedLogs = logs + .map((logLine) => { + const timestamp = logLine.timestamp.toLocaleString(); + const level = logLine.level.toUpperCase(); + return `[${timestamp}] ${level}: ${logLine.message}`; + }) + .join("\n"); - navigator.clipboard - .writeText(formattedLogs) - .then(() => { - new Notice(`Copied ${logs.length} log entries to clipboard`); - }) - .catch((error: unknown) => { - this.client.logger.error( - `Failed to copy logs to clipboard: ${error}` - ); - new Notice("Failed to copy logs to clipboard"); - }); - } + navigator.clipboard + .writeText(formattedLogs) + .then(() => { + new Notice(`Copied ${logs.length} log entries to clipboard`); + }) + .catch((error: unknown) => { + this.client.logger.error( + `Failed to copy logs to clipboard: ${error}` + ); + new Notice("Failed to copy logs to clipboard"); + }); + } - private updateView(): void { - const container = this.logsContainer; - if (container === undefined) { - return; - } + private updateView(): void { + const container = this.logsContainer; + if (container === undefined) { + return; + } - const logs = this.client.logger.getMessages(this.minLogLevel); + const logs = this.client.logger.getMessages(this.minLogLevel); - if (this.logLineToElement.size === 0 && logs.length > 0) { - // Clear the "No logs available yet" message - container.empty(); - } + if (this.logLineToElement.size === 0 && logs.length > 0) { + // Clear the "No logs available yet" message + container.empty(); + } - const shouldScroll = - container.scrollTop == 0 || - container.scrollHeight - - container.clientHeight - - container.scrollTop < - LogsView.MAX_OFFSET_FROM_BOTTOM_WITH_AUTO_SCROLL_PX; + const shouldScroll = + container.scrollTop == 0 || + container.scrollHeight - + container.clientHeight - + container.scrollTop < + LogsView.MAX_OFFSET_FROM_BOTTOM_WITH_AUTO_SCROLL_PX; - logs.forEach((message) => { - if (this.logLineToElement.has(message)) { - return; - } + logs.forEach((message) => { + if (this.logLineToElement.has(message)) { + return; + } - const element = LogsView.createLogLineElement(container, message); + const element = LogsView.createLogLineElement(container, message); - this.logLineToElement.set(message, element); - }); + this.logLineToElement.set(message, element); + }); - const newLines = new Set(logs); - for (const [logLine, element] of this.logLineToElement) { - if (!newLines.has(logLine)) { - element.remove(); - this.logLineToElement.delete(logLine); - } - } + const newLines = new Set(logs); + for (const [logLine, element] of this.logLineToElement) { + if (!newLines.has(logLine)) { + element.remove(); + this.logLineToElement.delete(logLine); + } + } - if (logs.length === 0) { - container.empty(); - container.createEl("p", { - text: "No logs available yet." - }); - } else if (shouldScroll) { - container.scrollTop = container.scrollHeight; - } - } + if (logs.length === 0) { + container.empty(); + container.createEl("p", { + text: "No logs available yet." + }); + } else if (shouldScroll) { + container.scrollTop = container.scrollHeight; + } + } } diff --git a/frontend/obsidian-plugin/src/views/settings/settings-tab.scss b/frontend/obsidian-plugin/src/views/settings/settings-tab.scss index 0aabbadc..579c5b8c 100644 --- a/frontend/obsidian-plugin/src/views/settings/settings-tab.scss +++ b/frontend/obsidian-plugin/src/views/settings/settings-tab.scss @@ -1,134 +1,134 @@ @mixin number-card { - padding: var(--size-2-1) var(--size-4-1); - border-radius: var(--radius-s); - background-color: var(--color-base-30); - font-size: var(--font-ui-small); + padding: var(--size-2-1) var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--color-base-30); + font-size: var(--font-ui-small); - &.good { - background-color: rgba(var(--color-green-rgb), 0.35); - } + &.good { + background-color: rgba(var(--color-green-rgb), 0.35); + } - &.bad { - background-color: rgba(var(--color-red-rgb), 0.35); - } + &.bad { + background-color: rgba(var(--color-red-rgb), 0.35); + } } .vault-link-settings-container { - position: relative; + position: relative; - .vault-link-settings { - h2 { - display: flex; - align-items: center; - font-size: var(--h2-size); + .vault-link-settings { + h2 { + display: flex; + align-items: center; + font-size: var(--h2-size); - .version { - @include number-card; - margin: var(--size-2-2) 0 0 var(--size-4-2); - background-color: var(--color-base-30); - color: var(--color-base-70); - font-size: var(--font-ui-smaller); - } - } + .version { + @include number-card; + margin: var(--size-2-2) 0 0 var(--size-4-2); + background-color: var(--color-base-30); + color: var(--color-base-70); + font-size: var(--font-ui-smaller); + } + } - .button-container { - display: flex; - gap: var(--size-4-2); - } + .button-container { + display: flex; + gap: var(--size-4-2); + } - h3 { - font-size: var(--font-ui-large); - margin-top: var(--heading-spacing); - } + h3 { + font-size: var(--font-ui-large); + margin-top: var(--heading-spacing); + } - button, - input[type="range"], - .checkbox-container, - .slider::-webkit-slider-thumb { - cursor: pointer; - } + button, + input[type="range"], + .checkbox-container, + .slider::-webkit-slider-thumb { + cursor: pointer; + } - input[type="text"], - textarea { - width: 250px; - } + input[type="text"], + textarea { + width: 250px; + } - textarea { - resize: none; - height: 75px; - } + textarea { + resize: none; + height: 75px; + } - .applying-changes-overlay { - position: absolute; - top: 50%; - left: 50%; - transform: translateY(-50%) translateX(-50%); - z-index: 10; - backdrop-filter: blur(10px); + .applying-changes-overlay { + position: absolute; + top: 50%; + left: 50%; + transform: translateY(-50%) translateX(-50%); + z-index: 10; + backdrop-filter: blur(10px); - .spinner-container { - background-color: rgba(var(--background-primary), 0.5); - border: 1px solid var(--background-modifier-border); - border-radius: var(--radius-m); - padding: var(--size-4-8); - display: flex; - flex-direction: column; - align-items: center; - gap: var(--size-4-3); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); - min-width: 200px; - } + .spinner-container { + background-color: rgba(var(--background-primary), 0.5); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-m); + padding: var(--size-4-8); + display: flex; + flex-direction: column; + align-items: center; + gap: var(--size-4-3); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); + min-width: 200px; + } - .spinner { - width: 48px; - height: 48px; - border: 4px solid var(--background-modifier-border); - border-top-color: var(--interactive-accent); - border-radius: 50%; - animation: spin 0.8s linear infinite; - } + .spinner { + width: 48px; + height: 48px; + border: 4px solid var(--background-modifier-border); + border-top-color: var(--interactive-accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; + } - .spinner-text { - color: var(--text-normal); - font-size: var(--font-ui-medium); - font-weight: 500; - } + .spinner-text { + color: var(--text-normal); + font-size: var(--font-ui-medium); + font-weight: 500; + } - .spinner-warning { - color: var(--text-muted); - font-size: var(--font-ui-small); - text-align: center; - margin-top: var(--size-2-2); - } - } + .spinner-warning { + color: var(--text-muted); + font-size: var(--font-ui-small); + text-align: center; + margin-top: var(--size-2-2); + } + } - @keyframes spin { - from { - transform: rotate(0deg); - } + @keyframes spin { + from { + transform: rotate(0deg); + } - to { - transform: rotate(360deg); - } - } + to { + transform: rotate(360deg); + } + } - &.applying-changes { - .setting-item-control { - pointer-events: none; - opacity: 0.5; - } + &.applying-changes { + .setting-item-control { + pointer-events: none; + opacity: 0.5; + } - button:not(.applying-changes-overlay button) { - pointer-events: none; - opacity: 0.5; - } + button:not(.applying-changes-overlay button) { + pointer-events: none; + opacity: 0.5; + } - input, - textarea, - select { - pointer-events: none; - opacity: 0.5; - } - } - } -} \ No newline at end of file + input, + textarea, + select { + pointer-events: none; + opacity: 0.5; + } + } + } +} diff --git a/frontend/obsidian-plugin/src/views/settings/settings-tab.ts b/frontend/obsidian-plugin/src/views/settings/settings-tab.ts index 1ff78a4b..afd2b0b0 100644 --- a/frontend/obsidian-plugin/src/views/settings/settings-tab.ts +++ b/frontend/obsidian-plugin/src/views/settings/settings-tab.ts @@ -9,559 +9,559 @@ import { LogsView } from "../logs/logs-view"; import type { StatusDescription } from "../status-description/status-description"; export class SyncSettingsTab extends PluginSettingTab { - private editedServerUri: string; - private editedToken: string; - private editedVaultName: string; + private editedServerUri: string; + private editedToken: string; + private editedVaultName: string; - private _isApplyingChanges = false; - private syncEnabledOverride: boolean | undefined = undefined; + private _isApplyingChanges = false; + private syncEnabledOverride: boolean | undefined = undefined; - private readonly plugin: VaultLinkPlugin; - private readonly syncClient: SyncClient; - private readonly statusDescription: StatusDescription; - private statusDescriptionSubscription: (() => unknown) | undefined; + private readonly plugin: VaultLinkPlugin; + private readonly syncClient: SyncClient; + private readonly statusDescription: StatusDescription; + private statusDescriptionSubscription: (() => unknown) | undefined; - public constructor({ - app, - plugin, - syncClient, - statusDescription - }: { - app: App; - plugin: VaultLinkPlugin; - syncClient: SyncClient; - statusDescription: StatusDescription; - }) { - super(app, plugin); - this.plugin = plugin; - this.syncClient = syncClient; - this.statusDescription = statusDescription; + public constructor({ + app, + plugin, + syncClient, + statusDescription + }: { + app: App; + plugin: VaultLinkPlugin; + syncClient: SyncClient; + statusDescription: StatusDescription; + }) { + super(app, plugin); + this.plugin = plugin; + this.syncClient = syncClient; + this.statusDescription = statusDescription; - this.editedServerUri = this.syncClient.getSettings().remoteUri; - this.editedToken = this.syncClient.getSettings().token; - this.editedVaultName = this.syncClient.getSettings().vaultName; + this.editedServerUri = this.syncClient.getSettings().remoteUri; + this.editedToken = this.syncClient.getSettings().token; + this.editedVaultName = this.syncClient.getSettings().vaultName; - this.syncClient.addOnSettingsChangeListener( - (newSettings, oldSettings) => { - let hasChanged = false; + this.syncClient.addOnSettingsChangeListener( + (newSettings, oldSettings) => { + let hasChanged = false; - if (newSettings.remoteUri !== oldSettings.remoteUri) { - this.editedServerUri = newSettings.remoteUri; - hasChanged = true; - } + if (newSettings.remoteUri !== oldSettings.remoteUri) { + this.editedServerUri = newSettings.remoteUri; + hasChanged = true; + } - if (newSettings.token !== oldSettings.token) { - this.editedToken = newSettings.token; - hasChanged = true; - } + if (newSettings.token !== oldSettings.token) { + this.editedToken = newSettings.token; + hasChanged = true; + } - if (newSettings.vaultName !== oldSettings.vaultName) { - this.editedVaultName = newSettings.vaultName; - hasChanged = true; - } + if (newSettings.vaultName !== oldSettings.vaultName) { + this.editedVaultName = newSettings.vaultName; + hasChanged = true; + } - if (hasChanged) { - this.display(); - } - } - ); - } + if (hasChanged) { + this.display(); + } + } + ); + } - private get isApplyingChanges(): boolean { - return this._isApplyingChanges; - } + private get isApplyingChanges(): boolean { + return this._isApplyingChanges; + } - private set isApplyingChanges(value: boolean) { - this._isApplyingChanges = value; - this.display(); - } + private set isApplyingChanges(value: boolean) { + this._isApplyingChanges = value; + this.display(); + } - public display(): void { - const { containerEl } = this; - containerEl.empty(); - containerEl.addClass("vault-link-settings"); - containerEl.parentElement?.addClass("vault-link-settings-container"); + public display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.addClass("vault-link-settings"); + containerEl.parentElement?.addClass("vault-link-settings-container"); - if (this.isApplyingChanges) { - containerEl.addClass("applying-changes"); - } else { - containerEl.removeClass("applying-changes"); - } + if (this.isApplyingChanges) { + containerEl.addClass("applying-changes"); + } else { + containerEl.removeClass("applying-changes"); + } - this.renderApplyingChanges(containerEl); - this.renderSettingsHeader(containerEl); - this.renderConnectionSettings(containerEl); - this.renderSyncSettings(containerEl); - this.renderMiscSettings(containerEl); - } + this.renderApplyingChanges(containerEl); + this.renderSettingsHeader(containerEl); + this.renderConnectionSettings(containerEl); + this.renderSyncSettings(containerEl); + this.renderMiscSettings(containerEl); + } - public hide(): void { - super.hide(); - this.setStatusDescriptionSubscription(); - } + public hide(): void { + super.hide(); + this.setStatusDescriptionSubscription(); + } - private renderApplyingChanges(containerEl: HTMLElement): void { - if (this.isApplyingChanges) { - const overlay = containerEl.createDiv({ - cls: "applying-changes-overlay" - }); + private renderApplyingChanges(containerEl: HTMLElement): void { + if (this.isApplyingChanges) { + const overlay = containerEl.createDiv({ + cls: "applying-changes-overlay" + }); - const spinnerContainer = overlay.createDiv({ - cls: "spinner-container" - }); + const spinnerContainer = overlay.createDiv({ + cls: "spinner-container" + }); - spinnerContainer.createDiv({ - cls: "spinner" - }); + spinnerContainer.createDiv({ + cls: "spinner" + }); - spinnerContainer.createDiv({ - text: "Applying changes...", - cls: "spinner-text" - }); + spinnerContainer.createDiv({ + text: "Applying changes...", + cls: "spinner-text" + }); - spinnerContainer.createDiv({ - text: "You can exit, but changes won't be saved", - cls: "spinner-warning" - }); - } - } + spinnerContainer.createDiv({ + text: "You can exit, but changes won't be saved", + cls: "spinner-warning" + }); + } + } - private renderSettingsHeader(containerEl: HTMLElement): void { - containerEl.createEl("h2", { text: "VaultLink" }).createSpan({ - text: this.plugin.manifest.version, - cls: "version" - }); + private renderSettingsHeader(containerEl: HTMLElement): void { + containerEl.createEl("h2", { text: "VaultLink" }).createSpan({ + text: this.plugin.manifest.version, + cls: "version" + }); - containerEl.createDiv( - { - cls: "description" - }, - (descriptionContainer) => { - this.setStatusDescriptionSubscription( - this.statusDescription.renderStatusDescription.bind( - this.statusDescription, - descriptionContainer - ) - ); - } - ); + containerEl.createDiv( + { + cls: "description" + }, + (descriptionContainer) => { + this.setStatusDescriptionSubscription( + this.statusDescription.renderStatusDescription.bind( + this.statusDescription, + descriptionContainer + ) + ); + } + ); - containerEl.createDiv( - { - cls: "button-container" - }, - (buttonContainer) => { - buttonContainer.createEl( - "button", - { - text: "Show history" - }, - (button) => - (button.onclick = async (): Promise => { - this.plugin.closeSettings(); - await this.plugin.activateView(HistoryView.TYPE); - }) - ); + containerEl.createDiv( + { + cls: "button-container" + }, + (buttonContainer) => { + buttonContainer.createEl( + "button", + { + text: "Show history" + }, + (button) => + (button.onclick = async (): Promise => { + this.plugin.closeSettings(); + await this.plugin.activateView(HistoryView.TYPE); + }) + ); - buttonContainer.createEl( - "button", - { - text: "Show logs" - }, - (button) => - (button.onclick = async (): Promise => { - this.plugin.closeSettings(); - await this.plugin.activateView(LogsView.TYPE); - }) - ); - } - ); - } + buttonContainer.createEl( + "button", + { + text: "Show logs" + }, + (button) => + (button.onclick = async (): Promise => { + this.plugin.closeSettings(); + await this.plugin.activateView(LogsView.TYPE); + }) + ); + } + ); + } - private renderConnectionSettings(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Connection" }); + private renderConnectionSettings(containerEl: HTMLElement): void { + containerEl.createEl("h3", { text: "Connection" }); - const [title, updateTitle] = this.unsavedAwareSettingName( - "Server address", - "remoteUri" - ); - new Setting(containerEl) - .setName(title) - .setDesc( - "Your VaultLink server's URL including the protocol and full path." - ) - .setTooltip("This is the URL of the server you want to sync with.") - .addText((text) => - text - .setPlaceholder("https://example.com:3000") - .setValue(this.editedServerUri.toLowerCase().trim()) - .onChange((value) => { - this.editedServerUri = value.toLowerCase().trim(); - updateTitle(value.toLowerCase().trim()); - }) - ); + const [title, updateTitle] = this.unsavedAwareSettingName( + "Server address", + "remoteUri" + ); + new Setting(containerEl) + .setName(title) + .setDesc( + "Your VaultLink server's URL including the protocol and full path." + ) + .setTooltip("This is the URL of the server you want to sync with.") + .addText((text) => + text + .setPlaceholder("https://example.com:3000") + .setValue(this.editedServerUri.toLowerCase().trim()) + .onChange((value) => { + this.editedServerUri = value.toLowerCase().trim(); + updateTitle(value.toLowerCase().trim()); + }) + ); - const [tokenTitle, updateTokenTitle] = this.unsavedAwareSettingName( - "Access token", - "token" - ); - new Setting(containerEl) - .setName(tokenTitle) - .setClass("sync-settings-access-token") - .setDesc( - "Set the access token for the server that you can get from the server" - ) - .setTooltip("todo, links to dcocs") - .addTextArea((text) => - text - .setPlaceholder("ey...") - .setValue(this.editedToken.trim()) - .onChange((value) => { - this.editedToken = value.trim(); - updateTokenTitle(value.trim()); - }) - ); + const [tokenTitle, updateTokenTitle] = this.unsavedAwareSettingName( + "Access token", + "token" + ); + new Setting(containerEl) + .setName(tokenTitle) + .setClass("sync-settings-access-token") + .setDesc( + "Set the access token for the server that you can get from the server" + ) + .setTooltip("todo, links to dcocs") + .addTextArea((text) => + text + .setPlaceholder("ey...") + .setValue(this.editedToken.trim()) + .onChange((value) => { + this.editedToken = value.trim(); + updateTokenTitle(value.trim()); + }) + ); - const [vaultNameTitle, updateVaultNameTitle] = - this.unsavedAwareSettingName("Vault name", "vaultName"); - new Setting(containerEl) - .setName(vaultNameTitle) - .setDesc( - "Set the name of the remote vault that you want to sync with" - ) - .setTooltip("todo, links to dcocs") - .addText((text) => - text - .setPlaceholder("My Obsidian Vault") - .setValue(this.editedVaultName.toLowerCase().trim()) - .onChange((value) => { - this.editedVaultName = value.toLowerCase().trim(); - updateVaultNameTitle(value.toLowerCase().trim()); - }) - ); + const [vaultNameTitle, updateVaultNameTitle] = + this.unsavedAwareSettingName("Vault name", "vaultName"); + new Setting(containerEl) + .setName(vaultNameTitle) + .setDesc( + "Set the name of the remote vault that you want to sync with" + ) + .setTooltip("todo, links to dcocs") + .addText((text) => + text + .setPlaceholder("My Obsidian Vault") + .setValue(this.editedVaultName.toLowerCase().trim()) + .onChange((value) => { + this.editedVaultName = value.toLowerCase().trim(); + updateVaultNameTitle(value.toLowerCase().trim()); + }) + ); - new Setting(containerEl).addButton((button) => - button - .setButtonText("Apply & test connection") - .setDisabled(this.isApplyingChanges) - .setTooltip( - this.isApplyingChanges - ? "Waiting for applying changes to finish..." - : "Apply the changes made to the connection settings and test the connection to the server." - ) - .onClick(() => { - // don't show loader within the button - void (async (): Promise => { - if (this.areThereUnsavedChanges()) { - new Notice("Applying changes to the server..."); + new Setting(containerEl).addButton((button) => + button + .setButtonText("Apply & test connection") + .setDisabled(this.isApplyingChanges) + .setTooltip( + this.isApplyingChanges + ? "Waiting for applying changes to finish..." + : "Apply the changes made to the connection settings and test the connection to the server." + ) + .onClick(() => { + // don't show loader within the button + void (async (): Promise => { + if (this.areThereUnsavedChanges()) { + new Notice("Applying changes to the server..."); - this.isApplyingChanges = true; - try { - await this.syncClient.setSettings({ - vaultName: this.editedVaultName, - remoteUri: this.editedServerUri, - token: this.editedToken - }); - } finally { - this.isApplyingChanges = false; - } + this.isApplyingChanges = true; + try { + await this.syncClient.setSettings({ + vaultName: this.editedVaultName, + remoteUri: this.editedServerUri, + token: this.editedToken + }); + } finally { + this.isApplyingChanges = false; + } - new Notice("Checking connection to the server..."); - new Notice( - ( - await this.syncClient.checkConnection() - ).serverMessage - ); - await this.statusDescription.updateConnectionState(); - } else { - new Notice("No changes to apply"); - } - })(); - }) - ); - } + new Notice("Checking connection to the server..."); + new Notice( + ( + await this.syncClient.checkConnection() + ).serverMessage + ); + await this.statusDescription.updateConnectionState(); + } else { + new Notice("No changes to apply"); + } + })(); + }) + ); + } - private areThereUnsavedChanges(): boolean { - return ( - this.editedServerUri !== this.syncClient.getSettings().remoteUri || - this.editedToken !== this.syncClient.getSettings().token || - this.editedVaultName !== this.syncClient.getSettings().vaultName - ); - } + private areThereUnsavedChanges(): boolean { + return ( + this.editedServerUri !== this.syncClient.getSettings().remoteUri || + this.editedToken !== this.syncClient.getSettings().token || + this.editedVaultName !== this.syncClient.getSettings().vaultName + ); + } - private renderSyncSettings(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Sync" }); + private renderSyncSettings(containerEl: HTMLElement): void { + containerEl.createEl("h3", { text: "Sync" }); - new Setting(containerEl) - .setName("Enable sync") - .setDesc( - "Enable pulling and pushing changes to the remote server. The first time it's enabled, or after the sync state has been reset, all local files will be pushed to the server." - ) - .setTooltip( - "Enable pulling and pushing changes to the remote server." - ) - .addToggle((toggle) => - toggle - .setValue( - this.syncEnabledOverride ?? - this.syncClient.getSettings().isSyncEnabled - ) - .setDisabled(this.isApplyingChanges) - .setTooltip( - this.isApplyingChanges - ? "Waiting for applying changes to finish..." - : "Enable or disable syncing." - ) - .onChange( - (value) => - void (async (): Promise => { - this.syncEnabledOverride = value; - this.isApplyingChanges = true; - try { - await this.syncClient.setSetting( - "isSyncEnabled", - value - ); - } finally { - this.syncEnabledOverride = undefined; - this.isApplyingChanges = false; - } - })() - ) - ); + new Setting(containerEl) + .setName("Enable sync") + .setDesc( + "Enable pulling and pushing changes to the remote server. The first time it's enabled, or after the sync state has been reset, all local files will be pushed to the server." + ) + .setTooltip( + "Enable pulling and pushing changes to the remote server." + ) + .addToggle((toggle) => + toggle + .setValue( + this.syncEnabledOverride ?? + this.syncClient.getSettings().isSyncEnabled + ) + .setDisabled(this.isApplyingChanges) + .setTooltip( + this.isApplyingChanges + ? "Waiting for applying changes to finish..." + : "Enable or disable syncing." + ) + .onChange( + (value) => + void (async (): Promise => { + this.syncEnabledOverride = value; + this.isApplyingChanges = true; + try { + await this.syncClient.setSetting( + "isSyncEnabled", + value + ); + } finally { + this.syncEnabledOverride = undefined; + this.isApplyingChanges = false; + } + })() + ) + ); - new Setting(containerEl) - .setName("Ignore patterns") - .setDesc( - "Patterns to ignore when syncing. Each line is a separate glob pattern. Patterns are matched against the relative path of the file. For example, to ignore all files in a folder named 'ignore', enter 'ignore/*'. To ignore all files with the extension '.log', enter '*.log'." - ) - .addTextArea((text) => - text - .setValue( - this.syncClient.getSettings().ignorePatterns.join("\n") - ) - .setPlaceholder("Enter patterns to ignore, one per line") - .onChange(async (value) => { - const patterns = value - .split("\n") - .map((pattern) => pattern.trim()) - .filter((pattern) => pattern.length > 0); - return this.syncClient.setSetting( - "ignorePatterns", - patterns - ); - }) - ); + new Setting(containerEl) + .setName("Ignore patterns") + .setDesc( + "Patterns to ignore when syncing. Each line is a separate glob pattern. Patterns are matched against the relative path of the file. For example, to ignore all files in a folder named 'ignore', enter 'ignore/*'. To ignore all files with the extension '.log', enter '*.log'." + ) + .addTextArea((text) => + text + .setValue( + this.syncClient.getSettings().ignorePatterns.join("\n") + ) + .setPlaceholder("Enter patterns to ignore, one per line") + .onChange(async (value) => { + const patterns = value + .split("\n") + .map((pattern) => pattern.trim()) + .filter((pattern) => pattern.length > 0); + return this.syncClient.setSetting( + "ignorePatterns", + patterns + ); + }) + ); - new Setting(containerEl) - .setName("Sync concurrency") - .setDesc( - "How many concurrent sync operations to run. Setting this value higher may increase the overall performance, however, it will require more memory as well. If you notice frequent crashes, especially on mobile, set this to 1." - ) - .addSlider((text) => - text - .setLimits(1, 16, 1) - .setDynamicTooltip() - .setInstant(false) - .setValue(this.syncClient.getSettings().syncConcurrency) - .onChange(async (value) => - this.syncClient.setSetting("syncConcurrency", value) - ) - ); + new Setting(containerEl) + .setName("Sync concurrency") + .setDesc( + "How many concurrent sync operations to run. Setting this value higher may increase the overall performance, however, it will require more memory as well. If you notice frequent crashes, especially on mobile, set this to 1." + ) + .addSlider((text) => + text + .setLimits(1, 16, 1) + .setDynamicTooltip() + .setInstant(false) + .setValue(this.syncClient.getSettings().syncConcurrency) + .onChange(async (value) => + this.syncClient.setSetting("syncConcurrency", value) + ) + ); - new Setting(containerEl) - .setName("Maximum file size to be uploaded (MB)") - .setDesc( - "Set the maximum file size that can be uploaded to the server. Files larger than this size will be ignored." - ) - .addText((input) => - input - .setValue( - this.syncClient.getSettings().maxFileSizeMB.toString() - ) - .onChange(async (value) => { - if (value === "") { - return; - } - let parsedValue = Number.parseFloat(value); - if (Number.isNaN(parsedValue) || parsedValue < 0) { - parsedValue = - this.syncClient.getSettings().maxFileSizeMB; - } + new Setting(containerEl) + .setName("Maximum file size to be uploaded (MB)") + .setDesc( + "Set the maximum file size that can be uploaded to the server. Files larger than this size will be ignored." + ) + .addText((input) => + input + .setValue( + this.syncClient.getSettings().maxFileSizeMB.toString() + ) + .onChange(async (value) => { + if (value === "") { + return; + } + let parsedValue = Number.parseFloat(value); + if (Number.isNaN(parsedValue) || parsedValue < 0) { + parsedValue = + this.syncClient.getSettings().maxFileSizeMB; + } - if (value !== parsedValue.toString()) { - input.setValue(parsedValue.toString()); - } + if (value !== parsedValue.toString()) { + input.setValue(parsedValue.toString()); + } - return this.syncClient.setSetting( - "maxFileSizeMB", - parsedValue - ); - }) - ); + return this.syncClient.setSetting( + "maxFileSizeMB", + parsedValue + ); + }) + ); - new Setting(containerEl) - .setName("Danger zone") - .setDesc( - "Delete the local metadata database while leaving the local and remote files intact." - ) - .addButton((button) => - button - .setDisabled(this.isApplyingChanges) - .setTooltip( - this.isApplyingChanges - ? "Waiting for applying changes to finish..." - : "Reset sync state" - ) - .setButtonText("Reset sync state") - .onClick( - () => - void (async (): Promise => { - this.isApplyingChanges = true; - try { - await this.syncClient.reset(); - } finally { - this.isApplyingChanges = false; - } + new Setting(containerEl) + .setName("Danger zone") + .setDesc( + "Delete the local metadata database while leaving the local and remote files intact." + ) + .addButton((button) => + button + .setDisabled(this.isApplyingChanges) + .setTooltip( + this.isApplyingChanges + ? "Waiting for applying changes to finish..." + : "Reset sync state" + ) + .setButtonText("Reset sync state") + .onClick( + () => + void (async (): Promise => { + this.isApplyingChanges = true; + try { + await this.syncClient.reset(); + } finally { + this.isApplyingChanges = false; + } - new Notice( - "Sync state has been reset, you will need to resync" - ); - })() - ) - ); - } + new Notice( + "Sync state has been reset, you will need to resync" + ); + })() + ) + ); + } - private renderMiscSettings(containerEl: HTMLElement): void { - containerEl.createEl("h3", { text: "Other" }); + private renderMiscSettings(containerEl: HTMLElement): void { + containerEl.createEl("h3", { text: "Other" }); - new Setting(containerEl) - .setName("Enable telemetry") - .setDesc( - "Allow sending anonymous usage data & error reports to help improve the plugin. The data collected is never shared with third parties." - ) - .setTooltip( - "Allow sending anonymous usage data & error reports to help improve the plugin. The data collected is never shared with third parties." - ) - .addToggle((toggle) => - toggle - .setValue(this.syncClient.getSettings().enableTelemetry) - .onChange(async (value) => - this.syncClient.setSetting("enableTelemetry", value) - ) - ); + new Setting(containerEl) + .setName("Enable telemetry") + .setDesc( + "Allow sending anonymous usage data & error reports to help improve the plugin. The data collected is never shared with third parties." + ) + .setTooltip( + "Allow sending anonymous usage data & error reports to help improve the plugin. The data collected is never shared with third parties." + ) + .addToggle((toggle) => + toggle + .setValue(this.syncClient.getSettings().enableTelemetry) + .onChange(async (value) => + this.syncClient.setSetting("enableTelemetry", value) + ) + ); - containerEl.createEl("h3", { text: "Advanced" }); + containerEl.createEl("h3", { text: "Advanced" }); - new Setting(containerEl) - .setName("Network retry interval (ms)") - .setDesc( - "The time to wait between retrying failed network requests, in milliseconds." - ) - .addText((input) => - input - .setValue( - this.syncClient - .getSettings() - .networkRetryIntervalMs.toString() - ) - .onChange(async (value) => { - if (value === "") { - return; - } - let parsedValue = Number.parseInt(value, 10); - if (Number.isNaN(parsedValue) || parsedValue < 0) { - parsedValue = - this.syncClient.getSettings() - .networkRetryIntervalMs; - } + new Setting(containerEl) + .setName("Network retry interval (ms)") + .setDesc( + "The time to wait between retrying failed network requests, in milliseconds." + ) + .addText((input) => + input + .setValue( + this.syncClient + .getSettings() + .networkRetryIntervalMs.toString() + ) + .onChange(async (value) => { + if (value === "") { + return; + } + let parsedValue = Number.parseInt(value, 10); + if (Number.isNaN(parsedValue) || parsedValue < 0) { + parsedValue = + this.syncClient.getSettings() + .networkRetryIntervalMs; + } - if (value !== parsedValue.toString()) { - input.setValue(parsedValue.toString()); - } + if (value !== parsedValue.toString()) { + input.setValue(parsedValue.toString()); + } - return this.syncClient.setSetting( - "networkRetryIntervalMs", - parsedValue - ); - }) - ); + return this.syncClient.setSetting( + "networkRetryIntervalMs", + parsedValue + ); + }) + ); - new Setting(containerEl) - .setName("Minimum save interval (ms)") - .setDesc( - "The minimum time between saving settings and database to disk, in milliseconds. Lower values save more frequently but may impact performance." - ) - .addText((input) => - input - .setValue( - this.syncClient - .getSettings() - .minimumSaveIntervalMs.toString() - ) - .onChange(async (value) => { - if (value === "") { - return; - } - let parsedValue = Number.parseInt(value, 10); - if (Number.isNaN(parsedValue) || parsedValue < 0) { - parsedValue = - this.syncClient.getSettings() - .minimumSaveIntervalMs; - } + new Setting(containerEl) + .setName("Minimum save interval (ms)") + .setDesc( + "The minimum time between saving settings and database to disk, in milliseconds. Lower values save more frequently but may impact performance." + ) + .addText((input) => + input + .setValue( + this.syncClient + .getSettings() + .minimumSaveIntervalMs.toString() + ) + .onChange(async (value) => { + if (value === "") { + return; + } + let parsedValue = Number.parseInt(value, 10); + if (Number.isNaN(parsedValue) || parsedValue < 0) { + parsedValue = + this.syncClient.getSettings() + .minimumSaveIntervalMs; + } - if (value !== parsedValue.toString()) { - input.setValue(parsedValue.toString()); - } + if (value !== parsedValue.toString()) { + input.setValue(parsedValue.toString()); + } - return this.syncClient.setSetting( - "minimumSaveIntervalMs", - parsedValue - ); - }) - ); - } + return this.syncClient.setSetting( + "minimumSaveIntervalMs", + parsedValue + ); + }) + ); + } - private setStatusDescriptionSubscription( - newSubscription?: () => unknown - ): void { - if (this.statusDescriptionSubscription) { - this.statusDescription.removeStatusChangeListener( - this.statusDescriptionSubscription - ); - } - this.statusDescriptionSubscription = newSubscription; - if (this.statusDescriptionSubscription) { - this.statusDescriptionSubscription(); - this.statusDescription.addStatusChangeListener( - this.statusDescriptionSubscription - ); - } - } + private setStatusDescriptionSubscription( + newSubscription?: () => unknown + ): void { + if (this.statusDescriptionSubscription) { + this.statusDescription.removeStatusChangeListener( + this.statusDescriptionSubscription + ); + } + this.statusDescriptionSubscription = newSubscription; + if (this.statusDescriptionSubscription) { + this.statusDescriptionSubscription(); + this.statusDescription.addStatusChangeListener( + this.statusDescriptionSubscription + ); + } + } - private unsavedAwareSettingName( - name: string, - settingName: keyof SyncSettings - ): [ - DocumentFragment, - (newValue: SyncSettings[keyof SyncSettings]) => unknown - ] { - const titleContainer = document.createDocumentFragment(); - const title = titleContainer.createEl("div", { - text: name, - cls: "setting-item-name" - }); + private unsavedAwareSettingName( + name: string, + settingName: keyof SyncSettings + ): [ + DocumentFragment, + (newValue: SyncSettings[keyof SyncSettings]) => unknown + ] { + const titleContainer = document.createDocumentFragment(); + const title = titleContainer.createEl("div", { + text: name, + cls: "setting-item-name" + }); - const updateTitle = ( - currentValue: SyncSettings[keyof SyncSettings] - ): void => { - title.innerText = `${name}${ - currentValue !== this.syncClient.getSettings()[settingName] - ? " (unsaved)" - : "" - }`; - }; + const updateTitle = ( + currentValue: SyncSettings[keyof SyncSettings] + ): void => { + title.innerText = `${name}${ + currentValue !== this.syncClient.getSettings()[settingName] + ? " (unsaved)" + : "" + }`; + }; - return [titleContainer, updateTitle]; - } + return [titleContainer, updateTitle]; + } } diff --git a/frontend/obsidian-plugin/src/views/status-bar/status-bar.scss b/frontend/obsidian-plugin/src/views/status-bar/status-bar.scss index 3762c2d9..06dfef8f 100644 --- a/frontend/obsidian-plugin/src/views/status-bar/status-bar.scss +++ b/frontend/obsidian-plugin/src/views/status-bar/status-bar.scss @@ -1,14 +1,14 @@ .sync-status { - display: flex; - gap: var(--size-4-2); + display: flex; + gap: var(--size-4-2); - * { - display: block; - } + * { + display: block; + } - .initialize-button { - padding: 0 var(--size-4-2); - background: rgba(var(--color-red-rgb), 0.4); - cursor: pointer; - } + .initialize-button { + padding: 0 var(--size-4-2); + background: rgba(var(--color-red-rgb), 0.4); + cursor: pointer; + } } diff --git a/frontend/obsidian-plugin/src/views/status-bar/status-bar.ts b/frontend/obsidian-plugin/src/views/status-bar/status-bar.ts index 6466601c..7a128ae9 100644 --- a/frontend/obsidian-plugin/src/views/status-bar/status-bar.ts +++ b/frontend/obsidian-plugin/src/views/status-bar/status-bar.ts @@ -4,72 +4,72 @@ import type { HistoryStats, SyncClient } from "sync-client"; import type VaultLinkPlugin from "../../vault-link-plugin"; export class StatusBar { - private readonly statusBarItem: HTMLElement; + private readonly statusBarItem: HTMLElement; - private lastHistoryStats: HistoryStats | undefined; - private lastRemaining: number | undefined; + private lastHistoryStats: HistoryStats | undefined; + private lastRemaining: number | undefined; - public constructor( - private readonly plugin: VaultLinkPlugin, - private readonly syncClient: SyncClient - ) { - this.statusBarItem = plugin.addStatusBarItem(); - this.syncClient.addSyncHistoryUpdateListener((status) => { - this.lastHistoryStats = status; - this.updateStatus(); - }); + public constructor( + private readonly plugin: VaultLinkPlugin, + private readonly syncClient: SyncClient + ) { + this.statusBarItem = plugin.addStatusBarItem(); + this.syncClient.addSyncHistoryUpdateListener((status) => { + this.lastHistoryStats = status; + this.updateStatus(); + }); - this.syncClient.addRemainingSyncOperationsListener( - (remainingOperations) => { - this.lastRemaining = remainingOperations; - this.updateStatus(); - } - ); + this.syncClient.addRemainingSyncOperationsListener( + (remainingOperations) => { + this.lastRemaining = remainingOperations; + this.updateStatus(); + } + ); - this.syncClient.addOnSettingsChangeListener(() => { - this.updateStatus(); - }); - } + this.syncClient.addOnSettingsChangeListener(() => { + this.updateStatus(); + }); + } - private updateStatus(): void { - this.statusBarItem.empty(); - const container = this.statusBarItem.createDiv({ - cls: ["sync-status"] - }); + private updateStatus(): void { + this.statusBarItem.empty(); + const container = this.statusBarItem.createDiv({ + cls: ["sync-status"] + }); - if (!this.syncClient.getSettings().isSyncEnabled) { - const button = container.createEl("button", { - text: "VaultLink is disabled, click to configure", - cls: "initialize-button" - }); - button.onclick = this.plugin.openSettings.bind(this.plugin); + if (!this.syncClient.getSettings().isSyncEnabled) { + const button = container.createEl("button", { + text: "VaultLink is disabled, click to configure", + cls: "initialize-button" + }); + button.onclick = this.plugin.openSettings.bind(this.plugin); - return; - } + return; + } - let hasShownMessage = false; + let hasShownMessage = false; - if ((this.lastRemaining ?? 0) > 0) { - hasShownMessage = true; - container.createSpan({ text: `${this.lastRemaining} ⏳` }); - } + if ((this.lastRemaining ?? 0) > 0) { + hasShownMessage = true; + container.createSpan({ text: `${this.lastRemaining} ⏳` }); + } - if ((this.lastHistoryStats?.success ?? 0) > 0) { - hasShownMessage = true; - container.createSpan({ - text: `${this.lastHistoryStats?.success ?? 0} ✅` - }); - } + if ((this.lastHistoryStats?.success ?? 0) > 0) { + hasShownMessage = true; + container.createSpan({ + text: `${this.lastHistoryStats?.success ?? 0} ✅` + }); + } - if ((this.lastHistoryStats?.error ?? 0) > 0) { - hasShownMessage = true; - container.createSpan({ - text: `${this.lastHistoryStats?.error ?? 0} ❌` - }); - } + if ((this.lastHistoryStats?.error ?? 0) > 0) { + hasShownMessage = true; + container.createSpan({ + text: `${this.lastHistoryStats?.error ?? 0} ❌` + }); + } - if (!hasShownMessage) { - container.createSpan({ text: "VaultLink is idle" }); - } - } + if (!hasShownMessage) { + container.createSpan({ text: "VaultLink is idle" }); + } + } } diff --git a/frontend/obsidian-plugin/src/views/status-description/status-description.scss b/frontend/obsidian-plugin/src/views/status-description/status-description.scss index 3ac86944..b66447bd 100644 --- a/frontend/obsidian-plugin/src/views/status-description/status-description.scss +++ b/frontend/obsidian-plugin/src/views/status-description/status-description.scss @@ -1,32 +1,32 @@ @mixin number-card { - padding: var(--size-2-1) var(--size-4-1); - border-radius: var(--radius-s); - background-color: var(--color-base-30); - font-size: var(--font-ui-small); + padding: var(--size-2-1) var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--color-base-30); + font-size: var(--font-ui-small); - &.good { - background-color: rgba(var(--color-green-rgb), 0.35); - } + &.good { + background-color: rgba(var(--color-green-rgb), 0.35); + } - &.bad { - background-color: rgba(var(--color-red-rgb), 0.35); - } + &.bad { + background-color: rgba(var(--color-red-rgb), 0.35); + } } .status-description { - margin: var(--p-spacing) 0; + margin: var(--p-spacing) 0; - .number { - @include number-card; - font-family: var(--font-monospace); - font-weight: var(--bold-weight); - } + .number { + @include number-card; + font-family: var(--font-monospace); + font-weight: var(--bold-weight); + } - .error { - color: rgb(var(--color-red-rgb)); - } + .error { + color: rgb(var(--color-red-rgb)); + } - .warning { - color: rgb(var(--color-yellow-rgb)); - } + .warning { + color: rgb(var(--color-yellow-rgb)); + } } diff --git a/frontend/obsidian-plugin/src/views/status-description/status-description.ts b/frontend/obsidian-plugin/src/views/status-description/status-description.ts index fe4f17dc..540d5f21 100644 --- a/frontend/obsidian-plugin/src/views/status-description/status-description.ts +++ b/frontend/obsidian-plugin/src/views/status-description/status-description.ts @@ -1,147 +1,147 @@ import "./status-description.scss"; import type { - HistoryStats, - NetworkConnectionStatus, - SyncClient + HistoryStats, + NetworkConnectionStatus, + SyncClient } from "sync-client"; import { utils } from "sync-client"; export class StatusDescription { - private lastHistoryStats: HistoryStats | undefined; - private lastRemaining: number | undefined; - private lastConnectionState: NetworkConnectionStatus | undefined; + private lastHistoryStats: HistoryStats | undefined; + private lastRemaining: number | undefined; + private lastConnectionState: NetworkConnectionStatus | undefined; - private readonly statusChangeListeners: (() => unknown)[] = []; + private readonly statusChangeListeners: (() => unknown)[] = []; - public constructor(private readonly syncClient: SyncClient) { - void this.updateConnectionState(); + public constructor(private readonly syncClient: SyncClient) { + void this.updateConnectionState(); - syncClient.addSyncHistoryUpdateListener((status) => { - this.lastHistoryStats = status; - this.updateDescription(); - }); + syncClient.addSyncHistoryUpdateListener((status) => { + this.lastHistoryStats = status; + this.updateDescription(); + }); - this.syncClient.addRemainingSyncOperationsListener( - (remainingOperations) => { - this.lastRemaining = remainingOperations; - this.updateDescription(); - } - ); + this.syncClient.addRemainingSyncOperationsListener( + (remainingOperations) => { + this.lastRemaining = remainingOperations; + this.updateDescription(); + } + ); - this.syncClient.addWebSocketStatusChangeListener(async () => - this.updateConnectionState() - ); + this.syncClient.addWebSocketStatusChangeListener(async () => + this.updateConnectionState() + ); - this.syncClient.addOnSettingsChangeListener(async () => - this.updateConnectionState() - ); - } + this.syncClient.addOnSettingsChangeListener(async () => + this.updateConnectionState() + ); + } - public async updateConnectionState(): Promise { - this.lastConnectionState = await this.syncClient.checkConnection(); - this.updateDescription(); - } + public async updateConnectionState(): Promise { + this.lastConnectionState = await this.syncClient.checkConnection(); + this.updateDescription(); + } - public addStatusChangeListener(listener: () => unknown): void { - this.statusChangeListeners.push(listener); - } - public removeStatusChangeListener(listener: () => unknown): void { - utils.removeFromArray(this.statusChangeListeners, listener); - } + public addStatusChangeListener(listener: () => unknown): void { + this.statusChangeListeners.push(listener); + } + public removeStatusChangeListener(listener: () => unknown): void { + utils.removeFromArray(this.statusChangeListeners, listener); + } - public renderStatusDescription(container: HTMLElement): void { - container.empty(); - container.addClass("status-description"); + public renderStatusDescription(container: HTMLElement): void { + container.empty(); + container.addClass("status-description"); - if (this.lastConnectionState == undefined) { - container.createSpan({ - text: "VaultLink is starting up…", - cls: "warning" - }); - return; - } + if (this.lastConnectionState == undefined) { + container.createSpan({ + text: "VaultLink is starting up…", + cls: "warning" + }); + return; + } - if (!this.lastConnectionState.isSuccessful) { - container.createSpan({ - text: `VaultLink failed to connect to the remote server with error '${this.lastConnectionState.serverMessage}'`, - cls: "error" - }); - return; - } + if (!this.lastConnectionState.isSuccessful) { + container.createSpan({ + text: `VaultLink failed to connect to the remote server with error '${this.lastConnectionState.serverMessage}'`, + cls: "error" + }); + return; + } - if (!this.lastConnectionState.isWebSocketConnected) { - container.createSpan({ - text: `${this.lastConnectionState.serverMessage} but the WebSocket connection could not be established.`, - cls: "error" - }); - return; - } + if (!this.lastConnectionState.isWebSocketConnected) { + container.createSpan({ + text: `${this.lastConnectionState.serverMessage} but the WebSocket connection could not be established.`, + cls: "error" + }); + return; + } - container.createSpan({ text: "VaultLink is connected to the server " }); - container.createEl("a", { - text: this.syncClient.getSettings().remoteUri, - href: this.syncClient.getSettings().remoteUri - }); + container.createSpan({ text: "VaultLink is connected to the server " }); + container.createEl("a", { + text: this.syncClient.getSettings().remoteUri, + href: this.syncClient.getSettings().remoteUri + }); - container.createSpan({ - text: ` and has indexed approximately ` - }); - container.createSpan({ - text: `${this.syncClient.documentCount}`, - cls: "number" - }); - container.createSpan({ - text: ` documents. ` - }); + container.createSpan({ + text: ` and has indexed approximately ` + }); + container.createSpan({ + text: `${this.syncClient.documentCount}`, + cls: "number" + }); + container.createSpan({ + text: ` documents. ` + }); - if ( - (this.lastRemaining ?? 0) === 0 && - (this.lastHistoryStats?.success ?? 0) === 0 && - (this.lastHistoryStats?.error ?? 0) === 0 - ) { - if (this.syncClient.getSettings().isSyncEnabled) { - container.createSpan({ - text: "Syncing is enabled but VaultLink hasn't found anything to sync yet." - }); - } else { - container.createSpan({ - text: "However, syncing is disabled right now.", - cls: "warning" - }); - } - return; - } + if ( + (this.lastRemaining ?? 0) === 0 && + (this.lastHistoryStats?.success ?? 0) === 0 && + (this.lastHistoryStats?.error ?? 0) === 0 + ) { + if (this.syncClient.getSettings().isSyncEnabled) { + container.createSpan({ + text: "Syncing is enabled but VaultLink hasn't found anything to sync yet." + }); + } else { + container.createSpan({ + text: "However, syncing is disabled right now.", + cls: "warning" + }); + } + return; + } - container.createSpan({ - text: "The plugin has " - }); - container.createSpan({ - text: `${this.lastRemaining ?? 0}`, - cls: "number" - }); - container.createSpan({ - text: " outstanding operations while having succeeded " - }); - container.createSpan({ - text: `${this.lastHistoryStats?.success ?? 0}`, - cls: ["number", "good"] - }); - container.createSpan({ - text: " times and failed " - }); - container.createSpan({ - text: `${this.lastHistoryStats?.error ?? 0}`, - cls: ["number", "bad"] - }); - container.createSpan({ - text: " times." - }); - } + container.createSpan({ + text: "The plugin has " + }); + container.createSpan({ + text: `${this.lastRemaining ?? 0}`, + cls: "number" + }); + container.createSpan({ + text: " outstanding operations while having succeeded " + }); + container.createSpan({ + text: `${this.lastHistoryStats?.success ?? 0}`, + cls: ["number", "good"] + }); + container.createSpan({ + text: " times and failed " + }); + container.createSpan({ + text: `${this.lastHistoryStats?.error ?? 0}`, + cls: ["number", "bad"] + }); + container.createSpan({ + text: " times." + }); + } - private updateDescription(): void { - this.statusChangeListeners.forEach((listener) => { - listener(); - }); - } + private updateDescription(): void { + this.statusChangeListeners.forEach((listener) => { + listener(); + }); + } } diff --git a/frontend/obsidian-plugin/webpack.config.js b/frontend/obsidian-plugin/webpack.config.js index 8b7cb411..b749b20d 100644 --- a/frontend/obsidian-plugin/webpack.config.js +++ b/frontend/obsidian-plugin/webpack.config.js @@ -4,114 +4,114 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const fs = require("fs-extra"); module.exports = (env, argv) => ({ - devtool: argv.mode === "development" ? "inline-source-map" : false, - entry: { - index: "./src/vault-link-plugin.ts" - }, - watchOptions: { - ignored: "**/node_modules" - }, - externals: { - obsidian: "commonjs obsidian", - electron: "commonjs electron", - "@codemirror/autocomplete": "commonjs @codemirror/autocomplete", - "@codemirror/collab": "commonjs @codemirror/collab", - "@codemirror/commands": "commonjs @codemirror/commands", - "@codemirror/language": "commonjs @codemirror/language", - "@codemirror/lint": "commonjs @codemirror/lint", - "@codemirror/search": "commonjs @codemirror/search", - "@codemirror/state": "commonjs @codemirror/state", - "@codemirror/view": "commonjs @codemirror/view" - }, - optimization: { - minimizer: [ - new TerserPlugin({ - terserOptions: { - module: true - } - }) - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: "styles.css" - }), - { - apply: (compiler) => { - if (argv.mode !== "development") { - return; - } + devtool: argv.mode === "development" ? "inline-source-map" : false, + entry: { + index: "./src/vault-link-plugin.ts" + }, + watchOptions: { + ignored: "**/node_modules" + }, + externals: { + obsidian: "commonjs obsidian", + electron: "commonjs electron", + "@codemirror/autocomplete": "commonjs @codemirror/autocomplete", + "@codemirror/collab": "commonjs @codemirror/collab", + "@codemirror/commands": "commonjs @codemirror/commands", + "@codemirror/language": "commonjs @codemirror/language", + "@codemirror/lint": "commonjs @codemirror/lint", + "@codemirror/search": "commonjs @codemirror/search", + "@codemirror/state": "commonjs @codemirror/state", + "@codemirror/view": "commonjs @codemirror/view" + }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true + } + }) + ] + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "styles.css" + }), + { + apply: (compiler) => { + if (argv.mode !== "development") { + return; + } - compiler.hooks.done.tap("Copy Files Plugin", (stats) => { - const source = path.resolve(__dirname, "dist"); - const destinations = [ - "/volumes/syncthing/Desktop/test/test/.obsidian/plugins/vault-link", - "/volumes/syncthing/Desktop/test/test2/.obsidian/plugins/vault-link", - // "/home/andras/obsidian-test/.obsidian/plugins/vault-link" - ]; - destinations.forEach((destination) => { - fs.copy(source, destination) - .then(() => - console.log( - "Files copied successfully after build!" - ) - ) - .catch((err) => - console.error("Error copying files:", err) - ); + compiler.hooks.done.tap("Copy Files Plugin", (stats) => { + const source = path.resolve(__dirname, "dist"); + const destinations = [ + "/volumes/syncthing/Desktop/test/test/.obsidian/plugins/vault-link", + "/volumes/syncthing/Desktop/test/test2/.obsidian/plugins/vault-link", + // "/home/andras/obsidian-test/.obsidian/plugins/vault-link" + ]; + destinations.forEach((destination) => { + fs.copy(source, destination) + .then(() => + console.log( + "Files copied successfully after build!" + ) + ) + .catch((err) => + console.error("Error copying files:", err) + ); - fs.createFile(path.join(destination, ".hotreload")); - }); - }); - } - } - ], - module: { - rules: [ - { - test: /\.json$/i, - type: "asset/resource", - generator: { - filename: "[name][ext]" - } - }, - { - test: /\.scss$/i, - use: [ - MiniCssExtractPlugin.loader, - "css-loader", - "resolve-url-loader", - { - loader: "sass-loader", - options: { - sourceMap: true // required by resolve-url-loader - } - } - ] - }, - { - test: /\.ts$/, - use: ["ts-loader"] - } - ] - }, - resolve: { - extensions: [ - ".ts", - ".js" // required for development - ], - alias: { - root: __dirname, - src: path.resolve(__dirname, "src") - } - }, - output: { - clean: true, - filename: "main.js", - library: { - type: "commonjs" // required for Obsidian - }, - path: path.resolve(__dirname, "dist"), - publicPath: "" - } + fs.createFile(path.join(destination, ".hotreload")); + }); + }); + } + } + ], + module: { + rules: [ + { + test: /\.json$/i, + type: "asset/resource", + generator: { + filename: "[name][ext]" + } + }, + { + test: /\.scss$/i, + use: [ + MiniCssExtractPlugin.loader, + "css-loader", + "resolve-url-loader", + { + loader: "sass-loader", + options: { + sourceMap: true // required by resolve-url-loader + } + } + ] + }, + { + test: /\.ts$/, + use: ["ts-loader"] + } + ] + }, + resolve: { + extensions: [ + ".ts", + ".js" // required for development + ], + alias: { + root: __dirname, + src: path.resolve(__dirname, "src") + } + }, + output: { + clean: true, + filename: "main.js", + library: { + type: "commonjs" // required for Obsidian + }, + path: path.resolve(__dirname, "dist"), + publicPath: "" + } }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 84834fd8..1d45b165 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,4736 +1,7527 @@ { - "name": "my-workspace", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "my-workspace", - "workspaces": [ - "sync-client", - "obsidian-plugin", - "test-client", - "local-client-cli" - ], - "devDependencies": { - "concurrently": "^9.2.1", - "eslint": "9.38.0", - "eslint-plugin-unused-imports": "^4.1.4", - "npm-check-updates": "^19.1.1", - "prettier": "^3.6.2", - "typescript-eslint": "8.41.0" - } - }, - "local-client-cli": { - "version": "0.12.0", - "dependencies": { - "commander": "^14.0.2" - }, - "bin": { - "vaultlink": "dist/cli.js" - }, - "devDependencies": { - "@types/node": "^24.8.1", - "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", - "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1" - } - }, - "node_modules/@codemirror/state": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", - "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.38.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", - "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@codemirror/state": "^6.5.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "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-community/regexpp": { - "version": "4.12.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "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.2", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", - "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.5.1", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "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.1", - "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/@sentry-internal/browser-utils": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.8.0.tgz", - "integrity": "sha512-FaQX9eefc8sh3h3ZQy16U73KiH0xgDldXnrFiWK6OeWg8X4bJpnYbLqEi96LgHiQhjnnz+UQP1GDzH5oFuu5fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/core": "10.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry-internal/feedback": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.8.0.tgz", - "integrity": "sha512-n7SqgFQItq4QSPG7bCjcZcIwK6AatKnnmSDJ/i6e8jXNIyLwkEuY2NyvTXACxVdO/kafGD5VmrwnTo3Ekc1AMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/core": "10.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry-internal/replay": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.8.0.tgz", - "integrity": "sha512-9+qDEoEjv4VopLuOzK1zM4LcvcUsvB5N0iJ+FRCM3XzzOCbebJOniXTQbt5HflJc3XLnQNKFdKfTfgj8M/0RKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry-internal/browser-utils": "10.8.0", - "@sentry/core": "10.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry-internal/replay-canvas": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.8.0.tgz", - "integrity": "sha512-jC4OOwiNgrlIPeXIPMLkaW53BSS1do+toYHoWzzO5AXGpN6jRhanoSj36FpVuH2N3kFnxKVfVxrwh8L+/3vFWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry-internal/replay": "10.8.0", - "@sentry/core": "10.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/browser": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.8.0.tgz", - "integrity": "sha512-2J7HST8/ixCaboq17yFn/j/OEokXSXoCBMXRrFx4FKJggKWZ90e2Iau5mP/IPPhrW+W9zCptCgNMY0167wS4qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry-internal/browser-utils": "10.8.0", - "@sentry-internal/feedback": "10.8.0", - "@sentry-internal/replay": "10.8.0", - "@sentry-internal/replay-canvas": "10.8.0", - "@sentry/core": "10.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/core": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.8.0.tgz", - "integrity": "sha512-scYzM/UOItu4PjEq6CpHLdArpXjIS0laHYxE4YjkIbYIH6VMcXGQbD/FSBClsnCr1wXRnlXfXBzj0hrQAFyw+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@types/codemirror": { - "version": "5.60.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/tern": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.8.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.1.tgz", - "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.14.0" - } - }, - "node_modules/@types/tern": { - "version": "0.23.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", - "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.41.0", - "@typescript-eslint/type-utils": "8.41.0", - "@typescript-eslint/utils": "8.41.0", - "@typescript-eslint/visitor-keys": "8.41.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.41.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", - "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.41.0", - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/typescript-estree": "8.41.0", - "@typescript-eslint/visitor-keys": "8.41.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", - "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.41.0", - "@typescript-eslint/types": "^8.41.0", - "debug": "^4.3.4" - }, - "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.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", - "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/visitor-keys": "8.41.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/tsconfig-utils": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", - "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", - "dev": true, - "license": "MIT", - "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/type-utils": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", - "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/typescript-estree": "8.41.0", - "@typescript-eslint/utils": "8.41.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", - "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", - "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.41.0", - "@typescript-eslint/tsconfig-utils": "8.41.0", - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/visitor-keys": "8.41.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", - "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.41.0", - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/typescript-estree": "8.41.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", - "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.41.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "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/ajv-formats": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "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/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/bufferutil": { - "version": "4.0.9", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/byte-base64": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "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", - "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/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001707", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "rxjs": "7.8.2", - "shell-quote": "1.8.3", - "supports-color": "8.1.1", - "tree-kill": "1.2.2", - "yargs": "17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "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-loader": { - "version": "7.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/date-fns": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "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/electron-to-chromium": { - "version": "1.5.127", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.14.0", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.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", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unused-imports": { - "version": "4.1.4", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", - "eslint": "^9.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "dev": true, - "license": "ISC" - }, - "node_modules/fs-extra": { - "version": "11.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "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", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "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", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/globals": { - "version": "14.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immutable": { - "version": "5.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/local-client-cli": { - "resolved": "local-client-cli", - "link": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "dev": true, - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.17.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "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", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "dev": true, - "license": "MIT" - }, - "node_modules/npm-check-updates": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.1.1.tgz", - "integrity": "sha512-vy/uNbaK6Xfj/QzM8OXeALZak67E0uHjUlbdT1YGy4bdj0xlBU6AVd+8bscY8vlDpyzL6Y7mxcrX8kzEDeEpNg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "ncu": "build/cli.js", - "npm-check-updates": "build/cli.js" - }, - "engines": { - "node": ">=20.0.0", - "npm": ">=8.12.1" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obsidian": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.2.tgz", - "integrity": "sha512-bX03YCHf06OTzI/D+QK71ajCPCmwr/cjxzlVXjQa10DjK5iHRWhtJJpp83arSCyayFMp23u+UHcY7hxcEx2Mvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/codemirror": "5.60.8", - "moment": "2.29.4" - }, - "peerDependencies": { - "@codemirror/state": "6.5.0", - "@codemirror/view": "6.38.1" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "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", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "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.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "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/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/reconcile-text": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/reconcile-text/-/reconcile-text-0.8.0.tgz", - "integrity": "sha512-evskVha3YgpP2ZelsFxP9t7CuKnwE7TrsH3FdrH2mfKbzjUWiNF7scHXsFbFS921lmFlAOB94DHNAWPvL34Mqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/regex-parser": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/resolve-url-loader/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.91.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz", - "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==", - "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-loader": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.6.tgz", - "integrity": "sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "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", - "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", - "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", - "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/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "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/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sync-client": { - "resolved": "sync-client", - "link": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.39.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/test-client": { - "resolved": "test-client", - "link": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-loader": { - "version": "9.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", - "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.41.0", - "@typescript-eslint/parser": "8.41.0", - "@typescript-eslint/typescript-estree": "8.41.0", - "@typescript-eslint/utils": "8.41.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url": { - "version": "0.11.4", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.12.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/utf-8-validate": { - "version": "6.0.5", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/vault-link-obsidian-plugin": { - "resolved": "obsidian-plugin", - "link": true - }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/watchpack": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.99.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/colorette": { - "version": "2.0.20", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "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/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/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "obsidian-plugin": { - "name": "vault-link-obsidian-plugin", - "version": "0.12.0", - "license": "MIT", - "devDependencies": { - "@types/node": "^24.8.1", - "css-loader": "^7.1.2", - "date-fns": "^4.1.0", - "file-loader": "^6.2.0", - "fs-extra": "^11.3.0", - "mini-css-extract-plugin": "^2.9.2", - "obsidian": "1.10.2", - "reconcile-text": "^0.8.0", - "resolve-url-loader": "^5.0.0", - "sass": "^1.91.0", - "sass-loader": "^16.0.6", - "sync-client": "file:../sync-client", - "terser-webpack-plugin": "^5.3.14", - "ts-loader": "^9.5.2", - "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "url": "^0.11.4", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1" - } - }, - "sync-client": { - "version": "0.12.0", - "devDependencies": { - "@sentry/browser": "^10.8.0", - "@types/node": "^24.8.1", - "byte-base64": "^1.1.0", - "minimatch": "^10.0.1", - "p-queue": "^8.1.0", - "reconcile-text": "^0.8.0", - "ts-loader": "^9.5.2", - "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "uuid": "^13.0.0", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1", - "ws": "^8.18.3" - } - }, - "sync-client/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "sync-client/node_modules/minimatch": { - "version": "10.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "test-client": { - "version": "0.12.0", - "bin": { - "test-client": "dist/cli.js" - }, - "devDependencies": { - "@types/node": "^24.8.1", - "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", - "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "uuid": "^13.0.0", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1" - } - } - } + "name": "my-workspace", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "my-workspace", + "workspaces": [ + "sync-client", + "obsidian-plugin", + "test-client", + "local-client-cli" + ], + "devDependencies": { + "concurrently": "^9.2.1", + "eclint": "^2.8.1", + "eslint": "9.38.0", + "eslint-plugin-unused-imports": "^4.1.4", + "npm-check-updates": "^19.1.1", + "prettier": "^3.6.2", + "typescript-eslint": "8.41.0" + } + }, + "local-client-cli": { + "version": "0.12.0", + "dependencies": { + "commander": "^14.0.2" + }, + "bin": { + "vaultlink": "dist/cli.js" + }, + "devDependencies": { + "@types/node": "^24.8.1", + "sync-client": "file:../sync-client", + "ts-loader": "^9.5.2", + "tslib": "2.8.1", + "tsx": "^4.20.6", + "typescript": "5.8.3", + "webpack": "^5.99.9", + "webpack-cli": "^6.0.1" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", + "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "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-community/regexpp": { + "version": "4.12.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", + "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", + "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "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.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "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.1", + "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/@sentry-internal/browser-utils": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.8.0.tgz", + "integrity": "sha512-FaQX9eefc8sh3h3ZQy16U73KiH0xgDldXnrFiWK6OeWg8X4bJpnYbLqEi96LgHiQhjnnz+UQP1GDzH5oFuu5fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/feedback": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.8.0.tgz", + "integrity": "sha512-n7SqgFQItq4QSPG7bCjcZcIwK6AatKnnmSDJ/i6e8jXNIyLwkEuY2NyvTXACxVdO/kafGD5VmrwnTo3Ekc1AMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.8.0.tgz", + "integrity": "sha512-9+qDEoEjv4VopLuOzK1zM4LcvcUsvB5N0iJ+FRCM3XzzOCbebJOniXTQbt5HflJc3XLnQNKFdKfTfgj8M/0RKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.8.0.tgz", + "integrity": "sha512-jC4OOwiNgrlIPeXIPMLkaW53BSS1do+toYHoWzzO5AXGpN6jRhanoSj36FpVuH2N3kFnxKVfVxrwh8L+/3vFWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry-internal/replay": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/browser": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.8.0.tgz", + "integrity": "sha512-2J7HST8/ixCaboq17yFn/j/OEokXSXoCBMXRrFx4FKJggKWZ90e2Iau5mP/IPPhrW+W9zCptCgNMY0167wS4qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.8.0", + "@sentry-internal/feedback": "10.8.0", + "@sentry-internal/replay": "10.8.0", + "@sentry-internal/replay-canvas": "10.8.0", + "@sentry/core": "10.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/core": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.8.0.tgz", + "integrity": "sha512-scYzM/UOItu4PjEq6CpHLdArpXjIS0laHYxE4YjkIbYIH6VMcXGQbD/FSBClsnCr1wXRnlXfXBzj0hrQAFyw+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/tern": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.1.tgz", + "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.41.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", + "debug": "^4.3.4" + }, + "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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.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/tsconfig-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", + "dev": true, + "license": "MIT", + "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/type-utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "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/ajv-formats": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "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/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-equals": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", + "integrity": "sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/buffered-spawn": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/buffered-spawn/-/buffered-spawn-3.3.2.tgz", + "integrity": "sha512-YVdiyWEbFCH+lu3USRFoH6UtvS3mr/e/obxZNbOkbbL3heLEUYb3YpTjKUQFWt5d3k9ZILabY8Kh2pp+i4SQqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/buffered-spawn/node_modules/cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/buffered-spawn/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/bufferstreams": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz", + "integrity": "sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.6" + }, + "engines": { + "node": ">=6.9.5" + } + }, + "node_modules/bufferutil": { + "version": "4.0.9", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/byte-base64": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "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", + "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/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001707", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/checkstyle-formatter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/checkstyle-formatter/-/checkstyle-formatter-1.1.0.tgz", + "integrity": "sha512-mak+5ooX5cDFBBIhsR+NqxoQ9+JQRqupr49G2PiUYXKn8OntoI9osjhECaScrzqq1l4phuRmK1VlMdxHdpwZvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-escape": "^1.0.0" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-1.1.0.tgz", + "integrity": "sha512-bAtZo0u82gCfaAGfSNxUdTI9mNyza7D8w4CVCcaOsy7sgwDzvx6ekr6cuWJqY3UGzgnQ1+4wgENup5eIhgxEYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^1.0.0", + "string-width": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "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-loader": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-format": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-0.0.2.tgz", + "integrity": "sha512-M4obuJx8jU5T91lcbwi0+QPNVaWOY1DQYz5xUuKYWO93osVzB2ZPqyDUc5T+mDjbA1X8VOb4JDZ+8r2MrSOp7Q==", + "deprecated": "0.x is no longer supported. Please upgrade to 4.x or higher.", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "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/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/eclint": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eclint/-/eclint-2.8.1.tgz", + "integrity": "sha512-0u1UubFXSOgZgXNhuPeliYyTFmjWStVph8JR6uD6NDuxl3xI5VSCsA1KX6/BSYtM9v4wQMifGoNFfN5VlRn4LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "editorconfig": "^0.15.2", + "file-type": "^10.1.0", + "gulp-exclude-gitignore": "^1.2.0", + "gulp-filter": "^5.1.0", + "gulp-reporter": "^2.9.0", + "gulp-tap": "^1.0.1", + "linez": "^4.1.4", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "os-locale": "^3.0.1", + "plugin-error": "^1.0.1", + "through2": "^2.0.3", + "vinyl": "^2.2.0", + "vinyl-fs": "^3.0.3", + "yargs": "^12.0.2" + }, + "bin": { + "eclint": "bin/eclint.js" + } + }, + "node_modules/eclint/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eclint/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/eclint/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eclint/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true, + "license": "ISC" + }, + "node_modules/eclint/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eclint/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eclint/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eclint/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eclint/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eclint/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eclint/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eclint/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eclint/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eclint/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eclint/node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eclint/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eclint/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/eclint/node_modules/yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "node_modules/eclint/node_modules/yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "bin": { + "editorconfig": "bin/editorconfig" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.127", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/emphasize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emphasize/-/emphasize-2.1.0.tgz", + "integrity": "sha512-wRlO0Qulw2jieQynsS3STzTabIhHCyjTjZraSkchOiT8rdvWZlahJAJ69HRxwGkv2NThmci2MSnDfJ60jB39tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.0", + "highlight.js": "~9.12.0", + "lowlight": "~1.9.0" + } + }, + "node_modules/emphasize/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/emphasize/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/emphasize/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/emphasize/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/emphasize/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/emphasize/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emphasize/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", + "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.1", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.38.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.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", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/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/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/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "dev": true, + "license": "ISC" + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/follow-redirects/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/follow-redirects/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/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-exclude-gitignore": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gulp-exclude-gitignore/-/gulp-exclude-gitignore-1.2.0.tgz", + "integrity": "sha512-J3LCmz9C1UU1pxf5Npx6SNc5o9YQptyc9IHaqLiBlihZmg44jaaTplWUZ0JPQkMdOTae0YgEDvT9TKlUWDSMUA==", + "dev": true, + "license": "ISC", + "dependencies": { + "gulp-ignore": "^2.0.2" + } + }, + "node_modules/gulp-filter": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-5.1.0.tgz", + "integrity": "sha512-ZERu1ipbPmjrNQ2dQD6lL4BjrJQG66P/c5XiyMMBqV+tUAJ+fLOyYIL/qnXd2pHmw/G/r7CLQb9ttANvQWbpfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "multimatch": "^2.0.0", + "plugin-error": "^0.1.2", + "streamfilter": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-filter/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-filter/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-filter/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-filter/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-filter/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-ignore": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-ignore/-/gulp-ignore-2.0.2.tgz", + "integrity": "sha512-KGtd/qgp0FLDlei986/aZ5xSyw1cqJ2BsiaWht0L0PzaQXxYKRCMkFcDPQ8fQx6JVA6Gx9OefmBFzxTtop5hMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gulp-match": "^1.0.3", + "through2": "^2.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.3" + } + }, + "node_modules/gulp-reporter": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/gulp-reporter/-/gulp-reporter-2.10.0.tgz", + "integrity": "sha512-HeruxN7TL/enOB+pJfFmeekVsXsZzQvVGpL7vOLdUe7y7VdqHUvMQRRW5qMIvVSKqRs3EtQiR/kURu3WWfXq6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^3.1.0", + "axios": "^0.18.0", + "buffered-spawn": "^3.3.2", + "bufferstreams": "^2.0.1", + "chalk": "^2.4.1", + "checkstyle-formatter": "^1.1.0", + "ci-info": "^2.0.0", + "cli-truncate": "^1.1.0", + "emphasize": "^2.0.0", + "fancy-log": "^1.3.3", + "fs-extra": "^7.0.1", + "in-gfw": "^1.2.0", + "is-windows": "^1.0.2", + "js-yaml": "^3.12.0", + "junit-report-builder": "^1.3.1", + "lodash.get": "^4.4.2", + "os-locale": "^3.0.1", + "plugin-error": "^1.0.1", + "string-width": "^3.0.0", + "term-size": "^1.2.0", + "through2": "^3.0.0", + "to-time": "^1.0.2" + } + }, + "node_modules/gulp-reporter/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-reporter/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-reporter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gulp-reporter/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-reporter/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/gulp-reporter/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-reporter/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-reporter/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gulp-reporter/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/gulp-reporter/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-reporter/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-reporter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gulp-reporter/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/gulp-reporter/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-reporter/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-reporter/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-reporter/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-reporter/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/gulp-tap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gulp-tap/-/gulp-tap-1.0.1.tgz", + "integrity": "sha512-VpCARRSyr+WP16JGnoIg98/AcmyQjOwCpQgYoE35CWTdEMSbpgtAIK2fndqv2yY7aXstW27v3ZNBs0Ltb0Zkbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.3" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha512-qNnYpBDO/FQwYVur1+sQBQw7v0cxso1nOYLklqWh6af8ROwwTVoII5+kf/BVa8354WL4ad6rURHYGUXCbD9mMg==", + "deprecated": "Version no longer supported. Upgrade to @latest", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "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/icss-utils": { + "version": "5.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/in-gfw": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/in-gfw/-/in-gfw-1.2.0.tgz", + "integrity": "sha512-LgSoQXzuSS/x/nh0eIggq7PsI7gs/sQdXNEolRmHaFUj6YMFmPO1kxQ7XpcT3nPpC3DMwYiJmgnluqJmFXYiMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.2", + "is-wsl": "^1.1.0", + "mem": "^3.0.1" + } + }, + "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/interpret": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/junit-report-builder": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-1.3.3.tgz", + "integrity": "sha512-75bwaXjP/3ogyzOSkkcshXGG7z74edkJjgTZlJGAyzxlOHaguexM3VLG6JyD9ZBF8mlpgsUPB1sIWU4LISgeJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "0.0.2", + "lodash": "^4.17.15", + "mkdirp": "^0.5.0", + "xmlbuilder": "^10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "invert-kv": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "license": "MIT", + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linez": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/linez/-/linez-4.1.4.tgz", + "integrity": "sha512-TsqcAfotPMB9xodBIklBaJz3sRIXtkca8Kv/MO8nzAufsitCKRoYWU5MZccdCVYB81tGexYHRsrSIEiJsQhpVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equals": "^1.0.4", + "iconv-lite": "^0.4.15" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/local-client-cli": { + "resolved": "local-client-cli", + "link": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lowlight": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.9.2.tgz", + "integrity": "sha512-Ek18ElVCf/wF/jEm1b92gTnigh94CtBNWiZ2ad+vTgW7cTmQxUY3I98BjHK68gZAJEWmybGBZgx9qv3QxLQB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^1.0.2", + "highlight.js": "~9.12.0" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mem": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-3.0.1.tgz", + "integrity": "sha512-QKs47bslvOE0NbXOqG6lMxn6Bk0Iuw0vfrIeLykmQle2LkCw1p48dZDdzE+D88b/xqRJcZGcMNeDvSVma+NuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "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/moment": { + "version": "2.29.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha512-0mzK8ymiWdehTBiJh0vClAzGyQbdtyWqzSVx//EK4N/D+599RFlGfTAsKw2zMSABtDG9C6Ul2+t8f2Lbdjf5mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "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", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/npm-check-updates": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.1.1.tgz", + "integrity": "sha512-vy/uNbaK6Xfj/QzM8OXeALZak67E0uHjUlbdT1YGy4bdj0xlBU6AVd+8bscY8vlDpyzL6Y7mxcrX8kzEDeEpNg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ncu": "build/cli.js", + "npm-check-updates": "build/cli.js" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=8.12.1" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obsidian": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.2.tgz", + "integrity": "sha512-bX03YCHf06OTzI/D+QK71ajCPCmwr/cjxzlVXjQa10DjK5iHRWhtJJpp83arSCyayFMp23u+UHcY7hxcEx2Mvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "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/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/os-locale/node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "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", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "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/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/reconcile-text": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/reconcile-text/-/reconcile-text-0.8.0.tgz", + "integrity": "sha512-evskVha3YgpP2ZelsFxP9t7CuKnwE7TrsH3FdrH2mfKbzjUWiNF7scHXsFbFS921lmFlAOB94DHNAWPvL34Mqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/regex-parser": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-buffer/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/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.91.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz", + "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==", + "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-loader": { + "version": "16.0.6", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.6.tgz", + "integrity": "sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "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", + "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", + "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", + "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/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamfilter": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-1.0.7.tgz", + "integrity": "sha512-Gk6KZM+yNA1JpW0KzlZIhjo3EaBJDkYfXtYSbOwNIQ7Zd6006E6+sCFlW1NDvFG/vnXhKmw6TJJgiEQg/8lXfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "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/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sync-client": { + "resolved": "sync-client", + "link": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^0.7.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/term-size/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/term-size/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/term-size/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/terser": { + "version": "5.39.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-client": { + "resolved": "test-client", + "link": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/to-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-time/-/to-time-1.0.2.tgz", + "integrity": "sha512-+wqaiQvnido2DI1bpiQ/Zv1LiOE9Fd0v35ySnNeqFmKNYJTJY/+ENI+3sHXCMzbAAOR/43aNyLM0XTpi0/zSQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^2.4.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.4.0.tgz", + "integrity": "sha512-V6QarSfeSgDipGA9EZdoIzu03ZDlOFkk+FbEP5cwgrZXN3iIkYR91IjU2EnM6rB835kGQsqHX8qncObTXV+6KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "3.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.11.4", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/utf-8-validate": { + "version": "6.0.5", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vault-link-obsidian-plugin": { + "resolved": "obsidian-plugin", + "link": true + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/watchpack": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.99.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wildcard": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "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/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/xml-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", + "integrity": "sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==", + "dev": true, + "license": "MIT License" + }, + "node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "obsidian-plugin": { + "name": "vault-link-obsidian-plugin", + "version": "0.12.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^24.8.1", + "css-loader": "^7.1.2", + "date-fns": "^4.1.0", + "file-loader": "^6.2.0", + "fs-extra": "^11.3.0", + "mini-css-extract-plugin": "^2.9.2", + "obsidian": "1.10.2", + "reconcile-text": "^0.8.0", + "resolve-url-loader": "^5.0.0", + "sass": "^1.91.0", + "sass-loader": "^16.0.6", + "sync-client": "file:../sync-client", + "terser-webpack-plugin": "^5.3.14", + "ts-loader": "^9.5.2", + "tslib": "2.8.1", + "tsx": "^4.20.6", + "typescript": "5.8.3", + "url": "^0.11.4", + "webpack": "^5.99.9", + "webpack-cli": "^6.0.1" + } + }, + "sync-client": { + "version": "0.12.0", + "devDependencies": { + "@sentry/browser": "^10.8.0", + "@types/node": "^24.8.1", + "byte-base64": "^1.1.0", + "minimatch": "^10.0.1", + "p-queue": "^8.1.0", + "reconcile-text": "^0.8.0", + "ts-loader": "^9.5.2", + "tslib": "2.8.1", + "tsx": "^4.20.6", + "typescript": "5.8.3", + "uuid": "^13.0.0", + "webpack": "^5.99.9", + "webpack-cli": "^6.0.1", + "webpack-merge": "^6.0.1", + "ws": "^8.18.3" + } + }, + "sync-client/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "sync-client/node_modules/minimatch": { + "version": "10.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "test-client": { + "version": "0.12.0", + "bin": { + "test-client": "dist/cli.js" + }, + "devDependencies": { + "@types/node": "^24.8.1", + "sync-client": "file:../sync-client", + "ts-loader": "^9.5.2", + "tslib": "2.8.1", + "tsx": "^4.20.6", + "typescript": "5.8.3", + "uuid": "^13.0.0", + "webpack": "^5.99.9", + "webpack-cli": "^6.0.1" + } + } + } } diff --git a/frontend/package.json b/frontend/package.json index ddd9e1c3..03bab82f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,32 +1,32 @@ { - "name": "my-workspace", - "private": true, - "workspaces": [ - "sync-client", - "obsidian-plugin", - "test-client", - "local-client-cli" - ], - "prettier": { - "trailingComma": "none", - "tabWidth": 4, - "useTabs": true, - "endOfLine": "lf" - }, - "scripts": { - "build": "npm run build --workspaces", - "dev": "concurrently --kill-others \"npm run dev -w sync-client\" \"npm run dev -w obsidian-plugin\"", - "test": "npm run test --workspaces", - "lint": "eslint --fix sync-client obsidian-plugin test-client local-client-cli && prettier --write \"**/*.ts\"", - "update": "ncu -u -ws" - }, - "devDependencies": { - "concurrently": "^9.2.1", - "eclint": "^2.8.1", - "eslint": "9.38.0", - "eslint-plugin-unused-imports": "^4.1.4", - "npm-check-updates": "^19.1.1", - "prettier": "^3.6.2", - "typescript-eslint": "8.41.0" - } + "name": "my-workspace", + "private": true, + "workspaces": [ + "sync-client", + "obsidian-plugin", + "test-client", + "local-client-cli" + ], + "prettier": { + "trailingComma": "none", + "tabWidth": 4, + "useTabs": true, + "endOfLine": "lf" + }, + "scripts": { + "build": "npm run build --workspaces", + "dev": "concurrently --kill-others \"npm run dev -w sync-client\" \"npm run dev -w obsidian-plugin\"", + "test": "npm run test --workspaces", + "lint": "eslint --fix sync-client obsidian-plugin test-client local-client-cli && prettier --write \"**/*.ts\"", + "update": "ncu -u -ws" + }, + "devDependencies": { + "concurrently": "^9.2.1", + "eclint": "^2.8.1", + "eslint": "9.38.0", + "eslint-plugin-unused-imports": "^4.1.4", + "npm-check-updates": "^19.1.1", + "prettier": "^3.6.2", + "typescript-eslint": "8.41.0" + } } diff --git a/frontend/sync-client/src/file-operations/file-not-found-error.ts b/frontend/sync-client/src/file-operations/file-not-found-error.ts index 8725e81e..b8acd265 100644 --- a/frontend/sync-client/src/file-operations/file-not-found-error.ts +++ b/frontend/sync-client/src/file-operations/file-not-found-error.ts @@ -1,9 +1,9 @@ export class FileNotFoundError extends Error { - public constructor( - message: string, - public readonly filePath: string - ) { - super(message); - this.name = "FileNotFoundError"; - } + public constructor( + message: string, + public readonly filePath: string + ) { + super(message); + this.name = "FileNotFoundError"; + } } diff --git a/frontend/sync-client/src/file-operations/file-operations.test.ts b/frontend/sync-client/src/file-operations/file-operations.test.ts index 353312a3..35595e6e 100644 --- a/frontend/sync-client/src/file-operations/file-operations.test.ts +++ b/frontend/sync-client/src/file-operations/file-operations.test.ts @@ -1,8 +1,8 @@ import { describe, it } from "node:test"; import type { - Database, - DocumentRecord, - RelativePath + Database, + DocumentRecord, + RelativePath } from "../persistence/database"; import { FileOperations } from "./file-operations"; import { Logger } from "../tracing/logger"; @@ -12,224 +12,224 @@ import type { TextWithCursors } from "reconcile-text"; import type { ServerConfig, ServerConfigData } from "../services/server-config"; class MockServerConfig implements Pick { - public getConfig(): ServerConfigData { - return { - mergeableFileExtensions: ["md", "txt"], - supportedApiVersion: 1, - isAuthenticated: true - }; - } + public getConfig(): ServerConfigData { + return { + mergeableFileExtensions: ["md", "txt"], + supportedApiVersion: 1, + isAuthenticated: true + }; + } } class MockDatabase implements Partial { - public getLatestDocumentByRelativePath( - _find: RelativePath - ): DocumentRecord | undefined { - // no-op - return undefined; - } + public getLatestDocumentByRelativePath( + _find: RelativePath + ): DocumentRecord | undefined { + // no-op + return undefined; + } - public move( - _oldRelativePath: RelativePath, - _newRelativePath: RelativePath - ): void { - // no-op - } + public move( + _oldRelativePath: RelativePath, + _newRelativePath: RelativePath + ): void { + // no-op + } } class FakeFileSystemOperations implements FileSystemOperations { - public readonly names = new Set(); + public readonly names = new Set(); - public async listFilesRecursively( - _root: RelativePath | undefined - ): Promise { - return ["file.md"]; - } - public async read(_path: RelativePath): Promise { - throw new Error("Method not implemented."); - } - public async write( - path: RelativePath, - _content: Uint8Array - ): Promise { - this.names.add(path); - } - public async atomicUpdateText( - _path: RelativePath, - _updater: (current: TextWithCursors) => TextWithCursors - ): Promise { - throw new Error("Method not implemented."); - } - public async getFileSize(_path: RelativePath): Promise { - throw new Error("Method not implemented."); - } - public async getModificationTime(_path: RelativePath): Promise { - throw new Error("Method not implemented."); - } - public async exists(path: RelativePath): Promise { - return this.names.has(path); - } - public async createDirectory(_path: RelativePath): Promise { - // this is called but irrelevant for this mock - } - public async delete(_path: RelativePath): Promise { - throw new Error("Method not implemented."); - } - public async rename( - oldPath: RelativePath, - newPath: RelativePath - ): Promise { - this.names.delete(oldPath); - this.names.add(newPath); - } + public async listFilesRecursively( + _root: RelativePath | undefined + ): Promise { + return ["file.md"]; + } + public async read(_path: RelativePath): Promise { + throw new Error("Method not implemented."); + } + public async write( + path: RelativePath, + _content: Uint8Array + ): Promise { + this.names.add(path); + } + public async atomicUpdateText( + _path: RelativePath, + _updater: (current: TextWithCursors) => TextWithCursors + ): Promise { + throw new Error("Method not implemented."); + } + public async getFileSize(_path: RelativePath): Promise { + throw new Error("Method not implemented."); + } + public async getModificationTime(_path: RelativePath): Promise { + throw new Error("Method not implemented."); + } + public async exists(path: RelativePath): Promise { + return this.names.has(path); + } + public async createDirectory(_path: RelativePath): Promise { + // this is called but irrelevant for this mock + } + public async delete(_path: RelativePath): Promise { + throw new Error("Method not implemented."); + } + public async rename( + oldPath: RelativePath, + newPath: RelativePath + ): Promise { + this.names.delete(oldPath); + this.names.add(newPath); + } } describe("File operations", () => { - it("should deconflict renames", async () => { - const fileSystemOperations = new FakeFileSystemOperations(); - const fileOperations = new FileOperations( - new Logger(), - new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - fileSystemOperations, - new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - ); + it("should deconflict renames", async () => { + const fileSystemOperations = new FakeFileSystemOperations(); + const fileOperations = new FileOperations( + new Logger(), + new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + fileSystemOperations, + new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + ); - await fileOperations.create("a", new Uint8Array()); - assertSetContainsExactly(fileSystemOperations.names, "a"); - await fileOperations.move("a", "b"); - assertSetContainsExactly(fileSystemOperations.names, "b"); + await fileOperations.create("a", new Uint8Array()); + assertSetContainsExactly(fileSystemOperations.names, "a"); + await fileOperations.move("a", "b"); + assertSetContainsExactly(fileSystemOperations.names, "b"); - await fileOperations.create("c", new Uint8Array()); - assertSetContainsExactly(fileSystemOperations.names, "b", "c"); + await fileOperations.create("c", new Uint8Array()); + assertSetContainsExactly(fileSystemOperations.names, "b", "c"); - await fileOperations.move("c", "b"); - assertSetContainsExactly(fileSystemOperations.names, "b", "b (1)"); + await fileOperations.move("c", "b"); + assertSetContainsExactly(fileSystemOperations.names, "b", "b (1)"); - await fileOperations.create("c", new Uint8Array()); - await fileOperations.move("c", "b"); - assertSetContainsExactly( - fileSystemOperations.names, - "b", - "b (1)", - "b (2)" - ); - }); + await fileOperations.create("c", new Uint8Array()); + await fileOperations.move("c", "b"); + assertSetContainsExactly( + fileSystemOperations.names, + "b", + "b (1)", + "b (2)" + ); + }); - it("should deconflict renames with file extension", async () => { - const fileSystemOperations = new FakeFileSystemOperations(); - const fileOperations = new FileOperations( - new Logger(), - new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - fileSystemOperations, - new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - ); + it("should deconflict renames with file extension", async () => { + const fileSystemOperations = new FakeFileSystemOperations(); + const fileOperations = new FileOperations( + new Logger(), + new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + fileSystemOperations, + new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + ); - await fileOperations.create("b.md", new Uint8Array()); - await fileOperations.create("c.md", new Uint8Array()); - await fileOperations.move("c.md", "b.md"); - assertSetContainsExactly( - fileSystemOperations.names, - "b.md", - "b (1).md" - ); + await fileOperations.create("b.md", new Uint8Array()); + await fileOperations.create("c.md", new Uint8Array()); + await fileOperations.move("c.md", "b.md"); + assertSetContainsExactly( + fileSystemOperations.names, + "b.md", + "b (1).md" + ); - await fileOperations.create("d.md", new Uint8Array()); - await fileOperations.move("d.md", "b.md"); - assertSetContainsExactly( - fileSystemOperations.names, - "b.md", - "b (1).md", - "b (2).md" - ); + await fileOperations.create("d.md", new Uint8Array()); + await fileOperations.move("d.md", "b.md"); + assertSetContainsExactly( + fileSystemOperations.names, + "b.md", + "b (1).md", + "b (2).md" + ); - await fileOperations.create("file-23.md", new Uint8Array()); - await fileOperations.create("file-23 (1).md", new Uint8Array()); - await fileOperations.move("file-23.md", "file-23 (1).md"); - assertSetContainsExactly( - fileSystemOperations.names, - "b.md", - "b (1).md", - "b (2).md", - "file-23 (1).md", - "file-23 (2).md" - ); - }); + await fileOperations.create("file-23.md", new Uint8Array()); + await fileOperations.create("file-23 (1).md", new Uint8Array()); + await fileOperations.move("file-23.md", "file-23 (1).md"); + assertSetContainsExactly( + fileSystemOperations.names, + "b.md", + "b (1).md", + "b (2).md", + "file-23 (1).md", + "file-23 (2).md" + ); + }); - it("should deconflict renames with paths", async () => { - const fileSystemOperations = new FakeFileSystemOperations(); - const fileOperations = new FileOperations( - new Logger(), - new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - fileSystemOperations, - new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - ); + it("should deconflict renames with paths", async () => { + const fileSystemOperations = new FakeFileSystemOperations(); + const fileOperations = new FileOperations( + new Logger(), + new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + fileSystemOperations, + new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + ); - await fileOperations.create("a/b.c/d", new Uint8Array()); - await fileOperations.create("a/b.c/e", new Uint8Array()); - await fileOperations.move("a/b.c/d", "a/b.c/e"); - assertSetContainsExactly( - fileSystemOperations.names, - "a/b.c/e", - "a/b.c/e (1)" - ); - }); + await fileOperations.create("a/b.c/d", new Uint8Array()); + await fileOperations.create("a/b.c/e", new Uint8Array()); + await fileOperations.move("a/b.c/d", "a/b.c/e"); + assertSetContainsExactly( + fileSystemOperations.names, + "a/b.c/e", + "a/b.c/e (1)" + ); + }); - it("should continue deconfliction from existing number in filename", async () => { - const fileSystemOperations = new FakeFileSystemOperations(); - const fileOperations = new FileOperations( - new Logger(), - new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - fileSystemOperations, - new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - ); + it("should continue deconfliction from existing number in filename", async () => { + const fileSystemOperations = new FakeFileSystemOperations(); + const fileOperations = new FileOperations( + new Logger(), + new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + fileSystemOperations, + new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + ); - await fileOperations.create("document (5).md", new Uint8Array()); - await fileOperations.create("other.md", new Uint8Array()); + await fileOperations.create("document (5).md", new Uint8Array()); + await fileOperations.create("other.md", new Uint8Array()); - await fileOperations.move("other.md", "document (5).md"); - assertSetContainsExactly( - fileSystemOperations.names, - "document (5).md", - "document (6).md" - ); + await fileOperations.move("other.md", "document (5).md"); + assertSetContainsExactly( + fileSystemOperations.names, + "document (5).md", + "document (6).md" + ); - await fileOperations.create("another.md", new Uint8Array()); - await fileOperations.move("another.md", "document (5).md"); - assertSetContainsExactly( - fileSystemOperations.names, - "document (5).md", - "document (6).md", - "document (7).md" - ); - }); + await fileOperations.create("another.md", new Uint8Array()); + await fileOperations.move("another.md", "document (5).md"); + assertSetContainsExactly( + fileSystemOperations.names, + "document (5).md", + "document (6).md", + "document (7).md" + ); + }); - it("should handle dotfiles correctly", async () => { - const fileSystemOperations = new FakeFileSystemOperations(); - const fileOperations = new FileOperations( - new Logger(), - new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - fileSystemOperations, - new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - ); + it("should handle dotfiles correctly", async () => { + const fileSystemOperations = new FakeFileSystemOperations(); + const fileOperations = new FileOperations( + new Logger(), + new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + fileSystemOperations, + new MockServerConfig() as ServerConfig // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + ); - await fileOperations.create(".gitignore", new Uint8Array()); - await fileOperations.create("temp", new Uint8Array()); - await fileOperations.move("temp", ".gitignore"); - assertSetContainsExactly( - fileSystemOperations.names, - ".gitignore", - ".gitignore (1)" - ); + await fileOperations.create(".gitignore", new Uint8Array()); + await fileOperations.create("temp", new Uint8Array()); + await fileOperations.move("temp", ".gitignore"); + assertSetContainsExactly( + fileSystemOperations.names, + ".gitignore", + ".gitignore (1)" + ); - await fileOperations.create(".config.json", new Uint8Array()); - await fileOperations.create("temp2", new Uint8Array()); - await fileOperations.move("temp2", ".config.json"); - assertSetContainsExactly( - fileSystemOperations.names, - ".gitignore", - ".gitignore (1)", - ".config.json", - ".config (1).json" - ); - }); + await fileOperations.create(".config.json", new Uint8Array()); + await fileOperations.create("temp2", new Uint8Array()); + await fileOperations.move("temp2", ".config.json"); + assertSetContainsExactly( + fileSystemOperations.names, + ".gitignore", + ".gitignore (1)", + ".config.json", + ".config (1).json" + ); + }); }); diff --git a/frontend/sync-client/src/file-operations/file-operations.ts b/frontend/sync-client/src/file-operations/file-operations.ts index 6bfdc305..4d3e517d 100644 --- a/frontend/sync-client/src/file-operations/file-operations.ts +++ b/frontend/sync-client/src/file-operations/file-operations.ts @@ -9,283 +9,283 @@ import { isBinary } from "../utils/is-binary"; import type { ServerConfig } from "../services/server-config"; export class FileOperations { - private static readonly PARENTHESES_REGEX = / \((?\d+)\)$/; - private readonly fs: SafeFileSystemOperations; + private static readonly PARENTHESES_REGEX = / \((?\d+)\)$/; + private readonly fs: SafeFileSystemOperations; - public constructor( - private readonly logger: Logger, - private readonly database: Database, - fs: FileSystemOperations, - private readonly serverConfig: ServerConfig, - private readonly nativeLineEndings = "\n" - ) { - this.fs = new SafeFileSystemOperations(fs, logger); - } + public constructor( + private readonly logger: Logger, + private readonly database: Database, + fs: FileSystemOperations, + private readonly serverConfig: ServerConfig, + private readonly nativeLineEndings = "\n" + ) { + this.fs = new SafeFileSystemOperations(fs, logger); + } - private static getParentDirAndFile( - path: RelativePath - ): [RelativePath, RelativePath] { - const pathParts = path.split("/"); - const fileName = pathParts.pop(); - if (fileName == null || fileName === "") { - throw new Error(`Path '${path}' cannot be empty`); - } + private static getParentDirAndFile( + path: RelativePath + ): [RelativePath, RelativePath] { + const pathParts = path.split("/"); + const fileName = pathParts.pop(); + if (fileName == null || fileName === "") { + throw new Error(`Path '${path}' cannot be empty`); + } - return [pathParts.join("/"), fileName]; - } + return [pathParts.join("/"), fileName]; + } - public async listFilesRecursively( - root: RelativePath | undefined = undefined - ): Promise { - return this.fs.listFilesRecursively(root); - } + public async listFilesRecursively( + root: RelativePath | undefined = undefined + ): Promise { + return this.fs.listFilesRecursively(root); + } - public async read(path: RelativePath): Promise { - return this.fromNativeLineEndings(await this.fs.read(path)); - } + public async read(path: RelativePath): Promise { + return this.fromNativeLineEndings(await this.fs.read(path)); + } - /** - * Create a file at the specified path. - * - * If a file with the same name already exists, it is moved before creating the new one. - * Parent directories are created if necessary. - */ - public async create( - path: RelativePath, - newContent: Uint8Array - ): Promise { - await this.ensureClearPath(path); - return this.fs.write(path, this.toNativeLineEndings(newContent)); - } + /** + * Create a file at the specified path. + * + * If a file with the same name already exists, it is moved before creating the new one. + * Parent directories are created if necessary. + */ + public async create( + path: RelativePath, + newContent: Uint8Array + ): Promise { + await this.ensureClearPath(path); + return this.fs.write(path, this.toNativeLineEndings(newContent)); + } - public async ensureClearPath(path: RelativePath): Promise { - if (await this.fs.exists(path)) { - const deconflictedPath = await this.deconflictPath(path); - try { - this.logger.debug( - `Didn't expect ${path} to exist, deconflicting by moving it to '${deconflictedPath}'` - ); + public async ensureClearPath(path: RelativePath): Promise { + if (await this.fs.exists(path)) { + const deconflictedPath = await this.deconflictPath(path); + try { + this.logger.debug( + `Didn't expect ${path} to exist, deconflicting by moving it to '${deconflictedPath}'` + ); - this.database.move(path, deconflictedPath); - await this.fs.rename(path, deconflictedPath, true); - } finally { - this.fs.unlock(deconflictedPath); - } - } else { - await this.createParentDirectories(path); - } - } + this.database.move(path, deconflictedPath); + await this.fs.rename(path, deconflictedPath, true); + } finally { + this.fs.unlock(deconflictedPath); + } + } else { + await this.createParentDirectories(path); + } + } - /** - * Update the file at the given path. - * - * Performs a 3-way merge before writing if the file's content differs from `expectedContent`. - * Does not recreate the file if it no longer exists, returning an empty array instead. - */ - public async write( - path: RelativePath, - expectedContent: Uint8Array, - newContent: Uint8Array - ): Promise { - if (!(await this.fs.exists(path))) { - this.logger.debug( - `The caller assumed ${path} exists, but it no longer, so we wont recreate it` - ); - return; - } + /** + * Update the file at the given path. + * + * Performs a 3-way merge before writing if the file's content differs from `expectedContent`. + * Does not recreate the file if it no longer exists, returning an empty array instead. + */ + public async write( + path: RelativePath, + expectedContent: Uint8Array, + newContent: Uint8Array + ): Promise { + if (!(await this.fs.exists(path))) { + this.logger.debug( + `The caller assumed ${path} exists, but it no longer, so we wont recreate it` + ); + return; + } - if ( - !isFileTypeMergable( - path, - this.serverConfig.getConfig().mergeableFileExtensions - ) || - isBinary(expectedContent) || - isBinary(newContent) - ) { - this.logger.debug( - `The expected content is not mergable, so we won't perform a 3-way merge, just overwrite it` - ); - await this.fs.write( - path, - // `newContent` might not be binary so we still have to ensure the line endings are correct - this.toNativeLineEndings(newContent) - ); - return; - } + if ( + !isFileTypeMergable( + path, + this.serverConfig.getConfig().mergeableFileExtensions + ) || + isBinary(expectedContent) || + isBinary(newContent) + ) { + this.logger.debug( + `The expected content is not mergable, so we won't perform a 3-way merge, just overwrite it` + ); + await this.fs.write( + path, + // `newContent` might not be binary so we still have to ensure the line endings are correct + this.toNativeLineEndings(newContent) + ); + return; + } - const expectedText = new TextDecoder().decode(expectedContent); // this comes from a previous read which must only have \n line endings - const newText = new TextDecoder().decode(newContent); // this comes from the server which stores text with \n line endings + const expectedText = new TextDecoder().decode(expectedContent); // this comes from a previous read which must only have \n line endings + const newText = new TextDecoder().decode(newContent); // this comes from the server which stores text with \n line endings - await this.fs.atomicUpdateText( - path, - ({ text, cursors }: TextWithCursors): TextWithCursors => { - this.logger.debug( - `Performing a 3-way merge for ${path} with the expected content` - ); + await this.fs.atomicUpdateText( + path, + ({ text, cursors }: TextWithCursors): TextWithCursors => { + this.logger.debug( + `Performing a 3-way merge for ${path} with the expected content` + ); - text = text.replaceAll(this.nativeLineEndings, "\n"); - const merged = reconcile( - expectedText, - { text, cursors }, - newText - ); + text = text.replaceAll(this.nativeLineEndings, "\n"); + const merged = reconcile( + expectedText, + { text, cursors }, + newText + ); - const resultText = merged.text.replaceAll( - "\n", - this.nativeLineEndings - ); + const resultText = merged.text.replaceAll( + "\n", + this.nativeLineEndings + ); - return { - text: resultText, - cursors: merged.cursors - }; - } - ); - } + return { + text: resultText, + cursors: merged.cursors + }; + } + ); + } - public async delete(path: RelativePath): Promise { - if (await this.exists(path)) { - await this.fs.delete(path); - await this.deletingEmptyParentDirectoriesOfDeletedFile(path); - } else { - this.logger.debug(`No need to delete '${path}', it doesn't exist`); - } - } + public async delete(path: RelativePath): Promise { + if (await this.exists(path)) { + await this.fs.delete(path); + await this.deletingEmptyParentDirectoriesOfDeletedFile(path); + } else { + this.logger.debug(`No need to delete '${path}', it doesn't exist`); + } + } - public async getFileSize(path: RelativePath): Promise { - return this.fs.getFileSize(path); - } + public async getFileSize(path: RelativePath): Promise { + return this.fs.getFileSize(path); + } - public async exists(path: RelativePath): Promise { - return this.fs.exists(path); - } + public async exists(path: RelativePath): Promise { + return this.fs.exists(path); + } - public async move( - oldPath: RelativePath, - newPath: RelativePath - ): Promise { - if (oldPath === newPath) { - return; - } + public async move( + oldPath: RelativePath, + newPath: RelativePath + ): Promise { + if (oldPath === newPath) { + return; + } - await this.ensureClearPath(newPath); + await this.ensureClearPath(newPath); - this.database.move(oldPath, newPath); - await this.fs.rename(oldPath, newPath); - await this.deletingEmptyParentDirectoriesOfDeletedFile(oldPath); - } + this.database.move(oldPath, newPath); + await this.fs.rename(oldPath, newPath); + await this.deletingEmptyParentDirectoriesOfDeletedFile(oldPath); + } - public reset(): void { - this.fs.reset(); - } + public reset(): void { + this.fs.reset(); + } - private async deletingEmptyParentDirectoriesOfDeletedFile( - path: RelativePath - ): Promise { - let directory = path; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - while (true) { - [directory] = FileOperations.getParentDirAndFile(directory); - if (directory.length === 0) { - break; - } + private async deletingEmptyParentDirectoriesOfDeletedFile( + path: RelativePath + ): Promise { + let directory = path; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + while (true) { + [directory] = FileOperations.getParentDirAndFile(directory); + if (directory.length === 0) { + break; + } - const remainingContent = - await this.fs.listFilesRecursively(directory); - if (remainingContent.length === 0) { - this.logger.debug( - `Folder (${directory}) is now empty, deleting` - ); - await this.fs.delete(directory); - } else { - break; - } - } - } + const remainingContent = + await this.fs.listFilesRecursively(directory); + if (remainingContent.length === 0) { + this.logger.debug( + `Folder (${directory}) is now empty, deleting` + ); + await this.fs.delete(directory); + } else { + break; + } + } + } - private fromNativeLineEndings(content: Uint8Array): Uint8Array { - if (isBinary(content)) { - return content; - } + private fromNativeLineEndings(content: Uint8Array): Uint8Array { + if (isBinary(content)) { + return content; + } - const decoder = new TextDecoder("utf-8"); - let text = decoder.decode(content); - text = text.replaceAll(this.nativeLineEndings, "\n"); - return new TextEncoder().encode(text); - } + const decoder = new TextDecoder("utf-8"); + let text = decoder.decode(content); + text = text.replaceAll(this.nativeLineEndings, "\n"); + return new TextEncoder().encode(text); + } - private toNativeLineEndings(content: Uint8Array): Uint8Array { - if (isBinary(content)) { - return content; - } + private toNativeLineEndings(content: Uint8Array): Uint8Array { + if (isBinary(content)) { + return content; + } - const decoder = new TextDecoder("utf-8"); - let text = decoder.decode(content); - text = text.replaceAll("\n", this.nativeLineEndings); - return new TextEncoder().encode(text); - } + const decoder = new TextDecoder("utf-8"); + let text = decoder.decode(content); + text = text.replaceAll("\n", this.nativeLineEndings); + return new TextEncoder().encode(text); + } - private async createParentDirectories(path: string): Promise { - const components = path.split("/"); - if (components.length === 1) { - return; - } - for (let i = 1; i < components.length; i++) { - const parentDir = components.slice(0, i).join("/"); - if (!(await this.fs.exists(parentDir))) { - await this.fs.createDirectory(parentDir); - } - } - } + private async createParentDirectories(path: string): Promise { + const components = path.split("/"); + if (components.length === 1) { + return; + } + for (let i = 1; i < components.length; i++) { + const parentDir = components.slice(0, i).join("/"); + if (!(await this.fs.exists(parentDir))) { + await this.fs.createDirectory(parentDir); + } + } + } - /** - * Deconflicts the given path by appending (1), (2), etc. before the file extension until a non-existent path is found. - * The returned path has a lock acquired on it; it must be released by the caller when no longer needed. - * - * @param path The starting path to deconflict - * @returns a non-existent path with a lock acquired on it - */ - private async deconflictPath(path: RelativePath): Promise { - // eslint-disable-next-line prefer-const - let [directory, fileName] = FileOperations.getParentDirAndFile(path); + /** + * Deconflicts the given path by appending (1), (2), etc. before the file extension until a non-existent path is found. + * The returned path has a lock acquired on it; it must be released by the caller when no longer needed. + * + * @param path The starting path to deconflict + * @returns a non-existent path with a lock acquired on it + */ + private async deconflictPath(path: RelativePath): Promise { + // eslint-disable-next-line prefer-const + let [directory, fileName] = FileOperations.getParentDirAndFile(path); - if (directory) { - directory += "/"; - } + if (directory) { + directory += "/"; + } - const nameParts = fileName.split("."); - // Handle dotfiles: ".gitignore" should have no extension, ".config.json" should have ".json" - const isDotfile = fileName.startsWith(".") && nameParts[0] === ""; - const extension = - nameParts.length > 1 && !(isDotfile && nameParts.length === 2) - ? "." + nameParts[nameParts.length - 1] - : ""; - let stem = extension ? nameParts.slice(0, -1).join(".") : fileName; - let currentCount = Number.parseInt( - FileOperations.PARENTHESES_REGEX.exec(stem)?.groups?.count ?? "0" - ); - stem = stem.replace(FileOperations.PARENTHESES_REGEX, ""); + const nameParts = fileName.split("."); + // Handle dotfiles: ".gitignore" should have no extension, ".config.json" should have ".json" + const isDotfile = fileName.startsWith(".") && nameParts[0] === ""; + const extension = + nameParts.length > 1 && !(isDotfile && nameParts.length === 2) + ? "." + nameParts[nameParts.length - 1] + : ""; + let stem = extension ? nameParts.slice(0, -1).join(".") : fileName; + let currentCount = Number.parseInt( + FileOperations.PARENTHESES_REGEX.exec(stem)?.groups?.count ?? "0" + ); + stem = stem.replace(FileOperations.PARENTHESES_REGEX, ""); - let newName = path; + let newName = path; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - while (true) { - currentCount++; - newName = `${directory}${stem} (${currentCount})${extension}`; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + while (true) { + currentCount++; + newName = `${directory}${stem} (${currentCount})${extension}`; - // Avoid multiple deconflictPath calls returning the same path - if (this.fs.tryLock(newName)) { - const newDocument = - this.database.getLatestDocumentByRelativePath(newName); - if ( - newDocument?.isDeleted === false || // the document might have been confirmed by the server at a new path but haven't yet moved there locally - (await this.fs.exists(newName, true)) - ) { - this.fs.unlock(newName); - } else { - return newName; - } - } - } - } + // Avoid multiple deconflictPath calls returning the same path + if (this.fs.tryLock(newName)) { + const newDocument = + this.database.getLatestDocumentByRelativePath(newName); + if ( + newDocument?.isDeleted === false || // the document might have been confirmed by the server at a new path but haven't yet moved there locally + (await this.fs.exists(newName, true)) + ) { + this.fs.unlock(newName); + } else { + return newName; + } + } + } + } } diff --git a/frontend/sync-client/src/file-operations/filesystem-operations.ts b/frontend/sync-client/src/file-operations/filesystem-operations.ts index 9c7a8366..36dddfe6 100644 --- a/frontend/sync-client/src/file-operations/filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/filesystem-operations.ts @@ -3,35 +3,35 @@ import type { RelativePath } from "../persistence/database"; import type { TextWithCursors } from "reconcile-text"; export interface FileSystemOperations { - // List all files under root that should be synced. If root is undefined, return every file. - listFilesRecursively: ( - root: RelativePath | undefined - ) => Promise; + // List all files under root that should be synced. If root is undefined, return every file. + listFilesRecursively: ( + root: RelativePath | undefined + ) => Promise; - // Read the content of a file. - read: (path: RelativePath) => Promise; + // Read the content of a file. + read: (path: RelativePath) => Promise; - // Create or overwrite a file with the given content. - write: (path: RelativePath, content: Uint8Array) => Promise; + // Create or overwrite a file with the given content. + write: (path: RelativePath, content: Uint8Array) => Promise; - // Atomically update the content of a text file. - atomicUpdateText: ( - path: RelativePath, - updater: (current: TextWithCursors) => TextWithCursors - ) => Promise; + // Atomically update the content of a text file. + atomicUpdateText: ( + path: RelativePath, + updater: (current: TextWithCursors) => TextWithCursors + ) => Promise; - // Get the size of a file in bytes. - getFileSize: (path: RelativePath) => Promise; + // Get the size of a file in bytes. + getFileSize: (path: RelativePath) => Promise; - // Check if a file exists. - exists: (path: RelativePath) => Promise; + // Check if a file exists. + exists: (path: RelativePath) => Promise; - // Create a directory at the specified path. All parent directories must already exist. - createDirectory: (path: RelativePath) => Promise; + // Create a directory at the specified path. All parent directories must already exist. + createDirectory: (path: RelativePath) => Promise; - // Delete a file. It is expected that the path points to an existing file. - delete: (path: RelativePath) => Promise; + // Delete a file. It is expected that the path points to an existing file. + delete: (path: RelativePath) => Promise; - // Rename a file. It is expected that the oldPath points to an existing file and the newPath does not exist. - rename: (oldPath: RelativePath, newPath: RelativePath) => Promise; + // Rename a file. It is expected that the oldPath points to an existing file and the newPath does not exist. + rename: (oldPath: RelativePath, newPath: RelativePath) => Promise; } diff --git a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts index 33984be4..904bf805 100644 --- a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts @@ -11,160 +11,160 @@ import type { TextWithCursors } from "reconcile-text"; * single request in-flight for any one file through the use of locks. */ export class SafeFileSystemOperations implements FileSystemOperations { - private readonly locks: Locks; + private readonly locks: Locks; - public constructor( - private readonly fs: FileSystemOperations, - private readonly logger: Logger - ) { - this.locks = new Locks(logger); - } + public constructor( + private readonly fs: FileSystemOperations, + private readonly logger: Logger + ) { + this.locks = new Locks(logger); + } - public async listFilesRecursively( - root: RelativePath | undefined - ): Promise { - this.logger.debug("Listing all files"); - const result = await this.fs.listFilesRecursively(root); - this.logger.debug(`Listed ${result.length} files`); - return result; - } + public async listFilesRecursively( + root: RelativePath | undefined + ): Promise { + this.logger.debug("Listing all files"); + const result = await this.fs.listFilesRecursively(root); + this.logger.debug(`Listed ${result.length} files`); + return result; + } - public async read(path: RelativePath): Promise { - this.logger.debug(`Reading file '${path}'`); - return this.safeOperation( - path, - async () => - this.locks.withLock(path, async () => this.fs.read(path)), - "read" - ); - } + public async read(path: RelativePath): Promise { + this.logger.debug(`Reading file '${path}'`); + return this.safeOperation( + path, + async () => + this.locks.withLock(path, async () => this.fs.read(path)), + "read" + ); + } - public async write(path: RelativePath, content: Uint8Array): Promise { - this.logger.debug(`Writing to file '${path}'`); - return this.locks.withLock(path, async () => - this.fs.write(path, content) - ); - } + public async write(path: RelativePath, content: Uint8Array): Promise { + this.logger.debug(`Writing to file '${path}'`); + return this.locks.withLock(path, async () => + this.fs.write(path, content) + ); + } - public async atomicUpdateText( - path: RelativePath, - updater: (current: TextWithCursors) => TextWithCursors - ): Promise { - this.logger.debug(`Atomically updating file '${path}'`); - return this.safeOperation( - path, - async () => - this.locks.withLock(path, async () => - this.fs.atomicUpdateText(path, updater) - ), - "atomicUpdateText" - ); - } + public async atomicUpdateText( + path: RelativePath, + updater: (current: TextWithCursors) => TextWithCursors + ): Promise { + this.logger.debug(`Atomically updating file '${path}'`); + return this.safeOperation( + path, + async () => + this.locks.withLock(path, async () => + this.fs.atomicUpdateText(path, updater) + ), + "atomicUpdateText" + ); + } - public async getFileSize(path: RelativePath): Promise { - // Logging this would be too noisy - return this.safeOperation( - path, - async () => - this.locks.withLock(path, async () => - this.fs.getFileSize(path) - ), - "getFileSize" - ); - } + public async getFileSize(path: RelativePath): Promise { + // Logging this would be too noisy + return this.safeOperation( + path, + async () => + this.locks.withLock(path, async () => + this.fs.getFileSize(path) + ), + "getFileSize" + ); + } - public async exists( - path: RelativePath, - skipLock = false - ): Promise { - this.logger.debug(`Checking if file '${path}' exists`); - if (skipLock) { - return this.fs.exists(path); - } else { - return this.locks.withLock(path, async () => this.fs.exists(path)); - } - } + public async exists( + path: RelativePath, + skipLock = false + ): Promise { + this.logger.debug(`Checking if file '${path}' exists`); + if (skipLock) { + return this.fs.exists(path); + } else { + return this.locks.withLock(path, async () => this.fs.exists(path)); + } + } - public async createDirectory(path: RelativePath): Promise { - this.logger.debug(`Creating directory '${path}'`); - return this.locks.withLock(path, async () => - this.fs.createDirectory(path) - ); - } + public async createDirectory(path: RelativePath): Promise { + this.logger.debug(`Creating directory '${path}'`); + return this.locks.withLock(path, async () => + this.fs.createDirectory(path) + ); + } - public async delete(path: RelativePath): Promise { - this.logger.debug(`Deleting file '${path}'`); - return this.locks.withLock(path, async () => this.fs.delete(path)); - } + public async delete(path: RelativePath): Promise { + this.logger.debug(`Deleting file '${path}'`); + return this.locks.withLock(path, async () => this.fs.delete(path)); + } - public async rename( - oldPath: RelativePath, - newPath: RelativePath, - skipLock = false - ): Promise { - this.logger.debug(`Renaming file '${oldPath}' to '${newPath}'`); - return this.safeOperation( - oldPath, - async () => { - if (skipLock) { - return this.fs.rename(oldPath, newPath); - } else { - return this.locks.withLock([oldPath, newPath], async () => - this.fs.rename(oldPath, newPath) - ); - } - }, - "rename" - ); - } + public async rename( + oldPath: RelativePath, + newPath: RelativePath, + skipLock = false + ): Promise { + this.logger.debug(`Renaming file '${oldPath}' to '${newPath}'`); + return this.safeOperation( + oldPath, + async () => { + if (skipLock) { + return this.fs.rename(oldPath, newPath); + } else { + return this.locks.withLock([oldPath, newPath], async () => + this.fs.rename(oldPath, newPath) + ); + } + }, + "rename" + ); + } - public tryLock(path: RelativePath): boolean { - return this.locks.tryLock(path); - } + public tryLock(path: RelativePath): boolean { + return this.locks.tryLock(path); + } - public async waitForLock(path: RelativePath): Promise { - return this.locks.waitForLock(path); - } + public async waitForLock(path: RelativePath): Promise { + return this.locks.waitForLock(path); + } - public unlock(path: RelativePath): void { - this.locks.unlock(path); - } + public unlock(path: RelativePath): void { + this.locks.unlock(path); + } - public reset(): void { - this.locks.reset(); - } + public reset(): void { + this.locks.reset(); + } - /** - * Decorate an operation to ensure that the file exists before running it. - * If the operation fails, it will check if the file still exists and throw - * a FileNotFoundError if it doesn't. - */ - private async safeOperation( - path: RelativePath, - operation: () => Promise, - operationName: string - ): Promise { - if (!(await this.fs.exists(path))) { - throw new FileNotFoundError( - `File not found before trying to ${operationName}`, - path - ); - } + /** + * Decorate an operation to ensure that the file exists before running it. + * If the operation fails, it will check if the file still exists and throw + * a FileNotFoundError if it doesn't. + */ + private async safeOperation( + path: RelativePath, + operation: () => Promise, + operationName: string + ): Promise { + if (!(await this.fs.exists(path))) { + throw new FileNotFoundError( + `File not found before trying to ${operationName}`, + path + ); + } - try { - return await operation(); - } catch (error) { - // Without locking the file, this isn't atomic, however, it's good enough in practice. - // This will only break if the file exists, gets deleted and then immediately - // recreated while `operation` is running. - if (await this.fs.exists(path)) { - throw error; - } else { - throw new FileNotFoundError( - `File not found when trying to ${operationName}`, - path - ); - } - } - } + try { + return await operation(); + } catch (error) { + // Without locking the file, this isn't atomic, however, it's good enough in practice. + // This will only break if the file exists, gets deleted and then immediately + // recreated while `operation` is running. + if (await this.fs.exists(path)) { + throw error; + } else { + throw new FileNotFoundError( + `File not found when trying to ${operationName}`, + path + ); + } + } + } } diff --git a/frontend/sync-client/src/index.ts b/frontend/sync-client/src/index.ts index 405acb10..cfcc5071 100644 --- a/frontend/sync-client/src/index.ts +++ b/frontend/sync-client/src/index.ts @@ -8,15 +8,15 @@ import { positionToLineAndColumn } from "./utils/position-to-line-and-column"; import { removeFromArray } from "./utils/remove-from-array"; export { - SyncType, - SyncStatus, - type HistoryStats, - type HistoryEntry, - type SyncDetails, - type SyncCreateDetails, - type SyncUpdateDetails, - type SyncMovedDetails, - type SyncDeleteDetails + SyncType, + SyncStatus, + type HistoryStats, + type HistoryEntry, + type SyncDetails, + type SyncCreateDetails, + type SyncUpdateDetails, + type SyncMovedDetails, + type SyncDeleteDetails } from "./tracing/sync-history"; export { Logger, LogLevel, LogLine } from "./tracing/logger"; export { type SyncSettings, DEFAULT_SETTINGS } from "./persistence/settings"; @@ -35,15 +35,15 @@ export { SyncClient } from "./sync-client"; export type { TextWithCursors, CursorPosition } from "reconcile-text"; export const debugging = { - slowFetchFactory, - slowWebSocketFactory, - logToConsole + slowFetchFactory, + slowWebSocketFactory, + logToConsole }; export const utils = { - getRandomColor, - positionToLineAndColumn, - lineAndColumnToPosition, - awaitAll, - removeFromArray + getRandomColor, + positionToLineAndColumn, + lineAndColumnToPosition, + awaitAll, + removeFromArray }; diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 5568169b..8e1cd61f 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -9,23 +9,23 @@ export type DocumentId = string; export type RelativePath = string; export interface DocumentMetadata { - parentVersionId: VaultUpdateId; - hash: string; - remoteRelativePath?: RelativePath; + parentVersionId: VaultUpdateId; + hash: string; + remoteRelativePath?: RelativePath; } export interface StoredDocumentMetadata { - relativePath: RelativePath; - documentId: DocumentId; - parentVersionId: VaultUpdateId; - remoteRelativePath?: RelativePath; - hash: string; + relativePath: RelativePath; + documentId: DocumentId; + parentVersionId: VaultUpdateId; + remoteRelativePath?: RelativePath; + hash: string; } export interface StoredDatabase { - documents: StoredDocumentMetadata[]; - lastSeenUpdateId: VaultUpdateId | undefined; - hasInitialSyncCompleted: boolean; + documents: StoredDocumentMetadata[]; + lastSeenUpdateId: VaultUpdateId | undefined; + hasInitialSyncCompleted: boolean; } /** @@ -35,340 +35,340 @@ export interface StoredDatabase { * state of the document on disk based on the update events we have seen. */ export interface DocumentRecord { - relativePath: RelativePath; - documentId: DocumentId; - metadata: DocumentMetadata | undefined; - isDeleted: boolean; - updates: Promise[]; - parallelVersion: number; + relativePath: RelativePath; + documentId: DocumentId; + metadata: DocumentMetadata | undefined; + isDeleted: boolean; + updates: Promise[]; + parallelVersion: number; } export class Database { - private documents: DocumentRecord[]; - private lastSeenUpdateIds: CoveredValues; - private hasInitialSyncCompleted: boolean; + private documents: DocumentRecord[]; + private lastSeenUpdateIds: CoveredValues; + private hasInitialSyncCompleted: boolean; - public constructor( - private readonly logger: Logger, - initialState: Partial | undefined, - private readonly saveData: (data: StoredDatabase) => Promise - ) { - initialState ??= {}; + public constructor( + private readonly logger: Logger, + initialState: Partial | undefined, + private readonly saveData: (data: StoredDatabase) => Promise + ) { + initialState ??= {}; - this.documents = - initialState.documents?.map( - ({ relativePath, documentId, ...metadata }) => ({ - relativePath, - documentId, - metadata, - isDeleted: false, - updates: [], - parallelVersion: 0 - }) - ) ?? []; + this.documents = + initialState.documents?.map( + ({ relativePath, documentId, ...metadata }) => ({ + relativePath, + documentId, + metadata, + isDeleted: false, + updates: [], + parallelVersion: 0 + }) + ) ?? []; - this.ensureConsistency(); - this.logger.debug(`Loaded ${this.documents.length} documents`); + this.ensureConsistency(); + this.logger.debug(`Loaded ${this.documents.length} documents`); - const { lastSeenUpdateId } = initialState; - this.logger.debug(`Loaded last seen update id: ${lastSeenUpdateId}`); - this.lastSeenUpdateIds = new CoveredValues( - Math.max(0, lastSeenUpdateId ?? 0) // the first updateId will be 1 which is the first integer after -1 - ); + const { lastSeenUpdateId } = initialState; + this.logger.debug(`Loaded last seen update id: ${lastSeenUpdateId}`); + this.lastSeenUpdateIds = new CoveredValues( + Math.max(0, lastSeenUpdateId ?? 0) // the first updateId will be 1 which is the first integer after -1 + ); - this.documents.forEach((doc) => { - this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId); - }); + this.documents.forEach((doc) => { + this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId); + }); - this.hasInitialSyncCompleted = - initialState.hasInitialSyncCompleted ?? false; - this.logger.debug( - `Loaded hasInitialSyncCompleted: ${this.hasInitialSyncCompleted}` - ); - } + this.hasInitialSyncCompleted = + initialState.hasInitialSyncCompleted ?? false; + this.logger.debug( + `Loaded hasInitialSyncCompleted: ${this.hasInitialSyncCompleted}` + ); + } - public get length(): number { - return this.documents.length; - } + public get length(): number { + return this.documents.length; + } - public get resolvedDocuments(): DocumentRecord[] { - const paths = new Map(); - this.documents - // eslint-disable-next-line no-restricted-syntax -- Type narrowing, not removing a specific item - .filter(({ metadata }) => metadata !== undefined) - .forEach((record) => - paths.set(record.relativePath, [ - record, - ...(paths.get(record.relativePath) ?? []) - ]) - ); + public get resolvedDocuments(): DocumentRecord[] { + const paths = new Map(); + this.documents + // eslint-disable-next-line no-restricted-syntax -- Type narrowing, not removing a specific item + .filter(({ metadata }) => metadata !== undefined) + .forEach((record) => + paths.set(record.relativePath, [ + record, + ...(paths.get(record.relativePath) ?? []) + ]) + ); - return Array.from(paths.values()).map((records) => { - records.sort( - (a, b) => b.parallelVersion - a.parallelVersion // descending - ); + return Array.from(paths.values()).map((records) => { + records.sort( + (a, b) => b.parallelVersion - a.parallelVersion // descending + ); - if ( - records.length > 1 && - records.some((current, i) => - i === 0 - ? false - : records[i - 1].parallelVersion === - current.parallelVersion - ) - ) { - throw new Error( - `Multiple documents with the same parallel version and path at ${records[0].relativePath}` - ); - } - return records[0]; - }); - } + if ( + records.length > 1 && + records.some((current, i) => + i === 0 + ? false + : records[i - 1].parallelVersion === + current.parallelVersion + ) + ) { + throw new Error( + `Multiple documents with the same parallel version and path at ${records[0].relativePath}` + ); + } + return records[0]; + }); + } - public updateDocumentMetadata( - metadata: { - parentVersionId: VaultUpdateId; - hash: string; - remoteRelativePath: RelativePath; - }, - toUpdate: DocumentRecord - ): void { - if (!this.documents.includes(toUpdate)) { - throw new Error("Document not found in database"); - } + public updateDocumentMetadata( + metadata: { + parentVersionId: VaultUpdateId; + hash: string; + remoteRelativePath: RelativePath; + }, + toUpdate: DocumentRecord + ): void { + if (!this.documents.includes(toUpdate)) { + throw new Error("Document not found in database"); + } - toUpdate.metadata = metadata; + toUpdate.metadata = metadata; - this.saveInTheBackground(); - } + this.saveInTheBackground(); + } - public removeDocumentPromise(promise: Promise): void { - const entry = this.documents.find(({ updates }) => - updates.includes(promise) - ); + public removeDocumentPromise(promise: Promise): void { + const entry = this.documents.find(({ updates }) => + updates.includes(promise) + ); - if (entry === undefined) { - // This method should be idempotent and tolerant of - // stragglers calling it after the databse has been reset. - return; - } + if (entry === undefined) { + // This method should be idempotent and tolerant of + // stragglers calling it after the databse has been reset. + return; + } - removeFromArray(entry.updates, promise); - // No need to save as Promises don't get serialized - } + removeFromArray(entry.updates, promise); + // No need to save as Promises don't get serialized + } - public removeDocument(find: DocumentRecord): void { - removeFromArray(this.documents, find); - this.saveInTheBackground(); - } + public removeDocument(find: DocumentRecord): void { + removeFromArray(this.documents, find); + this.saveInTheBackground(); + } - public getLatestDocumentByRelativePath( - find: RelativePath - ): DocumentRecord | undefined { - const candidates = this.documents.filter( - ({ relativePath }) => relativePath === find - ); - candidates.sort((a, b) => b.parallelVersion - a.parallelVersion); // descending - return candidates[0]; - } + public getLatestDocumentByRelativePath( + find: RelativePath + ): DocumentRecord | undefined { + const candidates = this.documents.filter( + ({ relativePath }) => relativePath === find + ); + candidates.sort((a, b) => b.parallelVersion - a.parallelVersion); // descending + return candidates[0]; + } - public async getResolvedDocumentByRelativePath( - relativePath: RelativePath, - promise: Promise - ): Promise { - const entry = this.getLatestDocumentByRelativePath(relativePath); + public async getResolvedDocumentByRelativePath( + relativePath: RelativePath, + promise: Promise + ): Promise { + const entry = this.getLatestDocumentByRelativePath(relativePath); - if (entry === undefined) { - throw new Error( - `Document not found by relative path: ${relativePath}, ${JSON.stringify( - this.documents, - null, - 2 - )}` - ); - } + if (entry === undefined) { + throw new Error( + `Document not found by relative path: ${relativePath}, ${JSON.stringify( + this.documents, + null, + 2 + )}` + ); + } - const currentPromises = entry.updates; - entry.updates = [...currentPromises, promise]; - await awaitAll(currentPromises); + const currentPromises = entry.updates; + entry.updates = [...currentPromises, promise]; + await awaitAll(currentPromises); - return entry; - } + return entry; + } - public createNewPendingDocument( - documentId: DocumentId, - relativePath: RelativePath, - promise: Promise - ): DocumentRecord { - this.logger.debug( - `Creating new pending document: ${relativePath} (${documentId})` - ); - const previousEntry = - this.getLatestDocumentByRelativePath(relativePath); + public createNewPendingDocument( + documentId: DocumentId, + relativePath: RelativePath, + promise: Promise + ): DocumentRecord { + this.logger.debug( + `Creating new pending document: ${relativePath} (${documentId})` + ); + const previousEntry = + this.getLatestDocumentByRelativePath(relativePath); - const entry = { - relativePath, - documentId, - metadata: undefined, - isDeleted: false, - updates: [promise], - parallelVersion: - previousEntry?.parallelVersion === undefined - ? 0 - : previousEntry.parallelVersion + 1 - }; + const entry = { + relativePath, + documentId, + metadata: undefined, + isDeleted: false, + updates: [promise], + parallelVersion: + previousEntry?.parallelVersion === undefined + ? 0 + : previousEntry.parallelVersion + 1 + }; - this.documents.push(entry); - this.saveInTheBackground(); + this.documents.push(entry); + this.saveInTheBackground(); - return entry; - } + return entry; + } - public createNewEmptyDocument( - documentId: DocumentId, - parentVersionId: VaultUpdateId, - relativePath: RelativePath - ): DocumentRecord { - const entry = { - relativePath, - documentId, - metadata: { - parentVersionId, - hash: EMPTY_HASH, - remoteRelativePath: relativePath - }, - isDeleted: false, - updates: [], - parallelVersion: 0 - }; + public createNewEmptyDocument( + documentId: DocumentId, + parentVersionId: VaultUpdateId, + relativePath: RelativePath + ): DocumentRecord { + const entry = { + relativePath, + documentId, + metadata: { + parentVersionId, + hash: EMPTY_HASH, + remoteRelativePath: relativePath + }, + isDeleted: false, + updates: [], + parallelVersion: 0 + }; - this.documents.push(entry); - this.saveInTheBackground(); + this.documents.push(entry); + this.saveInTheBackground(); - return entry; - } + return entry; + } - public getDocumentByDocumentId( - find: DocumentId - ): DocumentRecord | undefined { - return this.documents.find(({ documentId }) => documentId === find); - } + public getDocumentByDocumentId( + find: DocumentId + ): DocumentRecord | undefined { + return this.documents.find(({ documentId }) => documentId === find); + } - public move( - oldRelativePath: RelativePath, - newRelativePath: RelativePath - ): void { - const oldDocument = - this.getLatestDocumentByRelativePath(oldRelativePath); + public move( + oldRelativePath: RelativePath, + newRelativePath: RelativePath + ): void { + const oldDocument = + this.getLatestDocumentByRelativePath(oldRelativePath); - if (oldDocument === undefined) { - return; - } + if (oldDocument === undefined) { + return; + } - const newDocument = - this.getLatestDocumentByRelativePath(newRelativePath); - if (newDocument?.isDeleted === false) { - throw new Error( - `Document already exists at new location: ${newRelativePath}` - ); - } + const newDocument = + this.getLatestDocumentByRelativePath(newRelativePath); + if (newDocument?.isDeleted === false) { + throw new Error( + `Document already exists at new location: ${newRelativePath}` + ); + } - oldDocument.relativePath = newRelativePath; - // We're in a strange state where the target of the move has just got deleted, - // however, its metadata might already have a bunch of updates queued up for - // the document at the new location. We need to keep these updates. - oldDocument.parallelVersion = - newDocument !== undefined ? newDocument.parallelVersion + 1 : 0; + oldDocument.relativePath = newRelativePath; + // We're in a strange state where the target of the move has just got deleted, + // however, its metadata might already have a bunch of updates queued up for + // the document at the new location. We need to keep these updates. + oldDocument.parallelVersion = + newDocument !== undefined ? newDocument.parallelVersion + 1 : 0; - this.saveInTheBackground(); - } + this.saveInTheBackground(); + } - public delete(relativePath: RelativePath): void { - const candidate = this.getLatestDocumentByRelativePath(relativePath); - if (candidate === undefined) { - throw new Error( - `Document not found by relative path: ${relativePath}` - ); - } - candidate.isDeleted = true; - } + public delete(relativePath: RelativePath): void { + const candidate = this.getLatestDocumentByRelativePath(relativePath); + if (candidate === undefined) { + throw new Error( + `Document not found by relative path: ${relativePath}` + ); + } + candidate.isDeleted = true; + } - public getHasInitialSyncCompleted(): boolean { - return this.hasInitialSyncCompleted; - } + public getHasInitialSyncCompleted(): boolean { + return this.hasInitialSyncCompleted; + } - public setHasInitialSyncCompleted(value: boolean): void { - this.hasInitialSyncCompleted = value; - this.saveInTheBackground(); - } + public setHasInitialSyncCompleted(value: boolean): void { + this.hasInitialSyncCompleted = value; + this.saveInTheBackground(); + } - public getLastSeenUpdateId(): VaultUpdateId { - return this.lastSeenUpdateIds.min; - } + public getLastSeenUpdateId(): VaultUpdateId { + return this.lastSeenUpdateIds.min; + } - public addSeenUpdateId(value: number): void { - const previousMin = this.lastSeenUpdateIds.min; - this.lastSeenUpdateIds.add(value); - if (previousMin !== this.lastSeenUpdateIds.min) { - this.saveInTheBackground(); - } - } + public addSeenUpdateId(value: number): void { + const previousMin = this.lastSeenUpdateIds.min; + this.lastSeenUpdateIds.add(value); + if (previousMin !== this.lastSeenUpdateIds.min) { + this.saveInTheBackground(); + } + } - public setLastSeenUpdateId(value: number): void { - this.lastSeenUpdateIds.min = value; - this.saveInTheBackground(); - } + public setLastSeenUpdateId(value: number): void { + this.lastSeenUpdateIds.min = value; + this.saveInTheBackground(); + } - public reset(): void { - this.documents = []; - this.lastSeenUpdateIds = new CoveredValues( - 0 // the first updateId will be 1 which is the first integer after -1 - ); - this.hasInitialSyncCompleted = false; - this.saveInTheBackground(); - } + public reset(): void { + this.documents = []; + this.lastSeenUpdateIds = new CoveredValues( + 0 // the first updateId will be 1 which is the first integer after -1 + ); + this.hasInitialSyncCompleted = false; + this.saveInTheBackground(); + } - public async save(): Promise { - return this.saveData({ - documents: this.resolvedDocuments.map( - ({ relativePath, documentId, metadata }) => ({ - documentId, - relativePath, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ...metadata! // `resolvedDocuments` only returns docs with metadata set - }) - ), - lastSeenUpdateId: this.lastSeenUpdateIds.min, - hasInitialSyncCompleted: this.hasInitialSyncCompleted - }); - } + public async save(): Promise { + return this.saveData({ + documents: this.resolvedDocuments.map( + ({ relativePath, documentId, metadata }) => ({ + documentId, + relativePath, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ...metadata! // `resolvedDocuments` only returns docs with metadata set + }) + ), + lastSeenUpdateId: this.lastSeenUpdateIds.min, + hasInitialSyncCompleted: this.hasInitialSyncCompleted + }); + } - private ensureConsistency(): void { - const idToPath = new Map(); + private ensureConsistency(): void { + const idToPath = new Map(); - this.resolvedDocuments.forEach(({ relativePath, documentId }) => { - idToPath.set(documentId, [ - ...(idToPath.get(documentId) ?? []), - relativePath - ]); - }); + this.resolvedDocuments.forEach(({ relativePath, documentId }) => { + idToPath.set(documentId, [ + ...(idToPath.get(documentId) ?? []), + relativePath + ]); + }); - const duplicates = Array.from(idToPath.entries()) - .filter(([_, paths]) => paths.length > 1) - .map(([id, paths]) => `${id} (${paths.join(", ")})`); + const duplicates = Array.from(idToPath.entries()) + .filter(([_, paths]) => paths.length > 1) + .map(([id, paths]) => `${id} (${paths.join(", ")})`); - if (duplicates.length > 0) { - throw new Error( - "Document IDs are not unique, found duplicates: " + - duplicates.join("; ") - ); - } - } + if (duplicates.length > 0) { + throw new Error( + "Document IDs are not unique, found duplicates: " + + duplicates.join("; ") + ); + } + } - private saveInTheBackground(): void { - this.ensureConsistency(); - void this.save().catch((error: unknown) => { - this.logger.error(`Error saving data: ${error}`); - }); - } + private saveInTheBackground(): void { + this.ensureConsistency(); + void this.save().catch((error: unknown) => { + this.logger.error(`Error saving data: ${error}`); + }); + } } diff --git a/frontend/sync-client/src/persistence/persistence.ts b/frontend/sync-client/src/persistence/persistence.ts index 706ae6ff..d226e611 100644 --- a/frontend/sync-client/src/persistence/persistence.ts +++ b/frontend/sync-client/src/persistence/persistence.ts @@ -1,4 +1,4 @@ export interface PersistenceProvider { - load: () => Promise; - save: (data: T) => Promise; + load: () => Promise; + save: (data: T) => Promise; } diff --git a/frontend/sync-client/src/services/authentication-error.ts b/frontend/sync-client/src/services/authentication-error.ts index 9daa1937..6be4af24 100644 --- a/frontend/sync-client/src/services/authentication-error.ts +++ b/frontend/sync-client/src/services/authentication-error.ts @@ -1,6 +1,6 @@ export class AuthenticationError extends Error { - public constructor(message: string) { - super(message); - this.name = "AuthenticationError"; - } + public constructor(message: string) { + super(message); + this.name = "AuthenticationError"; + } } diff --git a/frontend/sync-client/src/services/fetch-controller.test.ts b/frontend/sync-client/src/services/fetch-controller.test.ts index 4ff57c55..94fa8424 100644 --- a/frontend/sync-client/src/services/fetch-controller.test.ts +++ b/frontend/sync-client/src/services/fetch-controller.test.ts @@ -7,171 +7,171 @@ import { SyncResetError } from "./sync-reset-error"; import { sleep } from "../utils/sleep"; describe("FetchController", () => { - const createMockFetch = ( - shouldSleep: boolean - ): Mock<() => Promise> => - mock.fn(async () => { - if (shouldSleep) { - await sleep(30); - } - return Promise.resolve(new Response("OK", { status: 200 })); - }); + const createMockFetch = ( + shouldSleep: boolean + ): Mock<() => Promise> => + mock.fn(async () => { + if (shouldSleep) { + await sleep(30); + } + return Promise.resolve(new Response("OK", { status: 200 })); + }); - beforeEach(() => { - mock.timers.enable({ apis: ["setTimeout"] }); - }); + beforeEach(() => { + mock.timers.enable({ apis: ["setTimeout"] }); + }); - afterEach(() => { - mock.timers.reset(); - }); + afterEach(() => { + mock.timers.reset(); + }); - it("should allow fetch when canFetch is true", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); - const mockFetch = createMockFetch(false); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should allow fetch when canFetch is true", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); + const mockFetch = createMockFetch(false); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - const response = await controlledFetch("http://example.com"); + const response = await controlledFetch("http://example.com"); - assert.strictEqual(await response.text(), "OK"); - assert.strictEqual(mockFetch.mock.calls.length, 1); - }); + assert.strictEqual(await response.text(), "OK"); + assert.strictEqual(mockFetch.mock.calls.length, 1); + }); - it("should block fetch until canFetch becomes true", async () => { - const logger = new Logger(); - const controller = new FetchController(false, logger); - const mockFetch = createMockFetch(true); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should block fetch until canFetch becomes true", async () => { + const logger = new Logger(); + const controller = new FetchController(false, logger); + const mockFetch = createMockFetch(true); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - const fetchPromise = controlledFetch("http://example.com"); - assert.strictEqual(mockFetch.mock.calls.length, 0); + const fetchPromise = controlledFetch("http://example.com"); + assert.strictEqual(mockFetch.mock.calls.length, 0); - controller.canFetch = true; - await Promise.resolve(); - mock.timers.tick(30); + controller.canFetch = true; + await Promise.resolve(); + mock.timers.tick(30); - const response = await fetchPromise; - assert.strictEqual(await response.text(), "OK"); - assert.strictEqual(mockFetch.mock.calls.length, 1); - }); + const response = await fetchPromise; + assert.strictEqual(await response.text(), "OK"); + assert.strictEqual(mockFetch.mock.calls.length, 1); + }); - it("should reject during reset", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); - const mockFetch = createMockFetch(true); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should reject during reset", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); + const mockFetch = createMockFetch(true); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - const firstRequest = controlledFetch("http://example.com"); - assert.strictEqual(mockFetch.mock.calls.length, 1); + const firstRequest = controlledFetch("http://example.com"); + assert.strictEqual(mockFetch.mock.calls.length, 1); - controller.startReset(); + controller.startReset(); - const secondRequest = controlledFetch("http://example.com"); + const secondRequest = controlledFetch("http://example.com"); - await assert.rejects( - firstRequest, - (error: unknown) => error instanceof SyncResetError - ); - await assert.rejects( - secondRequest, - (error: unknown) => error instanceof SyncResetError - ); - assert.strictEqual(mockFetch.mock.calls.length, 1); - }); + await assert.rejects( + firstRequest, + (error: unknown) => error instanceof SyncResetError + ); + await assert.rejects( + secondRequest, + (error: unknown) => error instanceof SyncResetError + ); + assert.strictEqual(mockFetch.mock.calls.length, 1); + }); - it("should allow fetch after reset finishes", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); - const mockFetch = createMockFetch(false); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should allow fetch after reset finishes", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); + const mockFetch = createMockFetch(false); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - controller.startReset(); - controller.finishReset(); + controller.startReset(); + controller.finishReset(); - const response = await controlledFetch("http://example.com"); - assert.strictEqual(await response.text(), "OK"); - }); + const response = await controlledFetch("http://example.com"); + assert.strictEqual(await response.text(), "OK"); + }); - it("should defer canFetch changes during reset", async () => { - const logger = new Logger(); - const controller = new FetchController(false, logger); - const mockFetch = createMockFetch(true); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should defer canFetch changes during reset", async () => { + const logger = new Logger(); + const controller = new FetchController(false, logger); + const mockFetch = createMockFetch(true); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - controller.startReset(); - controller.canFetch = true; + controller.startReset(); + controller.canFetch = true; - await assert.rejects( - async () => controlledFetch("http://example.com"), - (error: unknown) => error instanceof SyncResetError - ); + await assert.rejects( + async () => controlledFetch("http://example.com"), + (error: unknown) => error instanceof SyncResetError + ); - controller.finishReset(); + controller.finishReset(); - const fetchPromise = controlledFetch("http://example.com"); - mock.timers.tick(30); + const fetchPromise = controlledFetch("http://example.com"); + mock.timers.tick(30); - const response = await fetchPromise; - assert.strictEqual(await response.text(), "OK"); - }); + const response = await fetchPromise; + assert.strictEqual(await response.text(), "OK"); + }); - it("should handle different input types", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); - const mockFetch = createMockFetch(false); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should handle different input types", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); + const mockFetch = createMockFetch(false); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - await controlledFetch("http://example.com"); - await controlledFetch(new URL("http://example.com")); - await controlledFetch( - new Request("http://example.com", { method: "POST" }) - ); + await controlledFetch("http://example.com"); + await controlledFetch(new URL("http://example.com")); + await controlledFetch( + new Request("http://example.com", { method: "POST" }) + ); - assert.strictEqual(mockFetch.mock.calls.length, 3); - }); + assert.strictEqual(mockFetch.mock.calls.length, 3); + }); - it("should handle fetch errors", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); - const mockFetch = mock.fn(async () => { - throw new Error("Network error"); - }); - const controlledFetch = controller.getControlledFetchImplementation( - logger, - mockFetch - ); + it("should handle fetch errors", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); + const mockFetch = mock.fn(async () => { + throw new Error("Network error"); + }); + const controlledFetch = controller.getControlledFetchImplementation( + logger, + mockFetch + ); - await assert.rejects( - async () => controlledFetch("http://example.com"), - (error: unknown) => - error instanceof Error && error.message === "Network error" - ); - }); + await assert.rejects( + async () => controlledFetch("http://example.com"), + (error: unknown) => + error instanceof Error && error.message === "Network error" + ); + }); - it("should not create unhandled rejection on reset with no waiting fetches", async () => { - const logger = new Logger(); - const controller = new FetchController(true, logger); + it("should not create unhandled rejection on reset with no waiting fetches", async () => { + const logger = new Logger(); + const controller = new FetchController(true, logger); - controller.startReset(); - mock.timers.tick(10); - controller.finishReset(); - }); + controller.startReset(); + mock.timers.tick(10); + controller.finishReset(); + }); }); diff --git a/frontend/sync-client/src/services/fetch-controller.ts b/frontend/sync-client/src/services/fetch-controller.ts index 1e93c853..77b87e3a 100644 --- a/frontend/sync-client/src/services/fetch-controller.ts +++ b/frontend/sync-client/src/services/fetch-controller.ts @@ -7,143 +7,143 @@ import { SyncResetError } from "./sync-reset-error"; * and aborts outstanding requests when a reset is started. */ export class FetchController { - private static readonly UNTIL_RESOLUTION = Symbol(); + private static readonly UNTIL_RESOLUTION = Symbol(); - private isResetting = false; + private isResetting = false; - // Promise resolves on the next state change: sync enabled/disabled or reset started/ended - private until: Promise; - private resolveUntil: (result: symbol) => unknown; - private rejectUntil: (reason: unknown) => unknown; + // Promise resolves on the next state change: sync enabled/disabled or reset started/ended + private until: Promise; + private resolveUntil: (result: symbol) => unknown; + private rejectUntil: (reason: unknown) => unknown; - public constructor( - private _canFetch: boolean, - private readonly logger: Logger - ) { - [this.until, this.resolveUntil, this.rejectUntil] = - createPromise(); - } + public constructor( + private _canFetch: boolean, + private readonly logger: Logger + ) { + [this.until, this.resolveUntil, this.rejectUntil] = + createPromise(); + } - /** - * Whether the fetch implementation can immediately send requests once outside of a reset. - */ - public get canFetch(): boolean { - return this._canFetch; - } + /** + * Whether the fetch implementation can immediately send requests once outside of a reset. + */ + public get canFetch(): boolean { + return this._canFetch; + } - /** - * Allow or disallow fetching. The changes only take effect if not resetting. - * When called during a reset, its effect is deferred until the reset is finished. - * - * @param canFetch Whether fetching is enabled - */ - public set canFetch(canFetch: boolean) { - this._canFetch = canFetch; + /** + * Allow or disallow fetching. The changes only take effect if not resetting. + * When called during a reset, its effect is deferred until the reset is finished. + * + * @param canFetch Whether fetching is enabled + */ + public set canFetch(canFetch: boolean) { + this._canFetch = canFetch; - if (!this.isResetting) { - const previousResolve = this.resolveUntil; - [this.until, this.resolveUntil, this.rejectUntil] = - createPromise(); - previousResolve(FetchController.UNTIL_RESOLUTION); - } - } + if (!this.isResetting) { + const previousResolve = this.resolveUntil; + [this.until, this.resolveUntil, this.rejectUntil] = + createPromise(); + previousResolve(FetchController.UNTIL_RESOLUTION); + } + } - private static getUrlFromInput(input: RequestInfo | URL): string { - if (input instanceof URL) { - return input.href; - } - if (typeof input === "string") { - return input; - } - return input.url; - } + private static getUrlFromInput(input: RequestInfo | URL): string { + if (input instanceof URL) { + return input.href; + } + if (typeof input === "string") { + return input; + } + return input.url; + } - /** - * Starts a reset, causing all ongoing and future fetches to be rejected - * with a SyncResetError until finishReset is called. - */ - public startReset(): void { - this.isResetting = true; - this.rejectUntil(new SyncResetError()); - // Catch unhandled rejection if no fetches are waiting - this.until.catch(() => { - // Intentionally ignore - this rejection is handled by waiting fetches - }); - } + /** + * Starts a reset, causing all ongoing and future fetches to be rejected + * with a SyncResetError until finishReset is called. + */ + public startReset(): void { + this.isResetting = true; + this.rejectUntil(new SyncResetError()); + // Catch unhandled rejection if no fetches are waiting + this.until.catch(() => { + // Intentionally ignore - this rejection is handled by waiting fetches + }); + } - /** - * Finishes a reset, allowing fetches to proceed or wait again depending on - * the current sync settings. - */ - public finishReset(): void { - if (!this.isResetting) { - return; - } + /** + * Finishes a reset, allowing fetches to proceed or wait again depending on + * the current sync settings. + */ + public finishReset(): void { + if (!this.isResetting) { + return; + } - this.isResetting = false; - [this.until, this.resolveUntil, this.rejectUntil] = createPromise(); - } + this.isResetting = false; + [this.until, this.resolveUntil, this.rejectUntil] = createPromise(); + } - /** - * - * |------------------|---------------|-----------------------------------------------------| - * | | Sync enabled | Sync disabled | - * |------------------|-------------- |-----------------------------------------------------| - * | During reset | Rejects with SyncResetError without sending request | - * |------------------|-------------- |-----------------------------------------------------| - * | Outside of reset | Same as fetch | Blocks until sync is enabled and then same as fetch | - * |------------------|---------------|-----------------------------------------------------| - * - * @param logger for errors - * @param fetch to wrap - * @returns a wrapped fetch implementation affected by the FetchController state - */ - public getControlledFetchImplementation( - logger: Logger, - fetch: typeof globalThis.fetch = globalThis.fetch - ): typeof globalThis.fetch { - return async ( - input: RequestInfo | URL, - init?: RequestInit - ): Promise => { - while (!this.canFetch || this.isResetting) { - await this.until; - } + /** + * + * |------------------|---------------|-----------------------------------------------------| + * | | Sync enabled | Sync disabled | + * |------------------|-------------- |-----------------------------------------------------| + * | During reset | Rejects with SyncResetError without sending request | + * |------------------|-------------- |-----------------------------------------------------| + * | Outside of reset | Same as fetch | Blocks until sync is enabled and then same as fetch | + * |------------------|---------------|-----------------------------------------------------| + * + * @param logger for errors + * @param fetch to wrap + * @returns a wrapped fetch implementation affected by the FetchController state + */ + public getControlledFetchImplementation( + logger: Logger, + fetch: typeof globalThis.fetch = globalThis.fetch + ): typeof globalThis.fetch { + return async ( + input: RequestInfo | URL, + init?: RequestInit + ): Promise => { + while (!this.canFetch || this.isResetting) { + await this.until; + } - try { - // https://github.com/jonbern/fetch-retry/blob/8684ef4e688375f623bd76f13add76dbc1d67cfb/index.js#L67C1-L70C21 - const _input = - typeof Request !== "undefined" && input instanceof Request - ? input.clone() - : input; + try { + // https://github.com/jonbern/fetch-retry/blob/8684ef4e688375f623bd76f13add76dbc1d67cfb/index.js#L67C1-L70C21 + const _input = + typeof Request !== "undefined" && input instanceof Request + ? input.clone() + : input; - const fetchPromise = fetch(_input, init); + const fetchPromise = fetch(_input, init); - // We only want to catch rejections from `this.until` - let result: symbol | Response | undefined = undefined; - do { - result = await Promise.race([this.until, fetchPromise]); - } while (result === FetchController.UNTIL_RESOLUTION); + // We only want to catch rejections from `this.until` + let result: symbol | Response | undefined = undefined; + do { + result = await Promise.race([this.until, fetchPromise]); + } while (result === FetchController.UNTIL_RESOLUTION); - const fetchResult: Response = result as Response; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const fetchResult: Response = result as Response; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - if (!fetchResult.ok) { - this.logger.warn( - `Fetch for ${FetchController.getUrlFromInput( - input - )}, got status ${fetchResult.status}` - ); - } + if (!fetchResult.ok) { + this.logger.warn( + `Fetch for ${FetchController.getUrlFromInput( + input + )}, got status ${fetchResult.status}` + ); + } - return fetchResult; - } catch (error) { - logger.warn( - `Fetch for ${FetchController.getUrlFromInput( - input - )}, got error: ${error}` - ); - throw error; - } - }; - } + return fetchResult; + } catch (error) { + logger.warn( + `Fetch for ${FetchController.getUrlFromInput( + input + )}, got error: ${error}` + ); + throw error; + } + }; + } } diff --git a/frontend/sync-client/src/services/server-config.ts b/frontend/sync-client/src/services/server-config.ts index b3107d10..3d40f182 100644 --- a/frontend/sync-client/src/services/server-config.ts +++ b/frontend/sync-client/src/services/server-config.ts @@ -5,83 +5,83 @@ import type { SyncService } from "./sync-service"; import type { PingResponse } from "./types/PingResponse"; export interface ServerConfigData { - mergeableFileExtensions: string[]; - supportedApiVersion: number; - isAuthenticated: boolean; + mergeableFileExtensions: string[]; + supportedApiVersion: number; + isAuthenticated: boolean; } export class ServerConfig { - private response: Promise | undefined; - private config: ServerConfigData | undefined; + private response: Promise | undefined; + private config: ServerConfigData | undefined; - public constructor(private readonly syncService: SyncService) {} + public constructor(private readonly syncService: SyncService) {} - public async initialize(): Promise { - this.response = this.syncService.ping(); - this.config = await this.response; + public async initialize(): Promise { + this.response = this.syncService.ping(); + this.config = await this.response; - if (this.config.supportedApiVersion !== SUPPORTED_API_VERSION) { - const shouldUpgradeClient = - this.config.supportedApiVersion > SUPPORTED_API_VERSION; - throw new ServerVersionMismatchError( - `Unsupported API version: ${this.config.supportedApiVersion}. Consider upgrading the ${ - shouldUpgradeClient ? "client" : "sync-server" - } to ensure compatibility.` - ); - } + if (this.config.supportedApiVersion !== SUPPORTED_API_VERSION) { + const shouldUpgradeClient = + this.config.supportedApiVersion > SUPPORTED_API_VERSION; + throw new ServerVersionMismatchError( + `Unsupported API version: ${this.config.supportedApiVersion}. Consider upgrading the ${ + shouldUpgradeClient ? "client" : "sync-server" + } to ensure compatibility.` + ); + } - if (!this.config.isAuthenticated) { - throw new AuthenticationError( - "Failed to authenticate with the sync-server." - ); - } - } + if (!this.config.isAuthenticated) { + throw new AuthenticationError( + "Failed to authenticate with the sync-server." + ); + } + } - public async checkConnection(forceUpdate = false): Promise<{ - isSuccessful: boolean; - message: string; - }> { - try { - let { response } = this; - if (!response && !forceUpdate) { - throw new Error("ServerConfig not initialized"); - } else if (forceUpdate) { - response = this.response = this.syncService.ping(); - } + public async checkConnection(forceUpdate = false): Promise<{ + isSuccessful: boolean; + message: string; + }> { + try { + let { response } = this; + if (!response && !forceUpdate) { + throw new Error("ServerConfig not initialized"); + } else if (forceUpdate) { + response = this.response = this.syncService.ping(); + } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const result: PingResponse = (await response)!; // it must be defined, otherwise we would have thrown above - this.config = result; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const result: PingResponse = (await response)!; // it must be defined, otherwise we would have thrown above + this.config = result; - if (result.isAuthenticated) { - return { - isSuccessful: true, - message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated` - }; - } + if (result.isAuthenticated) { + return { + isSuccessful: true, + message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated` + }; + } - return { - isSuccessful: false, - message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate` - }; - } catch (e) { - return { - isSuccessful: false, - message: `Failed to connect to server: ${e}` - }; - } - } + return { + isSuccessful: false, + message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate` + }; + } catch (e) { + return { + isSuccessful: false, + message: `Failed to connect to server: ${e}` + }; + } + } - public getConfig(): ServerConfigData { - if (!this.config) { - throw new Error("ServerConfig not initialized"); - } + public getConfig(): ServerConfigData { + if (!this.config) { + throw new Error("ServerConfig not initialized"); + } - return this.config; - } + return this.config; + } - public reset(): void { - this.response = undefined; - this.config = undefined; - } + public reset(): void { + this.response = undefined; + this.config = undefined; + } } diff --git a/frontend/sync-client/src/services/server-version-mismatch-error.ts b/frontend/sync-client/src/services/server-version-mismatch-error.ts index 0f37fc6f..0b9960ea 100644 --- a/frontend/sync-client/src/services/server-version-mismatch-error.ts +++ b/frontend/sync-client/src/services/server-version-mismatch-error.ts @@ -1,6 +1,6 @@ export class ServerVersionMismatchError extends Error { - public constructor(message: string) { - super(message); - this.name = "ServerVersionMismatchError"; - } + public constructor(message: string) { + super(message); + this.name = "ServerVersionMismatchError"; + } } diff --git a/frontend/sync-client/src/services/sync-reset-error.ts b/frontend/sync-client/src/services/sync-reset-error.ts index 3fd8a86c..7b74e0b9 100644 --- a/frontend/sync-client/src/services/sync-reset-error.ts +++ b/frontend/sync-client/src/services/sync-reset-error.ts @@ -1,6 +1,6 @@ export class SyncResetError extends Error { - public constructor() { - super("SyncClient has been reset, cleaning up"); - this.name = "SyncResetError"; - } + public constructor() { + super("SyncClient has been reset, cleaning up"); + this.name = "SyncResetError"; + } } diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index 6850cb2b..8190a638 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -1,7 +1,7 @@ import type { - DocumentId, - RelativePath, - VaultUpdateId + DocumentId, + RelativePath, + VaultUpdateId } from "../persistence/database"; import type { Logger } from "../tracing/logger"; @@ -19,416 +19,416 @@ import type { DeleteDocumentVersion } from "./types/DeleteDocumentVersion"; import type { UpdateTextDocumentVersion } from "./types/UpdateTextDocumentVersion"; export class SyncService { - private readonly client: typeof globalThis.fetch; - private readonly pingClient: typeof globalThis.fetch; + private readonly client: typeof globalThis.fetch; + private readonly pingClient: typeof globalThis.fetch; - public constructor( - private readonly deviceId: string, - private readonly fetchController: FetchController, - private readonly settings: Settings, - private readonly logger: Logger, - fetchImplementation: typeof globalThis.fetch = globalThis.fetch - ) { - // ensure that if it's called a method, `this` won't be bound to the instance - const unboundFetch: typeof globalThis.fetch = async (...args) => - fetchImplementation(...args); + public constructor( + private readonly deviceId: string, + private readonly fetchController: FetchController, + private readonly settings: Settings, + private readonly logger: Logger, + fetchImplementation: typeof globalThis.fetch = globalThis.fetch + ) { + // ensure that if it's called a method, `this` won't be bound to the instance + const unboundFetch: typeof globalThis.fetch = async (...args) => + fetchImplementation(...args); - this.client = this.fetchController.getControlledFetchImplementation( - this.logger, - unboundFetch - ); - this.pingClient = unboundFetch; - } + this.client = this.fetchController.getControlledFetchImplementation( + this.logger, + unboundFetch + ); + this.pingClient = unboundFetch; + } - private static async errorFromResponse( - response: Response - ): Promise { - if ( - response.headers - .get("Content-Type") - ?.includes("application/json") == true - ) { - const result: SerializedError = - (await response.json()) as SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - return SyncService.formatError(result); - } - return `HTTP ${response.status}: ${response.statusText}`; - } + private static async errorFromResponse( + response: Response + ): Promise { + if ( + response.headers + .get("Content-Type") + ?.includes("application/json") == true + ) { + const result: SerializedError = + (await response.json()) as SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + return SyncService.formatError(result); + } + return `HTTP ${response.status}: ${response.statusText}`; + } - private static formatError(error: SerializedError): string { - let result = error.message; - if (error.causes.length > 0) { - const causes = error.causes.join(", "); - result += ` caused by: ${causes}`; - } + private static formatError(error: SerializedError): string { + let result = error.message; + if (error.causes.length > 0) { + const causes = error.causes.join(", "); + result += ` caused by: ${causes}`; + } - return result; - } + return result; + } - public async create({ - documentId, - relativePath, - contentBytes - }: { - documentId?: DocumentId; - relativePath: RelativePath; - contentBytes: Uint8Array; - }): Promise { - return this.retryForever(async () => { - const formData = new FormData(); - if (documentId !== undefined) { - formData.append("document_id", documentId); - } - formData.append("relative_path", relativePath); - formData.append( - "content", - new Blob([new Uint8Array(contentBytes)]) - ); + public async create({ + documentId, + relativePath, + contentBytes + }: { + documentId?: DocumentId; + relativePath: RelativePath; + contentBytes: Uint8Array; + }): Promise { + return this.retryForever(async () => { + const formData = new FormData(); + if (documentId !== undefined) { + formData.append("document_id", documentId); + } + formData.append("relative_path", relativePath); + formData.append( + "content", + new Blob([new Uint8Array(contentBytes)]) + ); - this.logger.debug( - `Creating document with id ${documentId} and relative path ${relativePath}` - ); + this.logger.debug( + `Creating document with id ${documentId} and relative path ${relativePath}` + ); - const response = await this.client(this.getUrl("/documents"), { - method: "POST", - body: formData, - headers: this.getDefaultHeaders() - }); + const response = await this.client(this.getUrl("/documents"), { + method: "POST", + body: formData, + headers: this.getDefaultHeaders() + }); - if (!response.ok) { - throw new Error( - `Failed to create document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to create document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: DocumentVersionWithoutContent = - (await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: DocumentVersionWithoutContent = + (await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug(`Created document ${JSON.stringify(result)}`); + this.logger.debug(`Created document ${JSON.stringify(result)}`); - return result; - }); - } + return result; + }); + } - public async putText({ - parentVersionId, - documentId, - relativePath, - content - }: { - parentVersionId: VaultUpdateId; - documentId: DocumentId; - relativePath: RelativePath; - content: (number | string)[]; - }): Promise { - return this.retryForever(async () => { - this.logger.debug( - `Updating text document ${documentId} with parent version ${parentVersionId} and relative path ${relativePath}, content [${content.join(", ")}]` - ); + public async putText({ + parentVersionId, + documentId, + relativePath, + content + }: { + parentVersionId: VaultUpdateId; + documentId: DocumentId; + relativePath: RelativePath; + content: (number | string)[]; + }): Promise { + return this.retryForever(async () => { + this.logger.debug( + `Updating text document ${documentId} with parent version ${parentVersionId} and relative path ${relativePath}, content [${content.join(", ")}]` + ); - const request: UpdateTextDocumentVersion = { - parentVersionId, - relativePath, - content - }; + const request: UpdateTextDocumentVersion = { + parentVersionId, + relativePath, + content + }; - const response = await this.client( - this.getUrl(`/documents/${documentId}/text`), - { - method: "PUT", - body: JSON.stringify(request), - headers: this.getDefaultHeaders({ type: "json" }) - } - ); + const response = await this.client( + this.getUrl(`/documents/${documentId}/text`), + { + method: "PUT", + body: JSON.stringify(request), + headers: this.getDefaultHeaders({ type: "json" }) + } + ); - if (!response.ok) { - throw new Error( - `Failed to update document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to update document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: DocumentUpdateResponse = - (await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: DocumentUpdateResponse = + (await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug( - `Updated document ${JSON.stringify(result)} with id ${ - result.documentId - }}` - ); + this.logger.debug( + `Updated document ${JSON.stringify(result)} with id ${ + result.documentId + }}` + ); - return result; - }); - } + return result; + }); + } - public async putBinary({ - parentVersionId, - documentId, - relativePath, - contentBytes - }: { - parentVersionId: VaultUpdateId; - documentId: DocumentId; - relativePath: RelativePath; - contentBytes: Uint8Array; - }): Promise { - return this.retryForever(async () => { - this.logger.debug( - `Updating binary document ${documentId} with parent version ${parentVersionId} and relative path ${relativePath}` - ); - const formData = new FormData(); - formData.append("parent_version_id", parentVersionId.toString()); - formData.append("relative_path", relativePath); - formData.append( - "content", - new Blob([new Uint8Array(contentBytes)]) - ); + public async putBinary({ + parentVersionId, + documentId, + relativePath, + contentBytes + }: { + parentVersionId: VaultUpdateId; + documentId: DocumentId; + relativePath: RelativePath; + contentBytes: Uint8Array; + }): Promise { + return this.retryForever(async () => { + this.logger.debug( + `Updating binary document ${documentId} with parent version ${parentVersionId} and relative path ${relativePath}` + ); + const formData = new FormData(); + formData.append("parent_version_id", parentVersionId.toString()); + formData.append("relative_path", relativePath); + formData.append( + "content", + new Blob([new Uint8Array(contentBytes)]) + ); - const response = await this.client( - this.getUrl(`/documents/${documentId}/binary`), - { - method: "PUT", - body: formData, - headers: this.getDefaultHeaders() - } - ); + const response = await this.client( + this.getUrl(`/documents/${documentId}/binary`), + { + method: "PUT", + body: formData, + headers: this.getDefaultHeaders() + } + ); - if (!response.ok) { - throw new Error( - `Failed to update document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to update document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: DocumentUpdateResponse = - (await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: DocumentUpdateResponse = + (await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug( - `Updated document ${JSON.stringify(result)} with id ${ - result.documentId - }}` - ); + this.logger.debug( + `Updated document ${JSON.stringify(result)} with id ${ + result.documentId + }}` + ); - return result; - }); - } + return result; + }); + } - public async delete({ - documentId, - relativePath - }: { - documentId: DocumentId; - relativePath: RelativePath; - }): Promise { - return this.retryForever(async () => { - const request: DeleteDocumentVersion = { - relativePath - }; + public async delete({ + documentId, + relativePath + }: { + documentId: DocumentId; + relativePath: RelativePath; + }): Promise { + return this.retryForever(async () => { + const request: DeleteDocumentVersion = { + relativePath + }; - this.logger.debug( - `Delete document with id ${documentId} and relative path ${relativePath}` - ); + this.logger.debug( + `Delete document with id ${documentId} and relative path ${relativePath}` + ); - const response = await this.client( - this.getUrl(`/documents/${documentId}`), - { - method: "DELETE", - body: JSON.stringify(request), - headers: this.getDefaultHeaders({ type: "json" }) - } - ); + const response = await this.client( + this.getUrl(`/documents/${documentId}`), + { + method: "DELETE", + body: JSON.stringify(request), + headers: this.getDefaultHeaders({ type: "json" }) + } + ); - if (!response.ok) { - throw new Error( - `Failed to delete document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to delete document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: DocumentVersionWithoutContent = - (await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: DocumentVersionWithoutContent = + (await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug( - `Deleted document ${relativePath} with id ${documentId}` - ); + this.logger.debug( + `Deleted document ${relativePath} with id ${documentId}` + ); - return result; - }); - } + return result; + }); + } - public async get({ - documentId - }: { - documentId: DocumentId; - }): Promise { - return this.retryForever(async () => { - this.logger.debug(`Getting document with id ${documentId}`); + public async get({ + documentId + }: { + documentId: DocumentId; + }): Promise { + return this.retryForever(async () => { + this.logger.debug(`Getting document with id ${documentId}`); - const response = await this.client( - this.getUrl(`/documents/${documentId}`), - { - headers: this.getDefaultHeaders() - } - ); + const response = await this.client( + this.getUrl(`/documents/${documentId}`), + { + headers: this.getDefaultHeaders() + } + ); - if (!response.ok) { - throw new Error( - `Failed to get document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to get document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: DocumentVersion = - (await response.json()) as DocumentVersion; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: DocumentVersion = + (await response.json()) as DocumentVersion; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug(`Got document ${JSON.stringify(result)}`); + this.logger.debug(`Got document ${JSON.stringify(result)}`); - return result; - }); - } + return result; + }); + } - public async getDocumentVersionContent({ - documentId, - vaultUpdateId - }: { - documentId: DocumentId; - vaultUpdateId: VaultUpdateId; - }): Promise { - return this.retryForever(async () => { - this.logger.debug( - `Getting document with id ${documentId} and version ${vaultUpdateId}` - ); + public async getDocumentVersionContent({ + documentId, + vaultUpdateId + }: { + documentId: DocumentId; + vaultUpdateId: VaultUpdateId; + }): Promise { + return this.retryForever(async () => { + this.logger.debug( + `Getting document with id ${documentId} and version ${vaultUpdateId}` + ); - const response = await this.client( - this.getUrl( - `/documents/${documentId}/versions/${vaultUpdateId}/content` - ), - { - headers: this.getDefaultHeaders() - } - ); + const response = await this.client( + this.getUrl( + `/documents/${documentId}/versions/${vaultUpdateId}/content` + ), + { + headers: this.getDefaultHeaders() + } + ); - if (!response.ok) { - throw new Error( - `Failed to get document: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to get document: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result = await response.bytes(); - this.logger.debug( - `Got document version content for document ${documentId} version ${vaultUpdateId}` - ); - return result; - }); - } + const result = await response.bytes(); + this.logger.debug( + `Got document version content for document ${documentId} version ${vaultUpdateId}` + ); + return result; + }); + } - public async getAll( - since?: VaultUpdateId - ): Promise { - return this.retryForever(async () => { - this.logger.debug( - "Getting all documents" + - (since != null ? ` since ${since}` : "") - ); + public async getAll( + since?: VaultUpdateId + ): Promise { + return this.retryForever(async () => { + this.logger.debug( + "Getting all documents" + + (since != null ? ` since ${since}` : "") + ); - const url = new URL(this.getUrl("/documents")); - if (since !== undefined) { - url.searchParams.append("since", since.toString()); - } - const response = await this.client(url.toString(), { - headers: this.getDefaultHeaders() - }); + const url = new URL(this.getUrl("/documents")); + if (since !== undefined) { + url.searchParams.append("since", since.toString()); + } + const response = await this.client(url.toString(), { + headers: this.getDefaultHeaders() + }); - if (!response.ok) { - throw new Error( - `Failed to get documents: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to get documents: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: FetchLatestDocumentsResponse = - (await response.json()) as FetchLatestDocumentsResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: FetchLatestDocumentsResponse = + (await response.json()) as FetchLatestDocumentsResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug( - `Got ${result.latestDocuments.length} document metadata` - ); + this.logger.debug( + `Got ${result.latestDocuments.length} document metadata` + ); - return result; - }); - } + return result; + }); + } - public async ping(): Promise { - this.logger.debug("Pinging server"); - const response = await this.pingClient(this.getUrl("/ping"), { - headers: this.getDefaultHeaders() - }); + public async ping(): Promise { + this.logger.debug("Pinging server"); + const response = await this.pingClient(this.getUrl("/ping"), { + headers: this.getDefaultHeaders() + }); - if (!response.ok) { - throw new Error( - `Failed to ping server: ${await SyncService.errorFromResponse( - response - )}` - ); - } + if (!response.ok) { + throw new Error( + `Failed to ping server: ${await SyncService.errorFromResponse( + response + )}` + ); + } - const result: PingResponse = (await response.json()) as PingResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + const result: PingResponse = (await response.json()) as PingResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion - this.logger.debug( - `Pinged server, got response: ${JSON.stringify(result)}` - ); + this.logger.debug( + `Pinged server, got response: ${JSON.stringify(result)}` + ); - return result; - } + return result; + } - private getUrl(path: string): string { - const { vaultName, remoteUri } = this.settings.getSettings(); - const remoteUriWithoutTrailingSlash = remoteUri.replace(/\/+$/, ""); - const encodedVaultName = encodeURIComponent(vaultName.trim()); - return `${remoteUriWithoutTrailingSlash}/vaults/${encodedVaultName}${path}`; - } + private getUrl(path: string): string { + const { vaultName, remoteUri } = this.settings.getSettings(); + const remoteUriWithoutTrailingSlash = remoteUri.replace(/\/+$/, ""); + const encodedVaultName = encodeURIComponent(vaultName.trim()); + return `${remoteUriWithoutTrailingSlash}/vaults/${encodedVaultName}${path}`; + } - private getDefaultHeaders( - { type }: { type?: "json" } = { type: undefined } - ): Record { - const headers: Record = { - "device-id": this.deviceId, - authorization: `Bearer ${this.settings.getSettings().token}` - }; + private getDefaultHeaders( + { type }: { type?: "json" } = { type: undefined } + ): Record { + const headers: Record = { + "device-id": this.deviceId, + authorization: `Bearer ${this.settings.getSettings().token}` + }; - if (type === "json") { - headers["Content-Type"] = "application/json"; - } + if (type === "json") { + headers["Content-Type"] = "application/json"; + } - return headers; - } + return headers; + } - private async retryForever(fn: () => Promise): Promise { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - while (true) { - try { - return await fn(); - } catch (e) { - // We must not retry errors coming from reset - if (e instanceof SyncResetError) { - throw e; - } + private async retryForever(fn: () => Promise): Promise { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + while (true) { + try { + return await fn(); + } catch (e) { + // We must not retry errors coming from reset + if (e instanceof SyncResetError) { + throw e; + } - const retryInterval = - this.settings.getSettings().networkRetryIntervalMs; - this.logger.error( - `Failed network call (${e}), retrying in ${retryInterval}ms` - ); - await sleep(retryInterval); - } - } - } + const retryInterval = + this.settings.getSettings().networkRetryIntervalMs; + this.logger.error( + `Failed network call (${e}), retrying in ${retryInterval}ms` + ); + await sleep(retryInterval); + } + } + } } diff --git a/frontend/sync-client/src/services/types/ClientCursors.ts b/frontend/sync-client/src/services/types/ClientCursors.ts index 8222bfb0..e8c9b93d 100644 --- a/frontend/sync-client/src/services/types/ClientCursors.ts +++ b/frontend/sync-client/src/services/types/ClientCursors.ts @@ -2,7 +2,7 @@ import type { DocumentWithCursors } from "./DocumentWithCursors"; export interface ClientCursors { - userName: string; - deviceId: string; - documentsWithCursors: DocumentWithCursors[]; + userName: string; + deviceId: string; + documentsWithCursors: DocumentWithCursors[]; } diff --git a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts index d4bd376b..ed921f18 100644 --- a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts @@ -1,13 +1,13 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface CreateDocumentVersion { - /** - * The client can decide the document id (if it wishes to) in order - * to help with syncing. If the client does not provide a document id, - * the server will generate one. If the client provides a document id - * it must not already exist in the database. - */ - document_id: string | null; - relative_path: string; - content: number[]; + /** + * The client can decide the document id (if it wishes to) in order + * to help with syncing. If the client does not provide a document id, + * the server will generate one. If the client provides a document id + * it must not already exist in the database. + */ + document_id: string | null; + relative_path: string; + content: number[]; } diff --git a/frontend/sync-client/src/services/types/CursorPositionFromClient.ts b/frontend/sync-client/src/services/types/CursorPositionFromClient.ts index ca940e3e..ee937f4e 100644 --- a/frontend/sync-client/src/services/types/CursorPositionFromClient.ts +++ b/frontend/sync-client/src/services/types/CursorPositionFromClient.ts @@ -2,5 +2,5 @@ import type { DocumentWithCursors } from "./DocumentWithCursors"; export interface CursorPositionFromClient { - documentsWithCursors: DocumentWithCursors[]; + documentsWithCursors: DocumentWithCursors[]; } diff --git a/frontend/sync-client/src/services/types/CursorPositionFromServer.ts b/frontend/sync-client/src/services/types/CursorPositionFromServer.ts index 2556b748..52a24f27 100644 --- a/frontend/sync-client/src/services/types/CursorPositionFromServer.ts +++ b/frontend/sync-client/src/services/types/CursorPositionFromServer.ts @@ -2,5 +2,5 @@ import type { ClientCursors } from "./ClientCursors"; export interface CursorPositionFromServer { - clients: ClientCursors[]; + clients: ClientCursors[]; } diff --git a/frontend/sync-client/src/services/types/CursorSpan.ts b/frontend/sync-client/src/services/types/CursorSpan.ts index 5bc2542e..2cc2b7fc 100644 --- a/frontend/sync-client/src/services/types/CursorSpan.ts +++ b/frontend/sync-client/src/services/types/CursorSpan.ts @@ -1,6 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface CursorSpan { - start: number; - end: number; + start: number; + end: number; } diff --git a/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts b/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts index 9edb09ed..99ecc9e7 100644 --- a/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts @@ -1,5 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface DeleteDocumentVersion { - relativePath: string; + relativePath: string; } diff --git a/frontend/sync-client/src/services/types/DocumentUpdateResponse.ts b/frontend/sync-client/src/services/types/DocumentUpdateResponse.ts index f0ed7abf..7fd06c7a 100644 --- a/frontend/sync-client/src/services/types/DocumentUpdateResponse.ts +++ b/frontend/sync-client/src/services/types/DocumentUpdateResponse.ts @@ -6,5 +6,5 @@ import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutCont * Response to an update document request. */ export type DocumentUpdateResponse = - | ({ type: "FastForwardUpdate" } & DocumentVersionWithoutContent) - | ({ type: "MergingUpdate" } & DocumentVersion); + | ({ type: "FastForwardUpdate" } & DocumentVersionWithoutContent) + | ({ type: "MergingUpdate" } & DocumentVersion); diff --git a/frontend/sync-client/src/services/types/DocumentVersion.ts b/frontend/sync-client/src/services/types/DocumentVersion.ts index 2076d296..3b9aa37b 100644 --- a/frontend/sync-client/src/services/types/DocumentVersion.ts +++ b/frontend/sync-client/src/services/types/DocumentVersion.ts @@ -1,12 +1,12 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface DocumentVersion { - vaultUpdateId: number; - documentId: string; - relativePath: string; - updatedDate: string; - contentBase64: string; - isDeleted: boolean; - userId: string; - deviceId: string; + vaultUpdateId: number; + documentId: string; + relativePath: string; + updatedDate: string; + contentBase64: string; + isDeleted: boolean; + userId: string; + deviceId: string; } diff --git a/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts b/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts index cb23f6a5..4b24e7c5 100644 --- a/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts +++ b/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts @@ -1,12 +1,12 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface DocumentVersionWithoutContent { - vaultUpdateId: number; - documentId: string; - relativePath: string; - updatedDate: string; - isDeleted: boolean; - userId: string; - deviceId: string; - contentSize: number; + vaultUpdateId: number; + documentId: string; + relativePath: string; + updatedDate: string; + isDeleted: boolean; + userId: string; + deviceId: string; + contentSize: number; } diff --git a/frontend/sync-client/src/services/types/DocumentWithCursors.ts b/frontend/sync-client/src/services/types/DocumentWithCursors.ts index dae654c7..dcfe6e2d 100644 --- a/frontend/sync-client/src/services/types/DocumentWithCursors.ts +++ b/frontend/sync-client/src/services/types/DocumentWithCursors.ts @@ -2,8 +2,8 @@ import type { CursorSpan } from "./CursorSpan"; export interface DocumentWithCursors { - vault_update_id: number | null; - document_id: string; - relative_path: string; - cursors: CursorSpan[]; + vault_update_id: number | null; + document_id: string; + relative_path: string; + cursors: CursorSpan[]; } diff --git a/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts index 67c19b2d..160c9279 100644 --- a/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts +++ b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts @@ -5,9 +5,9 @@ import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutCont * Response to a fetch latest documents request. */ export interface FetchLatestDocumentsResponse { - latestDocuments: DocumentVersionWithoutContent[]; - /** - * The update ID of the latest document in the response. - */ - lastUpdateId: bigint; + latestDocuments: DocumentVersionWithoutContent[]; + /** + * The update ID of the latest document in the response. + */ + lastUpdateId: bigint; } diff --git a/frontend/sync-client/src/services/types/PingResponse.ts b/frontend/sync-client/src/services/types/PingResponse.ts index cc7370e7..6db66354 100644 --- a/frontend/sync-client/src/services/types/PingResponse.ts +++ b/frontend/sync-client/src/services/types/PingResponse.ts @@ -4,22 +4,22 @@ * Response to a ping request. */ export interface PingResponse { - /** - * Semantic version of the server. - */ - serverVersion: string; - /** - * Whether the client is authenticated based on the sent Authorization - * header. - */ - isAuthenticated: boolean; - /** - * List of file extensions that are allowed to be merged. - */ - mergeableFileExtensions: string[]; - /** - * API version ensuring backwards & forwards compatibility between the client - * and server. - */ - supportedApiVersion: number; + /** + * Semantic version of the server. + */ + serverVersion: string; + /** + * Whether the client is authenticated based on the sent Authorization + * header. + */ + isAuthenticated: boolean; + /** + * List of file extensions that are allowed to be merged. + */ + mergeableFileExtensions: string[]; + /** + * API version ensuring backwards & forwards compatibility between the client + * and server. + */ + supportedApiVersion: number; } diff --git a/frontend/sync-client/src/services/types/SerializedError.ts b/frontend/sync-client/src/services/types/SerializedError.ts index c0979c5a..ec1c4503 100644 --- a/frontend/sync-client/src/services/types/SerializedError.ts +++ b/frontend/sync-client/src/services/types/SerializedError.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface SerializedError { - errorType: string; - message: string; - causes: string[]; + errorType: string; + message: string; + causes: string[]; } diff --git a/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts b/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts index bc3d54e5..4e57a297 100644 --- a/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface UpdateDocumentVersion { - parent_version_id: bigint; - relative_path: string; - content: number[]; + parent_version_id: bigint; + relative_path: string; + content: number[]; } diff --git a/frontend/sync-client/src/services/types/UpdateTextDocumentVersion.ts b/frontend/sync-client/src/services/types/UpdateTextDocumentVersion.ts index b3a5499b..46f36bd0 100644 --- a/frontend/sync-client/src/services/types/UpdateTextDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/UpdateTextDocumentVersion.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface UpdateTextDocumentVersion { - parentVersionId: number; - relativePath: string; - content: (number | string)[]; + parentVersionId: number; + relativePath: string; + content: (number | string)[]; } diff --git a/frontend/sync-client/src/services/types/WebSocketClientMessage.ts b/frontend/sync-client/src/services/types/WebSocketClientMessage.ts index e7de2cf3..9608f3af 100644 --- a/frontend/sync-client/src/services/types/WebSocketClientMessage.ts +++ b/frontend/sync-client/src/services/types/WebSocketClientMessage.ts @@ -3,5 +3,5 @@ import type { CursorPositionFromClient } from "./CursorPositionFromClient"; import type { WebSocketHandshake } from "./WebSocketHandshake"; export type WebSocketClientMessage = - | ({ type: "handshake" } & WebSocketHandshake) - | ({ type: "cursorPositions" } & CursorPositionFromClient); + | ({ type: "handshake" } & WebSocketHandshake) + | ({ type: "cursorPositions" } & CursorPositionFromClient); diff --git a/frontend/sync-client/src/services/types/WebSocketHandshake.ts b/frontend/sync-client/src/services/types/WebSocketHandshake.ts index 068b3505..a2910f49 100644 --- a/frontend/sync-client/src/services/types/WebSocketHandshake.ts +++ b/frontend/sync-client/src/services/types/WebSocketHandshake.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export interface WebSocketHandshake { - token: string; - deviceId: string; - lastSeenVaultUpdateId: number | null; + token: string; + deviceId: string; + lastSeenVaultUpdateId: number | null; } diff --git a/frontend/sync-client/src/services/types/WebSocketServerMessage.ts b/frontend/sync-client/src/services/types/WebSocketServerMessage.ts index 8ebf8911..fd250b7b 100644 --- a/frontend/sync-client/src/services/types/WebSocketServerMessage.ts +++ b/frontend/sync-client/src/services/types/WebSocketServerMessage.ts @@ -3,5 +3,5 @@ import type { CursorPositionFromServer } from "./CursorPositionFromServer"; import type { WebSocketVaultUpdate } from "./WebSocketVaultUpdate"; export type WebSocketServerMessage = - | ({ type: "vaultUpdate" } & WebSocketVaultUpdate) - | ({ type: "cursorPositions" } & CursorPositionFromServer); + | ({ type: "vaultUpdate" } & WebSocketVaultUpdate) + | ({ type: "cursorPositions" } & CursorPositionFromServer); diff --git a/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts b/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts index ad50c25d..f1ea0f80 100644 --- a/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts +++ b/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts @@ -2,6 +2,6 @@ import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutContent"; export interface WebSocketVaultUpdate { - documents: DocumentVersionWithoutContent[]; - isInitialSync: boolean; + documents: DocumentVersionWithoutContent[]; + isInitialSync: boolean; } diff --git a/frontend/sync-client/src/services/websocket-manager.test.ts b/frontend/sync-client/src/services/websocket-manager.test.ts index 8dd8180a..fef901e7 100644 --- a/frontend/sync-client/src/services/websocket-manager.test.ts +++ b/frontend/sync-client/src/services/websocket-manager.test.ts @@ -8,291 +8,291 @@ import type { Settings } from "../persistence/settings"; const WebSocket = require("ws") as typeof globalThis.WebSocket; class MockCloseEvent extends Event { - public code: number; - public reason: string; + public code: number; + public reason: string; - public constructor( - type: string, - options: { code: number; reason: string } - ) { - super(type); - this.code = options.code; - this.reason = options.reason; - } + public constructor( + type: string, + options: { code: number; reason: string } + ) { + super(type); + this.code = options.code; + this.reason = options.reason; + } } class MockMessageEvent extends Event { - public data: string; + public data: string; - public constructor(type: string, options: { data: string }) { - super(type); - this.data = options.data; - } + public constructor(type: string, options: { data: string }) { + super(type); + this.data = options.data; + } } class MockWebSocket { - public readyState: number = WebSocket.CONNECTING; - public onopen: ((event: Event) => void) | null = null; - public onclose: ((event: MockCloseEvent) => void) | null = null; - public onmessage: ((event: MockMessageEvent) => void) | null = null; - public onerror: ((event: Event) => void) | null = null; + public readyState: number = WebSocket.CONNECTING; + public onopen: ((event: Event) => void) | null = null; + public onclose: ((event: MockCloseEvent) => void) | null = null; + public onmessage: ((event: MockMessageEvent) => void) | null = null; + public onerror: ((event: Event) => void) | null = null; - public sentMessages: string[] = []; + public sentMessages: string[] = []; - public constructor(public url: string) { - setTimeout(() => { - if (this.readyState === WebSocket.CONNECTING) { - this.readyState = WebSocket.OPEN; - this.onopen?.(new Event("open")); - } - }, 0); - } + public constructor(public url: string) { + setTimeout(() => { + if (this.readyState === WebSocket.CONNECTING) { + this.readyState = WebSocket.OPEN; + this.onopen?.(new Event("open")); + } + }, 0); + } - public send(data: string): void { - if (this.readyState !== WebSocket.OPEN) { - throw new Error("WebSocket is not open"); - } - this.sentMessages.push(data); - } + public send(data: string): void { + if (this.readyState !== WebSocket.OPEN) { + throw new Error("WebSocket is not open"); + } + this.sentMessages.push(data); + } - public close(code?: number, reason?: string): void { - this.readyState = WebSocket.CLOSED; - this.onclose?.( - new MockCloseEvent("close", { - code: code ?? 1000, - reason: reason ?? "" - }) - ); - } + public close(code?: number, reason?: string): void { + this.readyState = WebSocket.CLOSED; + this.onclose?.( + new MockCloseEvent("close", { + code: code ?? 1000, + reason: reason ?? "" + }) + ); + } - public simulateMessage(data: unknown): void { - this.onmessage?.( - new MockMessageEvent("message", { data: JSON.stringify(data) }) - ); - } + public simulateMessage(data: unknown): void { + this.onmessage?.( + new MockMessageEvent("message", { data: JSON.stringify(data) }) + ); + } } type MockFn unknown> = T & { - calls: Parameters[]; + calls: Parameters[]; }; function createMockFn unknown>( - implementation?: T + implementation?: T ): MockFn { - const calls: Parameters[] = []; - const mockFn = ((...args: Parameters) => { - calls.push(args); - return implementation?.(...args); - }) as unknown as MockFn; - mockFn.calls = calls; - return mockFn; + const calls: Parameters[] = []; + const mockFn = ((...args: Parameters) => { + calls.push(args); + return implementation?.(...args); + }) as unknown as MockFn; + mockFn.calls = calls; + return mockFn; } describe("WebSocketManager", () => { - let mockLogger: Logger = undefined as unknown as Logger; - let mockSettings: Settings = undefined as unknown as Settings; - let deviceId = "test-device-123"; + let mockLogger: Logger = undefined as unknown as Logger; + let mockSettings: Settings = undefined as unknown as Settings; + let deviceId = "test-device-123"; - beforeEach(() => { - deviceId = "test-device-123"; - const noop = (): void => { - // Intentionally empty for mock - }; - mockLogger = { - info: createMockFn(noop), - warn: createMockFn(noop), - error: createMockFn(noop), - debug: createMockFn(noop) - } as unknown as Logger; + beforeEach(() => { + deviceId = "test-device-123"; + const noop = (): void => { + // Intentionally empty for mock + }; + mockLogger = { + info: createMockFn(noop), + warn: createMockFn(noop), + error: createMockFn(noop), + debug: createMockFn(noop) + } as unknown as Logger; - mockSettings = { - getSettings: () => ({ - remoteUri: "https://example.com", - vaultName: "test-vault", - webSocketRetryIntervalMs: 1000 - }) - } as unknown as Settings; - }); + mockSettings = { + getSettings: () => ({ + remoteUri: "https://example.com", + vaultName: "test-vault", + webSocketRetryIntervalMs: 1000 + }) + } as unknown as Settings; + }); - it("cleans up promises after message handling", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("cleans up promises after message handling", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - manager.onRemoteVaultUpdateReceived.add(async () => { - await new Promise((resolve) => setTimeout(resolve, 10)); - }); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.onRemoteVaultUpdateReceived.add(async () => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - const { outstandingPromises } = manager as unknown as { - outstandingPromises: Promise[]; - }; - const mockWs = (manager as unknown as { webSocket: MockWebSocket }) - .webSocket; + const { outstandingPromises } = manager as unknown as { + outstandingPromises: Promise[]; + }; + const mockWs = (manager as unknown as { webSocket: MockWebSocket }) + .webSocket; - mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); - mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); - mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); + mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); + mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); + mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); - assert.strictEqual(outstandingPromises.length, 0); - await manager.stop(); - }); + assert.strictEqual(outstandingPromises.length, 0); + await manager.stop(); + }); - it("cleans up cursor position promises", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("cleans up cursor position promises", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - manager.onRemoteCursorsUpdateReceived.add(async () => { - await new Promise((resolve) => setTimeout(resolve, 10)); - }); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.onRemoteCursorsUpdateReceived.add(async () => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - const { outstandingPromises } = manager as unknown as { - outstandingPromises: Promise[]; - }; - const mockWs = (manager as unknown as { webSocket: MockWebSocket }) - .webSocket; + const { outstandingPromises } = manager as unknown as { + outstandingPromises: Promise[]; + }; + const mockWs = (manager as unknown as { webSocket: MockWebSocket }) + .webSocket; - mockWs.simulateMessage({ - type: "cursorPositions", - clients: [{ deviceId: "other-device", cursors: [] }] - }); + mockWs.simulateMessage({ + type: "cursorPositions", + clients: [{ deviceId: "other-device", cursors: [] }] + }); - await new Promise((resolve) => setTimeout(resolve, 100)); - assert.strictEqual(outstandingPromises.length, 0); - await manager.stop(); - }); + await new Promise((resolve) => setTimeout(resolve, 100)); + assert.strictEqual(outstandingPromises.length, 0); + await manager.stop(); + }); - it("logs handshake send errors", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("logs handshake send errors", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - const mockWs = (manager as unknown as { webSocket: MockWebSocket }) - .webSocket; - mockWs.send = (): void => { - throw new Error("Buffer full"); - }; + const mockWs = (manager as unknown as { webSocket: MockWebSocket }) + .webSocket; + mockWs.send = (): void => { + throw new Error("Buffer full"); + }; - assert.throws(() => { - manager.sendHandshakeMessage({ - type: "handshake", - token: "test", - deviceId: "test", - lastSeenVaultUpdateId: null - }); - }); + assert.throws(() => { + manager.sendHandshakeMessage({ + type: "handshake", + token: "test", + deviceId: "test", + lastSeenVaultUpdateId: null + }); + }); - await manager.stop(); - }); + await manager.stop(); + }); - it("completes stop with timeout protection", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("completes stop with timeout protection", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - await manager.stop(); - assert.ok(true); - }); + await manager.stop(); + assert.ok(true); + }); - it("clears old handlers on reconnection", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("clears old handlers on reconnection", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - let statusChangeCount = 0; - manager.onWebSocketStatusChanged.add(() => { - statusChangeCount++; - }); + let statusChangeCount = 0; + manager.onWebSocketStatusChanged.add(() => { + statusChangeCount++; + }); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - const firstWs = (manager as unknown as { webSocket: MockWebSocket }) - .webSocket; + const firstWs = (manager as unknown as { webSocket: MockWebSocket }) + .webSocket; - statusChangeCount = 0; + statusChangeCount = 0; - ( - manager as unknown as { initializeWebSocket: () => void } - ).initializeWebSocket(); - await new Promise((resolve) => setTimeout(resolve, 50)); + ( + manager as unknown as { initializeWebSocket: () => void } + ).initializeWebSocket(); + await new Promise((resolve) => setTimeout(resolve, 50)); - statusChangeCount = 0; + statusChangeCount = 0; - // Old handler should be cleared - firstWs.onclose?.( - new MockCloseEvent("close", { code: 1000, reason: "test" }) - ); + // Old handler should be cleared + firstWs.onclose?.( + new MockCloseEvent("close", { code: 1000, reason: "test" }) + ); - assert.strictEqual(statusChangeCount, 0); - await manager.stop(); - }); + assert.strictEqual(statusChangeCount, 0); + await manager.stop(); + }); - it("tracks message handling promises", async () => { - const manager = new WebSocketManager( - deviceId, - mockLogger, - mockSettings, - MockWebSocket as unknown as typeof WebSocket - ); + it("tracks message handling promises", async () => { + const manager = new WebSocketManager( + deviceId, + mockLogger, + mockSettings, + MockWebSocket as unknown as typeof WebSocket + ); - // eslint-disable-next-line @typescript-eslint/init-declarations - let resolveListener: () => void; - const listenerPromise = new Promise((resolve) => { - resolveListener = resolve; - }); + // eslint-disable-next-line @typescript-eslint/init-declarations + let resolveListener: () => void; + const listenerPromise = new Promise((resolve) => { + resolveListener = resolve; + }); - manager.onRemoteVaultUpdateReceived.add(async () => { - await listenerPromise; - }); + manager.onRemoteVaultUpdateReceived.add(async () => { + await listenerPromise; + }); - manager.start(); - await new Promise((resolve) => setTimeout(resolve, 50)); + manager.start(); + await new Promise((resolve) => setTimeout(resolve, 50)); - const mockWs = (manager as unknown as { webSocket: MockWebSocket }) - .webSocket; - mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); + const mockWs = (manager as unknown as { webSocket: MockWebSocket }) + .webSocket; + mockWs.simulateMessage({ type: "vaultUpdate", updates: [] }); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => setTimeout(resolve, 10)); - const { outstandingPromises } = manager as unknown as { - outstandingPromises: Promise[]; - }; + const { outstandingPromises } = manager as unknown as { + outstandingPromises: Promise[]; + }; - assert.ok(outstandingPromises.length > 0); + assert.ok(outstandingPromises.length > 0); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - resolveListener!(); - await new Promise((resolve) => setTimeout(resolve, 50)); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + resolveListener!(); + await new Promise((resolve) => setTimeout(resolve, 50)); - assert.strictEqual(outstandingPromises.length, 0); - await manager.stop(); - }); + assert.strictEqual(outstandingPromises.length, 0); + await manager.stop(); + }); }); diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index af615f52..550ef096 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -240,10 +240,10 @@ export class SyncClient { } /** - * Reload settings from disk overriding current in-memory settings. - * Missing values will be filled in from DEFAULT_SETTINGS rather than - * retaining current in-memory settings. - */ + * Reload settings from disk overriding current in-memory settings. + * Missing values will be filled in from DEFAULT_SETTINGS rather than + * retaining current in-memory settings. + */ public async reloadSettings(): Promise { this.checkIfDestroyed("reloadSettings"); @@ -275,10 +275,10 @@ export class SyncClient { } /** - * Wait for the in-flight operations to finish, reset all tracking, - * and the local database but retain the settings. - * The SyncClient can be used again after calling this method. - */ + * Wait for the in-flight operations to finish, reset all tracking, + * and the local database but retain the settings. + * The SyncClient can be used again after calling this method. + */ public async reset(): Promise { this.checkIfDestroyed("reset"); @@ -430,9 +430,9 @@ export class SyncClient { } /** - * Completely destroy the SyncClient, cancelling all in-progress operations. - * After calling this method, the SyncClient cannot be used again. - */ + * Completely destroy the SyncClient, cancelling all in-progress operations. + * After calling this method, the SyncClient cannot be used again. + */ public async destroy(): Promise { this.checkIfDestroyed("destroy"); diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index e142e409..24b4a890 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -479,10 +479,10 @@ export class Syncer { } /** - * Create fake documents in the database for all files that are present locally - * and also exist remotely. This will stop the subequent syncs from duplicating - * the documents by creating the same documents from multiple clients. - */ + * Create fake documents in the database for all files that are present locally + * and also exist remotely. This will stop the subequent syncs from duplicating + * the documents by creating the same documents from multiple clients. + */ private async createFakeDocumentsFromRemoteState(): Promise { if (this.database.getHasInitialSyncCompleted()) { return; diff --git a/frontend/sync-client/src/tracing/sync-history.ts b/frontend/sync-client/src/tracing/sync-history.ts index 99cfb5ce..5768296d 100644 --- a/frontend/sync-client/src/tracing/sync-history.ts +++ b/frontend/sync-client/src/tracing/sync-history.ts @@ -88,11 +88,11 @@ export class SyncHistory { } /** - * Insert the entry at the beginning of the history list. If the entry - * already in the list, it will get moved to the beginning and updated. - * - * If the entry list is too long, the oldest entry will be removed. - */ + * Insert the entry at the beginning of the history list. If the entry + * already in the list, it will get moved to the beginning and updated. + * + * If the entry list is too long, the oldest entry will be removed. + */ public addHistoryEntry(entry: CommonHistoryEntry): void { const historyEntry = { ...entry, diff --git a/frontend/sync-client/src/types/document-sync-status.ts b/frontend/sync-client/src/types/document-sync-status.ts index 07a0e801..501469e5 100644 --- a/frontend/sync-client/src/types/document-sync-status.ts +++ b/frontend/sync-client/src/types/document-sync-status.ts @@ -1,5 +1,5 @@ export enum DocumentSyncStatus { - UP_TO_DATE = "UP_TO_DATE", - SYNCING = "SYNCING", - SYNCING_IS_DISABLED = "SYNCING_IS_DISABLED" + UP_TO_DATE = "UP_TO_DATE", + SYNCING = "SYNCING", + SYNCING_IS_DISABLED = "SYNCING_IS_DISABLED" } diff --git a/frontend/sync-client/src/types/document-up-to-dateness.ts b/frontend/sync-client/src/types/document-up-to-dateness.ts index 2f93f9b4..df30f5eb 100644 --- a/frontend/sync-client/src/types/document-up-to-dateness.ts +++ b/frontend/sync-client/src/types/document-up-to-dateness.ts @@ -1,5 +1,5 @@ export enum DocumentUpToDateness { - UpToDate = "UpToDate", // easiest case, the client can just show the cursors as-is - Prior = "Prior", // The cursors are outdated, so the client has to guess the cursor positions based on local updates. This is only possible if this client's cursor has once been up-to-date in a given document. - Later = "Later" // The cursors are from a future version of a document, there's no way we can accuratly show them locally. + UpToDate = "UpToDate", // easiest case, the client can just show the cursors as-is + Prior = "Prior", // The cursors are outdated, so the client has to guess the cursor positions based on local updates. This is only possible if this client's cursor has once been up-to-date in a given document. + Later = "Later" // The cursors are from a future version of a document, there's no way we can accuratly show them locally. } diff --git a/frontend/sync-client/src/types/maybe-outdated-client-cursors.ts b/frontend/sync-client/src/types/maybe-outdated-client-cursors.ts index e062f84e..4793b872 100644 --- a/frontend/sync-client/src/types/maybe-outdated-client-cursors.ts +++ b/frontend/sync-client/src/types/maybe-outdated-client-cursors.ts @@ -1,5 +1,5 @@ import type { ClientCursors } from "../services/types/ClientCursors"; export interface MaybeOutdatedClientCursors extends ClientCursors { - isOutdated: boolean; + isOutdated: boolean; } diff --git a/frontend/sync-client/src/types/network-connection-status.ts b/frontend/sync-client/src/types/network-connection-status.ts index fb93f5f5..bf876665 100644 --- a/frontend/sync-client/src/types/network-connection-status.ts +++ b/frontend/sync-client/src/types/network-connection-status.ts @@ -1,5 +1,5 @@ export interface NetworkConnectionStatus { - isSuccessful: boolean; - serverMessage: string; - isWebSocketConnected: boolean; + isSuccessful: boolean; + serverMessage: string; + isWebSocketConnected: boolean; } diff --git a/frontend/sync-client/src/utils/assert-set-contains-exactly.ts b/frontend/sync-client/src/utils/assert-set-contains-exactly.ts index 502dca03..5baf965e 100644 --- a/frontend/sync-client/src/utils/assert-set-contains-exactly.ts +++ b/frontend/sync-client/src/utils/assert-set-contains-exactly.ts @@ -1,13 +1,13 @@ import assert from "node:assert"; export function assertSetContainsExactly(set: Set, ...values: T[]): void { - assert.ok( - set.size === values.length && - Array.from(set).every((value) => values.includes(value)), - `Expected set to contain only ${values.map((v) => '"' + v + '"').join(", ")}, but it contained ${Array.from( - set - ) - .map((v) => '"' + v + '"') - .join(", ")}` - ); + assert.ok( + set.size === values.length && + Array.from(set).every((value) => values.includes(value)), + `Expected set to contain only ${values.map((v) => '"' + v + '"').join(", ")}, but it contained ${Array.from( + set + ) + .map((v) => '"' + v + '"') + .join(", ")}` + ); } diff --git a/frontend/sync-client/src/utils/await-all.test.ts b/frontend/sync-client/src/utils/await-all.test.ts index bbce9423..09a22d89 100644 --- a/frontend/sync-client/src/utils/await-all.test.ts +++ b/frontend/sync-client/src/utils/await-all.test.ts @@ -3,54 +3,54 @@ import assert from "node:assert"; import { awaitAll } from "./await-all"; void test("awaitAll resolves promises of the same type", async () => { - const promises = [ - Promise.resolve(1), - Promise.resolve(2), - Promise.resolve(3) - ]; + const promises = [ + Promise.resolve(1), + Promise.resolve(2), + Promise.resolve(3) + ]; - const results = await awaitAll(promises); - assert.deepStrictEqual(results, [1, 2, 3]); + const results = await awaitAll(promises); + assert.deepStrictEqual(results, [1, 2, 3]); }); void test("awaitAll resolves promises of different types", async () => { - const promises = [ - Promise.resolve("hello"), - Promise.resolve(42), - Promise.resolve(true) - ] as const; + const promises = [ + Promise.resolve("hello"), + Promise.resolve(42), + Promise.resolve(true) + ] as const; - const results = await awaitAll(promises); + const results = await awaitAll(promises); - // Type assertions to verify type inference - const str: string = results[0]; - const num: number = results[1]; - const bool: boolean = results[2]; + // Type assertions to verify type inference + const str: string = results[0]; + const num: number = results[1]; + const bool: boolean = results[2]; - assert.strictEqual(str, "hello"); - assert.strictEqual(num, 42); - assert.strictEqual(bool, true); + assert.strictEqual(str, "hello"); + assert.strictEqual(num, 42); + assert.strictEqual(bool, true); }); void test("awaitAll throws on first rejection", async () => { - const error = new Error("Test error"); - const promises = [ - Promise.resolve(1), - Promise.reject(error), - Promise.resolve(3) - ]; + const error = new Error("Test error"); + const promises = [ + Promise.resolve(1), + Promise.reject(error), + Promise.resolve(3) + ]; - await assert.rejects(async () => { - await awaitAll(promises); - }, error); + await assert.rejects(async () => { + await awaitAll(promises); + }, error); }); void test("awaitAll works with async functions", async () => { - const asyncString = async (): Promise => "async"; - const asyncNumber = async (): Promise => 123; + const asyncString = async (): Promise => "async"; + const asyncNumber = async (): Promise => 123; - const results = await awaitAll([asyncString(), asyncNumber()]); + const results = await awaitAll([asyncString(), asyncNumber()]); - assert.strictEqual(results[0], "async"); - assert.strictEqual(results[1], 123); + assert.strictEqual(results[0], "async"); + assert.strictEqual(results[1], 123); }); diff --git a/frontend/sync-client/src/utils/await-all.ts b/frontend/sync-client/src/utils/await-all.ts index b8d50250..9406a6b8 100644 --- a/frontend/sync-client/src/utils/await-all.ts +++ b/frontend/sync-client/src/utils/await-all.ts @@ -1,25 +1,25 @@ type PromiseTuple = readonly [ - ...{ [K in keyof T]: Promise } + ...{ [K in keyof T]: Promise } ]; type ResolvedTuple = { - [K in keyof T]: T[K]; + [K in keyof T]: T[K]; }; export const awaitAll = async ( - promises: PromiseTuple + promises: PromiseTuple ): Promise> => { - // eslint-disable-next-line no-restricted-properties - const result = await Promise.allSettled(promises); - for (const res of result) { - if (res.status === "rejected") { - throw res.reason; - } - } + // eslint-disable-next-line no-restricted-properties + const result = await Promise.allSettled(promises); + for (const res of result) { + if (res.status === "rejected") { + throw res.reason; + } + } - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - return result.map( - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - (res) => (res as PromiseFulfilledResult).value - ) as ResolvedTuple; + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + return result.map( + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + (res) => (res as PromiseFulfilledResult).value + ) as ResolvedTuple; }; diff --git a/frontend/sync-client/src/utils/create-client-id.ts b/frontend/sync-client/src/utils/create-client-id.ts index 60143b75..4da442c2 100644 --- a/frontend/sync-client/src/utils/create-client-id.ts +++ b/frontend/sync-client/src/utils/create-client-id.ts @@ -1,15 +1,15 @@ import { v4 as uuidv4 } from "uuid"; export function createClientId(): string { - // @ts-expect-error, injected by webpack - const packageVersion = __CURRENT_VERSION__; // eslint-disable-line + // @ts-expect-error, injected by webpack + const packageVersion = __CURRENT_VERSION__; // eslint-disable-line - const platform = - typeof navigator !== "undefined" - ? navigator.platform // eslint-disable-line @typescript-eslint/no-deprecated - : typeof process !== "undefined" - ? process.platform - : "unknown"; + const platform = + typeof navigator !== "undefined" + ? navigator.platform // eslint-disable-line @typescript-eslint/no-deprecated + : typeof process !== "undefined" + ? process.platform + : "unknown"; - return `vault-link/${packageVersion} (${uuidv4()}; ${platform})`; + return `vault-link/${packageVersion} (${uuidv4()}; ${platform})`; } diff --git a/frontend/sync-client/src/utils/create-promise.ts b/frontend/sync-client/src/utils/create-promise.ts index 542a4013..a49196ee 100644 --- a/frontend/sync-client/src/utils/create-promise.ts +++ b/frontend/sync-client/src/utils/create-promise.ts @@ -1,25 +1,25 @@ type ResolveFunction = undefined extends T - ? (value?: T) => unknown - : (value: T) => unknown; + ? (value?: T) => unknown + : (value: T) => unknown; /** * A type-safe utility function to create a Promise with resolve and reject functions. * @returns A tuple containing a Promise, a resolve function, and a reject function. */ export function createPromise(): [ - Promise, - ResolveFunction, - (error: unknown) => unknown + Promise, + ResolveFunction, + (error: unknown) => unknown ] { - let resolve: undefined | ResolveFunction = undefined; - let reject: undefined | ((error: unknown) => unknown) = undefined; + let resolve: undefined | ResolveFunction = undefined; + let reject: undefined | ((error: unknown) => unknown) = undefined; - const creationPromise = new Promise( - (resolve_, reject_) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - ((resolve = resolve_ as ResolveFunction), (reject = reject_)) - ); + const creationPromise = new Promise( + (resolve_, reject_) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + ((resolve = resolve_ as ResolveFunction), (reject = reject_)) + ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return [creationPromise, resolve!, reject!]; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return [creationPromise, resolve!, reject!]; } diff --git a/frontend/sync-client/src/utils/data-structures/event-listeners.ts b/frontend/sync-client/src/utils/data-structures/event-listeners.ts index 25be5344..008342e7 100644 --- a/frontend/sync-client/src/utils/data-structures/event-listeners.ts +++ b/frontend/sync-client/src/utils/data-structures/event-listeners.ts @@ -8,32 +8,32 @@ export class EventListeners any> { private readonly listeners: TListener[] = []; /** - * Adds a new listener to the collection. - * - * @param listener The listener callback to add - * @returns An unsubscribe function that removes this listener when called - */ + * Adds a new listener to the collection. + * + * @param listener The listener callback to add + * @returns An unsubscribe function that removes this listener when called + */ public add(listener: TListener): () => void { this.listeners.push(listener); return () => this.remove(listener); } /** - * Removes a listener from the collection. - * - * @param listener The listener callback to remove - * @returns true if the listener was found and removed, false otherwise - */ + * Removes a listener from the collection. + * + * @param listener The listener callback to remove + * @returns true if the listener was found and removed, false otherwise + */ public remove(listener: TListener): boolean { return removeFromArray(this.listeners, listener); } /** - * Triggers all listeners synchronously with the provided arguments. - * Any returned promises are ignored. Use triggerAsync() to await them. - * - * @param args The arguments to pass to each listener - */ + * Triggers all listeners synchronously with the provided arguments. + * Any returned promises are ignored. Use triggerAsync() to await them. + * + * @param args The arguments to pass to each listener + */ public trigger(...args: Parameters): void { this.listeners.forEach((listener) => { listener(...args); @@ -41,12 +41,12 @@ export class EventListeners any> { } /** - * Triggers all listeners and awaits any promises they return. - * Synchronous listeners are called immediately, and any async listeners - * are awaited in parallel. - * - * @param args The arguments to pass to each listener - */ + * Triggers all listeners and awaits any promises they return. + * Synchronous listeners are called immediately, and any async listeners + * are awaited in parallel. + * + * @param args The arguments to pass to each listener + */ public async triggerAsync(...args: Parameters): Promise { await awaitAll( this.listeners diff --git a/frontend/sync-client/src/utils/data-structures/fix-sized-cache.test.ts b/frontend/sync-client/src/utils/data-structures/fix-sized-cache.test.ts index a118815b..c5ca141c 100644 --- a/frontend/sync-client/src/utils/data-structures/fix-sized-cache.test.ts +++ b/frontend/sync-client/src/utils/data-structures/fix-sized-cache.test.ts @@ -3,273 +3,273 @@ import assert from "node:assert"; import { FixedSizeDocumentCache } from "./fix-sized-cache"; describe("fixedSizeDocumentCache", () => { - it("happyPath", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); + it("happyPath", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); - cache.put(1, doc1); - assert.equal(cache.get(1), doc1); - cache.put(2, doc2); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), doc2); - cache.put(3, doc3); - assert.equal(cache.get(1), undefined); - assert.equal(cache.get(2), doc2); - assert.equal(cache.get(3), doc3); - }); + cache.put(1, doc1); + assert.equal(cache.get(1), doc1); + cache.put(2, doc2); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), doc2); + cache.put(3, doc3); + assert.equal(cache.get(1), undefined); + assert.equal(cache.get(2), doc2); + assert.equal(cache.get(3), doc3); + }); - it("updateExistingEntry", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1_v1 = new Uint8Array([1, 2]); - const doc1_v2 = new Uint8Array([3, 4]); - const doc2 = new Uint8Array([5, 6]); + it("updateExistingEntry", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1_v1 = new Uint8Array([1, 2]); + const doc1_v2 = new Uint8Array([3, 4]); + const doc2 = new Uint8Array([5, 6]); - cache.put(1, doc1_v1); - assert.equal(cache.get(1), doc1_v1); - cache.put(2, doc2); - assert.equal(cache.get(1), doc1_v1); - assert.equal(cache.get(2), doc2); - cache.put(1, doc1_v2); // Update doc1 - assert.equal(cache.get(1), doc1_v2); - assert.equal(cache.get(2), doc2); - }); + cache.put(1, doc1_v1); + assert.equal(cache.get(1), doc1_v1); + cache.put(2, doc2); + assert.equal(cache.get(1), doc1_v1); + assert.equal(cache.get(2), doc2); + cache.put(1, doc1_v2); // Update doc1 + assert.equal(cache.get(1), doc1_v2); + assert.equal(cache.get(2), doc2); + }); - it("evictOldestEntry", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); + it("evictOldestEntry", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); - cache.put(1, doc1); - cache.put(2, doc2); - assert.equal(cache.get(2), doc2); - assert.equal(cache.get(1), doc1); - cache.put(3, doc3); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), undefined); - assert.equal(cache.get(3), doc3); - }); + cache.put(1, doc1); + cache.put(2, doc2); + assert.equal(cache.get(2), doc2); + assert.equal(cache.get(1), doc1); + cache.put(3, doc3); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), undefined); + assert.equal(cache.get(3), doc3); + }); - it("tooLargeEntry", async () => { - const cache = new FixedSizeDocumentCache(2); - const doc1 = new Uint8Array([1, 2, 3]); + it("tooLargeEntry", async () => { + const cache = new FixedSizeDocumentCache(2); + const doc1 = new Uint8Array([1, 2, 3]); - cache.put(1, doc1); - assert.equal(cache.get(1), undefined); - }); + cache.put(1, doc1); + assert.equal(cache.get(1), undefined); + }); - it("multipleEvictionsInSinglePut", async () => { - const cache = new FixedSizeDocumentCache(10); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); - const doc4 = new Uint8Array([7, 8, 9, 10, 11, 12, 13, 14]); // 8 bytes + it("multipleEvictionsInSinglePut", async () => { + const cache = new FixedSizeDocumentCache(10); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); + const doc4 = new Uint8Array([7, 8, 9, 10, 11, 12, 13, 14]); // 8 bytes - cache.put(1, doc1); - cache.put(2, doc2); - cache.put(3, doc3); - // Cache now has 6 bytes total + cache.put(1, doc1); + cache.put(2, doc2); + cache.put(3, doc3); + // Cache now has 6 bytes total - cache.put(4, doc4); // Should evict doc1 and doc2 to make room (total: 2+8=10) - assert.equal(cache.get(1), undefined); // Evicted - assert.equal(cache.get(2), undefined); // Evicted - assert.equal(cache.get(3), doc3); // Still present - assert.equal(cache.get(4), doc4); - }); + cache.put(4, doc4); // Should evict doc1 and doc2 to make room (total: 2+8=10) + assert.equal(cache.get(1), undefined); // Evicted + assert.equal(cache.get(2), undefined); // Evicted + assert.equal(cache.get(3), doc3); // Still present + assert.equal(cache.get(4), doc4); + }); - it("clearCache", async () => { - const cache = new FixedSizeDocumentCache(10); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); + it("clearCache", async () => { + const cache = new FixedSizeDocumentCache(10); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); - cache.put(1, doc1); - cache.put(2, doc2); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), doc2); + cache.put(1, doc1); + cache.put(2, doc2); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), doc2); - cache.reset(); - assert.equal(cache.get(1), undefined); - assert.equal(cache.get(2), undefined); + cache.reset(); + assert.equal(cache.get(1), undefined); + assert.equal(cache.get(2), undefined); - // Should be able to add entries after clear - cache.put(3, doc1); - assert.equal(cache.get(3), doc1); - }); + // Should be able to add entries after clear + cache.put(3, doc1); + assert.equal(cache.get(3), doc1); + }); - it("getNonExistentKey", async () => { - const cache = new FixedSizeDocumentCache(10); - const doc1 = new Uint8Array([1, 2]); - cache.put(1, doc1); - assert.equal(cache.get(999), undefined); - }); + it("getNonExistentKey", async () => { + const cache = new FixedSizeDocumentCache(10); + const doc1 = new Uint8Array([1, 2]); + cache.put(1, doc1); + assert.equal(cache.get(999), undefined); + }); - it("updateEntryWithDifferentSizeTriggeringEviction", async () => { - const cache = new FixedSizeDocumentCache(6); - const doc1_v1 = new Uint8Array([1, 2]); - const doc1_v2 = new Uint8Array([1, 2, 3, 4]); // Larger version - const doc2 = new Uint8Array([5, 6]); - const doc3 = new Uint8Array([7, 8]); + it("updateEntryWithDifferentSizeTriggeringEviction", async () => { + const cache = new FixedSizeDocumentCache(6); + const doc1_v1 = new Uint8Array([1, 2]); + const doc1_v2 = new Uint8Array([1, 2, 3, 4]); // Larger version + const doc2 = new Uint8Array([5, 6]); + const doc3 = new Uint8Array([7, 8]); - cache.put(1, doc1_v1); - cache.put(2, doc2); - cache.put(3, doc3); + cache.put(1, doc1_v1); + cache.put(2, doc2); + cache.put(3, doc3); - // Update doc1 with larger version, should evict doc2 - cache.put(1, doc1_v2); + // Update doc1 with larger version, should evict doc2 + cache.put(1, doc1_v2); - assert.equal(cache.get(1), doc1_v2); - assert.equal(cache.get(2), undefined); // Evicted - assert.equal(cache.get(3), doc3); - }); + assert.equal(cache.get(1), doc1_v2); + assert.equal(cache.get(2), undefined); // Evicted + assert.equal(cache.get(3), doc3); + }); - it("singleItemCache", async () => { - const cache = new FixedSizeDocumentCache(2); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); + it("singleItemCache", async () => { + const cache = new FixedSizeDocumentCache(2); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); - cache.put(1, doc1); - assert.equal(cache.get(1), doc1); + cache.put(1, doc1); + assert.equal(cache.get(1), doc1); - cache.put(2, doc2); - assert.equal(cache.get(1), undefined); // Evicted - assert.equal(cache.get(2), doc2); - }); + cache.put(2, doc2); + assert.equal(cache.get(1), undefined); // Evicted + assert.equal(cache.get(2), doc2); + }); - it("multipleGetsOnSameEntry", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); + it("multipleGetsOnSameEntry", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); - cache.put(1, doc1); - cache.put(2, doc2); + cache.put(1, doc1); + cache.put(2, doc2); - // Multiple gets on doc1 - cache.get(1); - cache.get(1); - cache.get(1); + // Multiple gets on doc1 + cache.get(1); + cache.get(1); + cache.get(1); - // Order should be: 2 (LRU), 1 (MRU) - cache.put(3, doc3); + // Order should be: 2 (LRU), 1 (MRU) + cache.put(3, doc3); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), undefined); // Evicted - assert.equal(cache.get(3), doc3); - }); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), undefined); // Evicted + assert.equal(cache.get(3), doc3); + }); - it("exactlySizedEntry", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2, 3, 4]); // Exactly cache size + it("exactlySizedEntry", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2, 3, 4]); // Exactly cache size - cache.put(1, doc1); - assert.equal(cache.get(1), doc1); + cache.put(1, doc1); + assert.equal(cache.get(1), doc1); - const doc2 = new Uint8Array([5, 6]); - cache.put(2, doc2); + const doc2 = new Uint8Array([5, 6]); + cache.put(2, doc2); - // doc1 should be evicted to make room for doc2 - assert.equal(cache.get(1), undefined); - assert.equal(cache.get(2), doc2); - }); + // doc1 should be evicted to make room for doc2 + assert.equal(cache.get(1), undefined); + assert.equal(cache.get(2), doc2); + }); - it("updateEntryMakesItMostRecent", async () => { - const cache = new FixedSizeDocumentCache(6); - const doc1_v1 = new Uint8Array([1, 2]); - const doc1_v2 = new Uint8Array([3, 4]); - const doc2 = new Uint8Array([5, 6]); - const doc3 = new Uint8Array([7, 8]); - const doc4 = new Uint8Array([9, 10]); + it("updateEntryMakesItMostRecent", async () => { + const cache = new FixedSizeDocumentCache(6); + const doc1_v1 = new Uint8Array([1, 2]); + const doc1_v2 = new Uint8Array([3, 4]); + const doc2 = new Uint8Array([5, 6]); + const doc3 = new Uint8Array([7, 8]); + const doc4 = new Uint8Array([9, 10]); - cache.put(1, doc1_v1); - cache.put(2, doc2); - cache.put(3, doc3); + cache.put(1, doc1_v1); + cache.put(2, doc2); + cache.put(3, doc3); - // Update doc1 (should move it to most recent) - cache.put(1, doc1_v2); + // Update doc1 (should move it to most recent) + cache.put(1, doc1_v2); - // Order should be: 2 (LRU), 3, 1 (MRU) - // Adding doc4 should evict doc2 - cache.put(4, doc4); + // Order should be: 2 (LRU), 3, 1 (MRU) + // Adding doc4 should evict doc2 + cache.put(4, doc4); - assert.equal(cache.get(1), doc1_v2); - assert.equal(cache.get(2), undefined); // Evicted - assert.equal(cache.get(3), doc3); - assert.equal(cache.get(4), doc4); - }); + assert.equal(cache.get(1), doc1_v2); + assert.equal(cache.get(2), undefined); // Evicted + assert.equal(cache.get(3), doc3); + assert.equal(cache.get(4), doc4); + }); - it("alternatingAccessPattern", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); + it("alternatingAccessPattern", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); - cache.put(1, doc1); - cache.put(2, doc2); + cache.put(1, doc1); + cache.put(2, doc2); - // Alternate access between doc1 and doc2 - cache.get(1); - cache.get(2); - cache.get(1); - cache.get(2); + // Alternate access between doc1 and doc2 + cache.get(1); + cache.get(2); + cache.get(1); + cache.get(2); - // Order should be: 1, 2 (MRU) - cache.put(3, doc3); + // Order should be: 1, 2 (MRU) + cache.put(3, doc3); - assert.equal(cache.get(1), undefined); // Evicted - assert.equal(cache.get(2), doc2); - assert.equal(cache.get(3), doc3); - }); + assert.equal(cache.get(1), undefined); // Evicted + assert.equal(cache.get(2), doc2); + assert.equal(cache.get(3), doc3); + }); - it("zeroByteDocs", async () => { - const cache = new FixedSizeDocumentCache(2); - const doc1 = new Uint8Array([]); - const doc2 = new Uint8Array([]); - const doc3 = new Uint8Array([1, 2]); + it("zeroByteDocs", async () => { + const cache = new FixedSizeDocumentCache(2); + const doc1 = new Uint8Array([]); + const doc2 = new Uint8Array([]); + const doc3 = new Uint8Array([1, 2]); - cache.put(1, doc1); - cache.put(2, doc2); - cache.put(3, doc3); + cache.put(1, doc1); + cache.put(2, doc2); + cache.put(3, doc3); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), doc2); - assert.equal(cache.get(3), doc3); - }); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), doc2); + assert.equal(cache.get(3), doc3); + }); - it("resizeToLargerSizeNoEviction", async () => { - const cache = new FixedSizeDocumentCache(4); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); + it("resizeToLargerSizeNoEviction", async () => { + const cache = new FixedSizeDocumentCache(4); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); - cache.put(1, doc1); - cache.put(2, doc2); + cache.put(1, doc1); + cache.put(2, doc2); - cache.resize(10); + cache.resize(10); - assert.equal(cache.get(1), doc1); - assert.equal(cache.get(2), doc2); - }); + assert.equal(cache.get(1), doc1); + assert.equal(cache.get(2), doc2); + }); - it("resizeCausesMultipleEvictions", async () => { - const cache = new FixedSizeDocumentCache(10); - const doc1 = new Uint8Array([1, 2]); - const doc2 = new Uint8Array([3, 4]); - const doc3 = new Uint8Array([5, 6]); - const doc4 = new Uint8Array([7, 8]); + it("resizeCausesMultipleEvictions", async () => { + const cache = new FixedSizeDocumentCache(10); + const doc1 = new Uint8Array([1, 2]); + const doc2 = new Uint8Array([3, 4]); + const doc3 = new Uint8Array([5, 6]); + const doc4 = new Uint8Array([7, 8]); - cache.put(1, doc1); - cache.put(2, doc2); - cache.put(3, doc3); - cache.put(4, doc4); - // Cache has 8 bytes total + cache.put(1, doc1); + cache.put(2, doc2); + cache.put(3, doc3); + cache.put(4, doc4); + // Cache has 8 bytes total - cache.resize(2); + cache.resize(2); - // Should evict doc1, doc2, doc3 to get down to 2 bytes - assert.equal(cache.get(1), undefined); - assert.equal(cache.get(2), undefined); - assert.equal(cache.get(3), undefined); - assert.equal(cache.get(4), doc4); - }); + // Should evict doc1, doc2, doc3 to get down to 2 bytes + assert.equal(cache.get(1), undefined); + assert.equal(cache.get(2), undefined); + assert.equal(cache.get(3), undefined); + assert.equal(cache.get(4), doc4); + }); }); diff --git a/frontend/sync-client/src/utils/data-structures/fix-sized-cache.ts b/frontend/sync-client/src/utils/data-structures/fix-sized-cache.ts index 1541d72f..51ad41c1 100644 --- a/frontend/sync-client/src/utils/data-structures/fix-sized-cache.ts +++ b/frontend/sync-client/src/utils/data-structures/fix-sized-cache.ts @@ -4,116 +4,116 @@ import type { VaultUpdateId } from "../../persistence/database"; // Doubly-linked list node for O(1) LRU operations class LRUNode { - public constructor( - public key: VaultUpdateId, - public value: Uint8Array, - public prev: LRUNode | null = null, - public next: LRUNode | null = null - ) {} + public constructor( + public key: VaultUpdateId, + public value: Uint8Array, + public prev: LRUNode | null = null, + public next: LRUNode | null = null + ) {} } // evicting the least recently used documents when the size limit is exceeded. export class FixedSizeDocumentCache { - private currentSizeInBytes: number; - private readonly cache: Map; - private head: LRUNode | null; // Least recently used - private tail: LRUNode | null; // Most recently used + private currentSizeInBytes: number; + private readonly cache: Map; + private head: LRUNode | null; // Least recently used + private tail: LRUNode | null; // Most recently used - public constructor(private maxSizeInBytes: number) { - this.currentSizeInBytes = 0; - this.cache = new Map(); - this.head = null; - this.tail = null; - } + public constructor(private maxSizeInBytes: number) { + this.currentSizeInBytes = 0; + this.cache = new Map(); + this.head = null; + this.tail = null; + } - public get(updateId: VaultUpdateId): Uint8Array | undefined { - const node = this.cache.get(updateId); - if (node) { - this.moveToTail(node); - return node.value; - } + public get(updateId: VaultUpdateId): Uint8Array | undefined { + const node = this.cache.get(updateId); + if (node) { + this.moveToTail(node); + return node.value; + } - return undefined; - } + return undefined; + } - public put(updateId: VaultUpdateId, content: Uint8Array): void { - if (content.byteLength > this.maxSizeInBytes) { - // Document is too large to fit in the cache - return; - } + public put(updateId: VaultUpdateId, content: Uint8Array): void { + if (content.byteLength > this.maxSizeInBytes) { + // Document is too large to fit in the cache + return; + } - // If the document is already in the cache, update it - const existingNode = this.cache.get(updateId); - if (existingNode != null) { - this.currentSizeInBytes -= existingNode.value.byteLength; - this.removeNode(existingNode); - this.cache.delete(updateId); - } + // If the document is already in the cache, update it + const existingNode = this.cache.get(updateId); + if (existingNode != null) { + this.currentSizeInBytes -= existingNode.value.byteLength; + this.removeNode(existingNode); + this.cache.delete(updateId); + } - const newNode = new LRUNode(updateId, content); - this.cache.set(updateId, newNode); - this.addToTail(newNode); - this.currentSizeInBytes += content.byteLength; - this.fitBelowMaxSize(); - } + const newNode = new LRUNode(updateId, content); + this.cache.set(updateId, newNode); + this.addToTail(newNode); + this.currentSizeInBytes += content.byteLength; + this.fitBelowMaxSize(); + } - public reset(): void { - this.cache.clear(); - this.head = null; - this.tail = null; - this.currentSizeInBytes = 0; - } + public reset(): void { + this.cache.clear(); + this.head = null; + this.tail = null; + this.currentSizeInBytes = 0; + } - public resize(newMaxSizeInBytes: number): void { - this.maxSizeInBytes = newMaxSizeInBytes; - this.fitBelowMaxSize(); - } + public resize(newMaxSizeInBytes: number): void { + this.maxSizeInBytes = newMaxSizeInBytes; + this.fitBelowMaxSize(); + } - private fitBelowMaxSize(): void { - // Evict least recently used documents if over size limit - while (this.currentSizeInBytes > this.maxSizeInBytes && this.head) { - const lruNode = this.head; - this.removeNode(lruNode); - this.cache.delete(lruNode.key); - this.currentSizeInBytes -= lruNode.value.byteLength; - } - } + private fitBelowMaxSize(): void { + // Evict least recently used documents if over size limit + while (this.currentSizeInBytes > this.maxSizeInBytes && this.head) { + const lruNode = this.head; + this.removeNode(lruNode); + this.cache.delete(lruNode.key); + this.currentSizeInBytes -= lruNode.value.byteLength; + } + } - private removeNode(node: LRUNode): void { - if (node.prev) { - node.prev.next = node.next; - } else { - this.head = node.next; - } + private removeNode(node: LRUNode): void { + if (node.prev) { + node.prev.next = node.next; + } else { + this.head = node.next; + } - if (node.next) { - node.next.prev = node.prev; - } else { - this.tail = node.prev; - } + if (node.next) { + node.next.prev = node.prev; + } else { + this.tail = node.prev; + } - node.prev = null; - node.next = null; - } + node.prev = null; + node.next = null; + } - private addToTail(node: LRUNode): void { - node.prev = this.tail; - node.next = null; + private addToTail(node: LRUNode): void { + node.prev = this.tail; + node.next = null; - if (this.tail) { - this.tail.next = node; - } + if (this.tail) { + this.tail.next = node; + } - this.tail = node; + this.tail = node; - this.head ??= node; - } + this.head ??= node; + } - private moveToTail(node: LRUNode): void { - if (node === this.tail) { - return; - } - this.removeNode(node); - this.addToTail(node); - } + private moveToTail(node: LRUNode): void { + if (node === this.tail) { + return; + } + this.removeNode(node); + this.addToTail(node); + } } diff --git a/frontend/sync-client/src/utils/data-structures/locks.test.ts b/frontend/sync-client/src/utils/data-structures/locks.test.ts index a13bb274..0c09c062 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.test.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.test.ts @@ -7,226 +7,226 @@ import { awaitAll } from "../await-all"; import { sleep } from "../sleep"; describe("withLock", () => { - const testPath: RelativePath = "test/document/path"; - const testPath2: RelativePath = "test/document/path2"; - const logger = new Logger(); + const testPath: RelativePath = "test/document/path"; + const testPath2: RelativePath = "test/document/path2"; + const logger = new Logger(); - // eslint-disable-next-line @typescript-eslint/init-declarations - let locks: Locks; + // eslint-disable-next-line @typescript-eslint/init-declarations + let locks: Locks; - beforeEach(() => { - locks = new Locks(logger); - }); + beforeEach(() => { + locks = new Locks(logger); + }); - it("should execute function with single key lock", async () => { - let executionCount = 0; - const result = await locks.withLock(testPath, () => { - executionCount++; - return "success"; - }); + it("should execute function with single key lock", async () => { + let executionCount = 0; + const result = await locks.withLock(testPath, () => { + executionCount++; + return "success"; + }); - assert.strictEqual(result, "success"); - assert.strictEqual(executionCount, 1); - }); + assert.strictEqual(result, "success"); + assert.strictEqual(executionCount, 1); + }); - it("should execute async function with single key lock", async () => { - let executionCount = 0; - const result = await locks.withLock(testPath, async () => { - executionCount++; - await sleep(10); - return "async-success"; - }); + it("should execute async function with single key lock", async () => { + let executionCount = 0; + const result = await locks.withLock(testPath, async () => { + executionCount++; + await sleep(10); + return "async-success"; + }); - assert.strictEqual(result, "async-success"); - assert.strictEqual(executionCount, 1); - }); + assert.strictEqual(result, "async-success"); + assert.strictEqual(executionCount, 1); + }); - it("should execute function with multiple key locks", async () => { - let executionCount = 0; - const result = await locks.withLock([testPath, testPath2], () => { - executionCount++; - return "multi-success"; - }); + it("should execute function with multiple key locks", async () => { + let executionCount = 0; + const result = await locks.withLock([testPath, testPath2], () => { + executionCount++; + return "multi-success"; + }); - assert.strictEqual(result, "multi-success"); - assert.strictEqual(executionCount, 1); - }); + assert.strictEqual(result, "multi-success"); + assert.strictEqual(executionCount, 1); + }); - it("should sort multiple keys to prevent deadlocks", async () => { - const executionOrder: string[] = []; + it("should sort multiple keys to prevent deadlocks", async () => { + const executionOrder: string[] = []; - // Start two concurrent operations with keys in different orders - const promise1 = locks.withLock([testPath2, testPath], async () => { - executionOrder.push("operation1-start"); - await sleep(50); - executionOrder.push("operation1-end"); - return "result1"; - }); + // Start two concurrent operations with keys in different orders + const promise1 = locks.withLock([testPath2, testPath], async () => { + executionOrder.push("operation1-start"); + await sleep(50); + executionOrder.push("operation1-end"); + return "result1"; + }); - const promise2 = locks.withLock([testPath, testPath2], async () => { - executionOrder.push("operation2-start"); - await sleep(50); - executionOrder.push("operation2-end"); - return "result2"; - }); + const promise2 = locks.withLock([testPath, testPath2], async () => { + executionOrder.push("operation2-start"); + await sleep(50); + executionOrder.push("operation2-end"); + return "result2"; + }); - const [result1, result2] = await awaitAll([promise1, promise2]); + const [result1, result2] = await awaitAll([promise1, promise2]); - assert.strictEqual(result1, "result1"); - assert.strictEqual(result2, "result2"); - // One operation should complete entirely before the other starts - assert.deepStrictEqual(executionOrder, [ - "operation1-start", - "operation1-end", - "operation2-start", - "operation2-end" - ]); - }); + assert.strictEqual(result1, "result1"); + assert.strictEqual(result2, "result2"); + // One operation should complete entirely before the other starts + assert.deepStrictEqual(executionOrder, [ + "operation1-start", + "operation1-end", + "operation2-start", + "operation2-end" + ]); + }); - it("should serialize access to same key", async () => { - const executionOrder: string[] = []; + it("should serialize access to same key", async () => { + const executionOrder: string[] = []; - const promise1 = locks.withLock(testPath, async () => { - executionOrder.push("operation1-start"); - await sleep(50); - executionOrder.push("operation1-end"); - return "result1"; - }); + const promise1 = locks.withLock(testPath, async () => { + executionOrder.push("operation1-start"); + await sleep(50); + executionOrder.push("operation1-end"); + return "result1"; + }); - const promise2 = locks.withLock(testPath, async () => { - executionOrder.push("operation2-start"); - await sleep(30); - executionOrder.push("operation2-end"); - return "result2"; - }); + const promise2 = locks.withLock(testPath, async () => { + executionOrder.push("operation2-start"); + await sleep(30); + executionOrder.push("operation2-end"); + return "result2"; + }); - const [result1, result2] = await awaitAll([promise1, promise2]); + const [result1, result2] = await awaitAll([promise1, promise2]); - assert.strictEqual(result1, "result1"); - assert.strictEqual(result2, "result2"); - assert.deepStrictEqual(executionOrder, [ - "operation1-start", - "operation1-end", - "operation2-start", - "operation2-end" - ]); - }); + assert.strictEqual(result1, "result1"); + assert.strictEqual(result2, "result2"); + assert.deepStrictEqual(executionOrder, [ + "operation1-start", + "operation1-end", + "operation2-start", + "operation2-end" + ]); + }); - it("should allow concurrent access to different keys", async () => { - const executionOrder: string[] = []; + it("should allow concurrent access to different keys", async () => { + const executionOrder: string[] = []; - const promise1 = locks.withLock(testPath, async () => { - executionOrder.push("operation1-start"); - await sleep(50); + const promise1 = locks.withLock(testPath, async () => { + executionOrder.push("operation1-start"); + await sleep(50); - executionOrder.push("operation1-end"); - return "result1"; - }); + executionOrder.push("operation1-end"); + return "result1"; + }); - const promise2 = locks.withLock(testPath2, async () => { - executionOrder.push("operation2-start"); - await sleep(30); - executionOrder.push("operation2-end"); - return "result2"; - }); + const promise2 = locks.withLock(testPath2, async () => { + executionOrder.push("operation2-start"); + await sleep(30); + executionOrder.push("operation2-end"); + return "result2"; + }); - const [result1, result2] = await awaitAll([promise1, promise2]); + const [result1, result2] = await awaitAll([promise1, promise2]); - assert.strictEqual(result1, "result1"); - assert.strictEqual(result2, "result2"); - // Both operations should run concurrently - assert.strictEqual(executionOrder[0], "operation1-start"); - assert.strictEqual(executionOrder[1], "operation2-start"); - }); + assert.strictEqual(result1, "result1"); + assert.strictEqual(result2, "result2"); + // Both operations should run concurrently + assert.strictEqual(executionOrder[0], "operation1-start"); + assert.strictEqual(executionOrder[1], "operation2-start"); + }); - it("should release locks even if function throws", async () => { - const error = new Error("test error"); + it("should release locks even if function throws", async () => { + const error = new Error("test error"); - await assert.rejects( - locks.withLock(testPath, () => { - throw error; - }), - { message: "test error" } - ); + await assert.rejects( + locks.withLock(testPath, () => { + throw error; + }), + { message: "test error" } + ); - // Lock should be released, allowing another operation - const result = await locks.withLock( - testPath, - () => "success-after-error" - ); - assert.strictEqual(result, "success-after-error"); - }); + // Lock should be released, allowing another operation + const result = await locks.withLock( + testPath, + () => "success-after-error" + ); + assert.strictEqual(result, "success-after-error"); + }); - it("should release locks even if async function throws", async () => { - const error = new Error("async test error"); + it("should release locks even if async function throws", async () => { + const error = new Error("async test error"); - await assert.rejects( - locks.withLock(testPath, async () => { - await sleep(10); + await assert.rejects( + locks.withLock(testPath, async () => { + await sleep(10); - throw error; - }), - { message: "async test error" } - ); + throw error; + }), + { message: "async test error" } + ); - // Lock should be released, allowing another operation - const result = await locks.withLock( - testPath, - () => "success-after-async-error" - ); - assert.strictEqual(result, "success-after-async-error"); - }); + // Lock should be released, allowing another operation + const result = await locks.withLock( + testPath, + () => "success-after-async-error" + ); + assert.strictEqual(result, "success-after-async-error"); + }); - it("should handle empty array of keys", async () => { - const result = await locks.withLock([], () => "empty-keys"); - assert.strictEqual(result, "empty-keys"); - }); + it("should handle empty array of keys", async () => { + const result = await locks.withLock([], () => "empty-keys"); + assert.strictEqual(result, "empty-keys"); + }); - it("should maintain FIFO order for multiple waiters", async () => { - const executionOrder: string[] = []; + it("should maintain FIFO order for multiple waiters", async () => { + const executionOrder: string[] = []; - // Start first operation that holds the lock - const firstPromise = locks.withLock(testPath, async () => { - executionOrder.push("first-start"); - await sleep(100); - executionOrder.push("first-end"); - return "first"; - }); + // Start first operation that holds the lock + const firstPromise = locks.withLock(testPath, async () => { + executionOrder.push("first-start"); + await sleep(100); + executionOrder.push("first-end"); + return "first"; + }); - // Small delay to ensure first operation starts - await sleep(10); + // Small delay to ensure first operation starts + await sleep(10); - // Queue second and third operations - const secondPromise = locks.withLock(testPath, async () => { - executionOrder.push("second-start"); - await sleep(50); - executionOrder.push("second-end"); - return "second"; - }); + // Queue second and third operations + const secondPromise = locks.withLock(testPath, async () => { + executionOrder.push("second-start"); + await sleep(50); + executionOrder.push("second-end"); + return "second"; + }); - const thirdPromise = locks.withLock(testPath, async () => { - executionOrder.push("third-start"); - await sleep(20); - executionOrder.push("third-end"); - return "third"; - }); + const thirdPromise = locks.withLock(testPath, async () => { + executionOrder.push("third-start"); + await sleep(20); + executionOrder.push("third-end"); + return "third"; + }); - const [first, second, third] = await awaitAll([ - firstPromise, - secondPromise, - thirdPromise - ]); + const [first, second, third] = await awaitAll([ + firstPromise, + secondPromise, + thirdPromise + ]); - assert.strictEqual(first, "first"); - assert.strictEqual(second, "second"); - assert.strictEqual(third, "third"); - assert.deepStrictEqual(executionOrder, [ - "first-start", - "first-end", - "second-start", - "second-end", - "third-start", - "third-end" - ]); - }); + assert.strictEqual(first, "first"); + assert.strictEqual(second, "second"); + assert.strictEqual(third, "third"); + assert.deepStrictEqual(executionOrder, [ + "first-start", + "first-end", + "second-start", + "second-end", + "third-start", + "third-end" + ]); + }); }); diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index fccccf8c..8ad60429 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -8,148 +8,148 @@ import { awaitAll } from "../await-all"; * @template T The type of the key used for locking */ export class Locks { - /** Currently locked keys */ - private readonly locked = new Set(); + /** Currently locked keys */ + private readonly locked = new Set(); - /** Queue of resolve functions waiting for each key */ - private readonly waiters = new Map unknown)[]>(); + /** Queue of resolve functions waiting for each key */ + private readonly waiters = new Map unknown)[]>(); - public constructor(private readonly logger?: Logger) {} + public constructor(private readonly logger?: Logger) {} - /** - * Executes a function while holding exclusive locks on one or more keys. - * - * This method ensures that the provided function runs with exclusive access to the - * specified key(s). Multiple keys are sorted to prevent deadlocks when different - * operations request the same keys in different orders. - * - * @template R The return type of the function to execute - * @param keyOrKeys A single key or array of keys to lock during function execution - * @param fn The function to execute while holding the lock(s). Can be sync or async. - * @returns A Promise that resolves to the return value of the executed function - * - * @example - * ```typescript - * // Lock a single key - * const result = await locks.withLock('file1', () => { - * // Critical section - only one operation can access 'file1' at a time - * return processFile('file1'); - * }); - * - * // Lock multiple keys (prevents deadlocks through consistent ordering) - * await locks.withLock(['file1', 'file2'], async () => { - * // Critical section - exclusive access to both files - * await moveFile('file1', 'file2'); - * }); - * ``` - * - * @throws Any error thrown by the provided function will be propagated after locks are released - */ - public async withLock( - keyOrKeys: T | T[], - fn: () => R | Promise - ): Promise { - const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys]; + /** + * Executes a function while holding exclusive locks on one or more keys. + * + * This method ensures that the provided function runs with exclusive access to the + * specified key(s). Multiple keys are sorted to prevent deadlocks when different + * operations request the same keys in different orders. + * + * @template R The return type of the function to execute + * @param keyOrKeys A single key or array of keys to lock during function execution + * @param fn The function to execute while holding the lock(s). Can be sync or async. + * @returns A Promise that resolves to the return value of the executed function + * + * @example + * ```typescript + * // Lock a single key + * const result = await locks.withLock('file1', () => { + * // Critical section - only one operation can access 'file1' at a time + * return processFile('file1'); + * }); + * + * // Lock multiple keys (prevents deadlocks through consistent ordering) + * await locks.withLock(['file1', 'file2'], async () => { + * // Critical section - exclusive access to both files + * await moveFile('file1', 'file2'); + * }); + * ``` + * + * @throws Any error thrown by the provided function will be propagated after locks are released + */ + public async withLock( + keyOrKeys: T | T[], + fn: () => R | Promise + ): Promise { + const keys = Array.isArray(keyOrKeys) ? keyOrKeys : [keyOrKeys]; - // Deduplicate keys to prevent deadlock from acquiring same lock twice - const uniqueKeys = Array.from(new Set(keys)); - uniqueKeys.sort((a, b) => String(a).localeCompare(String(b))); // Ensure consistent order to prevent deadlocks + // Deduplicate keys to prevent deadlock from acquiring same lock twice + const uniqueKeys = Array.from(new Set(keys)); + uniqueKeys.sort((a, b) => String(a).localeCompare(String(b))); // Ensure consistent order to prevent deadlocks - await awaitAll(uniqueKeys.map(async (key) => this.waitForLock(key))); + await awaitAll(uniqueKeys.map(async (key) => this.waitForLock(key))); - try { - return await fn(); - } finally { - uniqueKeys.forEach((key) => { - this.unlock(key); - }); - } - } + try { + return await fn(); + } finally { + uniqueKeys.forEach((key) => { + this.unlock(key); + }); + } + } - public reset(): void { - this.locked.clear(); - this.waiters.clear(); - } + public reset(): void { + this.locked.clear(); + this.waiters.clear(); + } - /** - * Attempts to acquire a lock immediately without waiting. - * Must call `unlock()` if successful. - * - * @param key The key to lock - * @returns `true` if lock acquired, `false` if already locked - */ - public tryLock(key: T): boolean { - if (this.locked.has(key)) { - return false; - } + /** + * Attempts to acquire a lock immediately without waiting. + * Must call `unlock()` if successful. + * + * @param key The key to lock + * @returns `true` if lock acquired, `false` if already locked + */ + public tryLock(key: T): boolean { + if (this.locked.has(key)) { + return false; + } - this.locked.add(key); + this.locked.add(key); - return true; - } + return true; + } - /** - * Waits to acquire a lock, blocking until available. - * Operations are queued in FIFO order. Must call `unlock()` when done. - * - * @param key The key to wait for and lock - * @returns Promise that resolves when lock is acquired - */ - public async waitForLock(key: T): Promise { - if (this.tryLock(key)) { - return Promise.resolve(); - } + /** + * Waits to acquire a lock, blocking until available. + * Operations are queued in FIFO order. Must call `unlock()` when done. + * + * @param key The key to wait for and lock + * @returns Promise that resolves when lock is acquired + */ + public async waitForLock(key: T): Promise { + if (this.tryLock(key)) { + return Promise.resolve(); + } - this.logger?.debug(`Waiting for lock on ${key}`); + this.logger?.debug(`Waiting for lock on ${key}`); - return new Promise((resolve) => { - // DefaultDict behavior - let waiting = this.waiters.get(key); - if (!waiting) { - waiting = []; - this.waiters.set(key, waiting); - } + return new Promise((resolve) => { + // DefaultDict behavior + let waiting = this.waiters.get(key); + if (!waiting) { + waiting = []; + this.waiters.set(key, waiting); + } - waiting.push(resolve); - }); - } + waiting.push(resolve); + }); + } - /** - * Releases a lock and grants access to the next waiting operation in FIFO order. - * Removes the key from locked set if no waiters. - * - * @param key The key to unlock - * @throws {Error} If key is not currently locked - */ - public unlock(key: T): void { - if (!this.locked.has(key)) { - return; - } + /** + * Releases a lock and grants access to the next waiting operation in FIFO order. + * Removes the key from locked set if no waiters. + * + * @param key The key to unlock + * @throws {Error} If key is not currently locked + */ + public unlock(key: T): void { + if (!this.locked.has(key)) { + return; + } - // Remove first waiter to ensure FIFO order - const nextWaiting = this.waiters.get(key)?.shift(); + // Remove first waiter to ensure FIFO order + const nextWaiting = this.waiters.get(key)?.shift(); - if (nextWaiting) { - this.logger?.debug(`Granted lock on ${key}`); - nextWaiting(); - } else { - this.locked.delete(key); - } - } + if (nextWaiting) { + this.logger?.debug(`Granted lock on ${key}`); + nextWaiting(); + } else { + this.locked.delete(key); + } + } } export class Lock { - private readonly locks: Locks; + private readonly locks: Locks; - public constructor(logger?: Logger) { - this.locks = new Locks(logger); - } + public constructor(logger?: Logger) { + this.locks = new Locks(logger); + } - public async withLock(fn: () => R | Promise): Promise { - return this.locks.withLock(true, fn); - } + public async withLock(fn: () => R | Promise): Promise { + return this.locks.withLock(true, fn); + } - public reset(): void { - this.locks.reset(); - } + public reset(): void { + this.locks.reset(); + } } diff --git a/frontend/sync-client/src/utils/data-structures/min-covered.test.ts b/frontend/sync-client/src/utils/data-structures/min-covered.test.ts index 1bbd1425..7b7271d7 100644 --- a/frontend/sync-client/src/utils/data-structures/min-covered.test.ts +++ b/frontend/sync-client/src/utils/data-structures/min-covered.test.ts @@ -3,74 +3,74 @@ import assert from "node:assert"; import { CoveredValues } from "./min-covered"; describe("CoveredValues", () => { - it("should initialize with the given min value", () => { - const covered = new CoveredValues(5); - assert.strictEqual(covered.min, 5); - }); + it("should initialize with the given min value", () => { + const covered = new CoveredValues(5); + assert.strictEqual(covered.min, 5); + }); - it("should add values greater than min", () => { - const covered = new CoveredValues(0); - covered.add(3); - assert.strictEqual(covered.min, 0); - covered.add(1); - assert.strictEqual(covered.min, 1); - covered.add(4); - assert.strictEqual(covered.min, 1); - covered.add(2); - assert.strictEqual(covered.min, 4); - }); + it("should add values greater than min", () => { + const covered = new CoveredValues(0); + covered.add(3); + assert.strictEqual(covered.min, 0); + covered.add(1); + assert.strictEqual(covered.min, 1); + covered.add(4); + assert.strictEqual(covered.min, 1); + covered.add(2); + assert.strictEqual(covered.min, 4); + }); - it("should ignore duplicate values", () => { - const covered = new CoveredValues(0); - covered.add(3); - covered.add(3); - covered.add(3); - assert.strictEqual(covered.min, 0); - covered.add(1); - covered.add(2); - assert.strictEqual(covered.min, 3); - }); + it("should ignore duplicate values", () => { + const covered = new CoveredValues(0); + covered.add(3); + covered.add(3); + covered.add(3); + assert.strictEqual(covered.min, 0); + covered.add(1); + covered.add(2); + assert.strictEqual(covered.min, 3); + }); - it("should handle multiple consecutive values", () => { - const covered = new CoveredValues(132); - for (let i = 250; i > 132; i--) { - assert.strictEqual(covered.min, 132); - covered.add(i); - } - assert.strictEqual(covered.min, 250); - }); + it("should handle multiple consecutive values", () => { + const covered = new CoveredValues(132); + for (let i = 250; i > 132; i--) { + assert.strictEqual(covered.min, 132); + covered.add(i); + } + assert.strictEqual(covered.min, 250); + }); - it("should handle adding values lower than current min", () => { - const covered = new CoveredValues(5); - covered.add(3); - assert.strictEqual(covered.min, 5); - covered.add(6); - assert.strictEqual(covered.min, 6); - }); + it("should handle adding values lower than current min", () => { + const covered = new CoveredValues(5); + covered.add(3); + assert.strictEqual(covered.min, 5); + covered.add(6); + assert.strictEqual(covered.min, 6); + }); - it("should auto-advance when setting min value", () => { - const covered = new CoveredValues(5); - covered.add(7); - covered.add(8); - covered.add(9); - assert.strictEqual(covered.min, 5); - // Setting min to 6 should auto-advance through 7, 8, 9 - covered.min = 6; - assert.strictEqual(covered.min, 9); - covered.add(10); - assert.strictEqual(covered.min, 10); - }); + it("should auto-advance when setting min value", () => { + const covered = new CoveredValues(5); + covered.add(7); + covered.add(8); + covered.add(9); + assert.strictEqual(covered.min, 5); + // Setting min to 6 should auto-advance through 7, 8, 9 + covered.min = 6; + assert.strictEqual(covered.min, 9); + covered.add(10); + assert.strictEqual(covered.min, 10); + }); - it("should handle setting min value with no consecutive values", () => { - const covered = new CoveredValues(5); - covered.add(10); - covered.add(15); - assert.strictEqual(covered.min, 5); - // Setting min to 8 should not auto-advance (no consecutive values) - covered.min = 8; - assert.strictEqual(covered.min, 8); - // Add 9 to trigger auto-advance to 10 - covered.add(9); - assert.strictEqual(covered.min, 10); - }); + it("should handle setting min value with no consecutive values", () => { + const covered = new CoveredValues(5); + covered.add(10); + covered.add(15); + assert.strictEqual(covered.min, 5); + // Setting min to 8 should not auto-advance (no consecutive values) + covered.min = 8; + assert.strictEqual(covered.min, 8); + // Add 9 to trigger auto-advance to 10 + covered.add(9); + assert.strictEqual(covered.min, 10); + }); }); diff --git a/frontend/sync-client/src/utils/data-structures/min-covered.ts b/frontend/sync-client/src/utils/data-structures/min-covered.ts index be480597..8b38822f 100644 --- a/frontend/sync-client/src/utils/data-structures/min-covered.ts +++ b/frontend/sync-client/src/utils/data-structures/min-covered.ts @@ -14,48 +14,48 @@ * ``` */ export class CoveredValues { - private seenValues: number[] = []; + private seenValues: number[] = []; - public constructor(private minValue: number) {} + public constructor(private minValue: number) {} - public get min(): number { - return this.minValue; - } + public get min(): number { + return this.minValue; + } - public set min(value: number) { - this.minValue = Math.max(value, this.minValue); - this.seenValues = this.seenValues.filter((v) => v > this.minValue); - this.advanceMinWhilePossible(); - } + public set min(value: number) { + this.minValue = Math.max(value, this.minValue); + this.seenValues = this.seenValues.filter((v) => v > this.minValue); + this.advanceMinWhilePossible(); + } - public add(value: number | undefined): void { - if (value === undefined || value < this.minValue) { - return; - } + public add(value: number | undefined): void { + if (value === undefined || value < this.minValue) { + return; + } - let i = 0; - while (i < this.seenValues.length && this.seenValues[i] < value) { - i++; - } + let i = 0; + while (i < this.seenValues.length && this.seenValues[i] < value) { + i++; + } - if (i === this.seenValues.length) { - this.seenValues.push(value); - } else if (this.seenValues[i] === value) { - return; - } else { - this.seenValues.splice(i, 0, value); - } + if (i === this.seenValues.length) { + this.seenValues.push(value); + } else if (this.seenValues[i] === value) { + return; + } else { + this.seenValues.splice(i, 0, value); + } - this.advanceMinWhilePossible(); - } + this.advanceMinWhilePossible(); + } - private advanceMinWhilePossible(): void { - while ( - this.seenValues.length > 0 && - this.seenValues[0] === this.minValue + 1 - ) { - this.seenValues.shift(); - this.minValue++; - } - } + private advanceMinWhilePossible(): void { + while ( + this.seenValues.length > 0 && + this.seenValues[0] === this.minValue + 1 + ) { + this.seenValues.shift(); + this.minValue++; + } + } } diff --git a/frontend/sync-client/src/utils/debugging/log-to-console.ts b/frontend/sync-client/src/utils/debugging/log-to-console.ts index 3499f029..c47f18f6 100644 --- a/frontend/sync-client/src/utils/debugging/log-to-console.ts +++ b/frontend/sync-client/src/utils/debugging/log-to-console.ts @@ -3,22 +3,22 @@ import type { LogLine } from "../../tracing/logger"; import { LogLevel } from "../../tracing/logger"; export function logToConsole(client: SyncClient): void { - client.logger.onLogEmitted.add((logLine: LogLine) => { - const formatted = `${logLine.timestamp.toISOString()} ${logLine.level} ${logLine.message}`; + client.logger.onLogEmitted.add((logLine: LogLine) => { + const formatted = `${logLine.timestamp.toISOString()} ${logLine.level} ${logLine.message}`; - switch (logLine.level) { - case LogLevel.ERROR: - console.error(formatted); - break; - case LogLevel.WARNING: - console.warn(formatted); - break; - case LogLevel.INFO: - console.info(formatted); - break; - case LogLevel.DEBUG: - console.debug(formatted); - break; - } - }); + switch (logLine.level) { + case LogLevel.ERROR: + console.error(formatted); + break; + case LogLevel.WARNING: + console.warn(formatted); + break; + case LogLevel.INFO: + console.info(formatted); + break; + case LogLevel.DEBUG: + console.debug(formatted); + break; + } + }); } diff --git a/frontend/sync-client/src/utils/debugging/slow-fetch-factory.ts b/frontend/sync-client/src/utils/debugging/slow-fetch-factory.ts index 4c2ddedb..e2908af0 100644 --- a/frontend/sync-client/src/utils/debugging/slow-fetch-factory.ts +++ b/frontend/sync-client/src/utils/debugging/slow-fetch-factory.ts @@ -1,20 +1,20 @@ import { sleep } from "../sleep"; export const slowFetchFactory = - (jitterScaleInSeconds: number) => - async ( - input: string | URL | globalThis.Request, - init?: RequestInit - ): Promise => { - if (jitterScaleInSeconds > 0) { - await sleep(((Math.random() * jitterScaleInSeconds) / 2) * 1000); - } + (jitterScaleInSeconds: number) => + async ( + input: string | URL | globalThis.Request, + init?: RequestInit + ): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(((Math.random() * jitterScaleInSeconds) / 2) * 1000); + } - const response = await fetch(input, init); + const response = await fetch(input, init); - if (jitterScaleInSeconds > 0) { - await sleep(((Math.random() * jitterScaleInSeconds) / 2) * 1000); - } + if (jitterScaleInSeconds > 0) { + await sleep(((Math.random() * jitterScaleInSeconds) / 2) * 1000); + } - return response; - }; + return response; + }; diff --git a/frontend/sync-client/src/utils/debugging/slow-web-socket-factory.ts b/frontend/sync-client/src/utils/debugging/slow-web-socket-factory.ts index e52ff76b..c64bff18 100644 --- a/frontend/sync-client/src/utils/debugging/slow-web-socket-factory.ts +++ b/frontend/sync-client/src/utils/debugging/slow-web-socket-factory.ts @@ -3,79 +3,79 @@ import { Locks } from "../data-structures/locks"; import type { Logger } from "../../tracing/logger"; export function slowWebSocketFactory( - jitterScaleInSeconds: number, - logger: Logger + jitterScaleInSeconds: number, + logger: Logger ): typeof WebSocket { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - return class FlakyWebSocket extends WebSocket { - private static readonly RECEIVE_KEY = "websocket-receive"; - private static readonly SEND_KEY = "websocket-send"; + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + return class FlakyWebSocket extends WebSocket { + private static readonly RECEIVE_KEY = "websocket-receive"; + private static readonly SEND_KEY = "websocket-send"; - private readonly locks = new Locks(logger); + private readonly locks = new Locks(logger); - public set onopen(callback: ((event: Event) => void) | null) { - super.onopen = async (event: Event): Promise => { - if (jitterScaleInSeconds > 0) { - await sleep(Math.random() * jitterScaleInSeconds * 1000); - } + public set onopen(callback: ((event: Event) => void) | null) { + super.onopen = async (event: Event): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } - callback?.(event); - }; - } + callback?.(event); + }; + } - public set onmessage(callback: ((event: MessageEvent) => void) | null) { - super.onmessage = async (event: MessageEvent): Promise => { - await this.locks.withLock( - FlakyWebSocket.RECEIVE_KEY, - async () => { - if (jitterScaleInSeconds > 0) { - await sleep( - Math.random() * jitterScaleInSeconds * 1000 - ); - } + public set onmessage(callback: ((event: MessageEvent) => void) | null) { + super.onmessage = async (event: MessageEvent): Promise => { + await this.locks.withLock( + FlakyWebSocket.RECEIVE_KEY, + async () => { + if (jitterScaleInSeconds > 0) { + await sleep( + Math.random() * jitterScaleInSeconds * 1000 + ); + } - callback?.(event); - } - ); - }; - } + callback?.(event); + } + ); + }; + } - public set onclose(callback: ((event: CloseEvent) => void) | null) { - super.onclose = async (event: CloseEvent): Promise => { - if (jitterScaleInSeconds > 0) { - await sleep(Math.random() * jitterScaleInSeconds * 1000); - } - callback?.(event); - }; - } + public set onclose(callback: ((event: CloseEvent) => void) | null) { + super.onclose = async (event: CloseEvent): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + callback?.(event); + }; + } - public set onerror(callback: ((event: Event) => void) | null) { - super.onerror = async (event: Event): Promise => { - if (jitterScaleInSeconds > 0) { - await sleep(Math.random() * jitterScaleInSeconds * 1000); - } - callback?.(event); - }; - } + public set onerror(callback: ((event: Event) => void) | null) { + super.onerror = async (event: Event): Promise => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + callback?.(event); + }; + } - public send( - data: string | ArrayBufferLike | Blob | ArrayBufferView - ): void { - this.waitingSend(data).catch((error: unknown) => { - logger.error(`Error sending WebSocket message: ${error}`); - }); - } + public send( + data: string | ArrayBufferLike | Blob | ArrayBufferView + ): void { + this.waitingSend(data).catch((error: unknown) => { + logger.error(`Error sending WebSocket message: ${error}`); + }); + } - private async waitingSend( - data: string | ArrayBufferLike | Blob | ArrayBufferView - ): Promise { - // maintain message order - await this.locks.withLock(FlakyWebSocket.SEND_KEY, async () => { - if (jitterScaleInSeconds > 0) { - await sleep(Math.random() * jitterScaleInSeconds * 1000); - } - super.send(data); - }); - } - } as unknown as typeof WebSocket; + private async waitingSend( + data: string | ArrayBufferLike | Blob | ArrayBufferView + ): Promise { + // maintain message order + await this.locks.withLock(FlakyWebSocket.SEND_KEY, async () => { + if (jitterScaleInSeconds > 0) { + await sleep(Math.random() * jitterScaleInSeconds * 1000); + } + super.send(data); + }); + } + } as unknown as typeof WebSocket; } diff --git a/frontend/sync-client/src/utils/find-matching-file.ts b/frontend/sync-client/src/utils/find-matching-file.ts index 10545f2c..c3d323d3 100644 --- a/frontend/sync-client/src/utils/find-matching-file.ts +++ b/frontend/sync-client/src/utils/find-matching-file.ts @@ -3,12 +3,12 @@ import { EMPTY_HASH } from "./hash"; // TODO: make this smarter so that offline files can be renamed & edited at the same time export function findMatchingFile( - contentHash: string, - candidates: DocumentRecord[] + contentHash: string, + candidates: DocumentRecord[] ): DocumentRecord | undefined { - if (contentHash === EMPTY_HASH) { - return undefined; - } + if (contentHash === EMPTY_HASH) { + return undefined; + } - return candidates.find(({ metadata }) => metadata?.hash === contentHash); + return candidates.find(({ metadata }) => metadata?.hash === contentHash); } diff --git a/frontend/sync-client/src/utils/get-random-color.ts b/frontend/sync-client/src/utils/get-random-color.ts index 543b943e..38015734 100644 --- a/frontend/sync-client/src/utils/get-random-color.ts +++ b/frontend/sync-client/src/utils/get-random-color.ts @@ -1,9 +1,9 @@ export function getRandomColor(name: string): string { - let hash = 0; - for (let i = 0; i < name.length; i++) { - hash = (hash << 5) - hash + name.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - const normalised = hash / 0x7fffffff; - return `oklch(0.58 0.15 ${Math.round(Math.abs(normalised * 360))})`; + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = (hash << 5) - hash + name.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + const normalised = hash / 0x7fffffff; + return `oklch(0.58 0.15 ${Math.round(Math.abs(normalised * 360))})`; } diff --git a/frontend/sync-client/src/utils/globs-to-regexes.test.ts b/frontend/sync-client/src/utils/globs-to-regexes.test.ts index 3e986ca4..f2b11787 100644 --- a/frontend/sync-client/src/utils/globs-to-regexes.test.ts +++ b/frontend/sync-client/src/utils/globs-to-regexes.test.ts @@ -4,10 +4,10 @@ import { Logger } from "../tracing/logger"; import { globsToRegexes } from "./globs-to-regexes"; describe("globsToRegexes", () => { - it("basicExample", async () => { - const [regex] = globsToRegexes([".git/**"], new Logger()); + it("basicExample", async () => { + const [regex] = globsToRegexes([".git/**"], new Logger()); - assert.ok(regex.test(".git/objects/object")); - assert.ok(regex.test(".git/objects/.object")); - }); + assert.ok(regex.test(".git/objects/object")); + assert.ok(regex.test(".git/objects/.object")); + }); }); diff --git a/frontend/sync-client/src/utils/globs-to-regexes.ts b/frontend/sync-client/src/utils/globs-to-regexes.ts index 5b8bf062..1cd048d3 100644 --- a/frontend/sync-client/src/utils/globs-to-regexes.ts +++ b/frontend/sync-client/src/utils/globs-to-regexes.ts @@ -2,20 +2,20 @@ import { makeRe } from "minimatch"; import type { Logger } from "../tracing/logger"; export function globsToRegexes(globs: string[], logger: Logger): RegExp[] { - return ( - globs - .map((pattern) => { - const result = makeRe(pattern, { - dot: true - }); - if (result === false) { - logger.warn( - `Failed to parse ${pattern}' as a glob pattern, skipping it` - ); - } - return result; - }) - // eslint-disable-next-line no-restricted-syntax -- Filtering out false values, not removing a specific item - .filter((pattern) => pattern !== false) - ); + return ( + globs + .map((pattern) => { + const result = makeRe(pattern, { + dot: true + }); + if (result === false) { + logger.warn( + `Failed to parse ${pattern}' as a glob pattern, skipping it` + ); + } + return result; + }) + // eslint-disable-next-line no-restricted-syntax -- Filtering out false values, not removing a specific item + .filter((pattern) => pattern !== false) + ); } diff --git a/frontend/sync-client/src/utils/hash.ts b/frontend/sync-client/src/utils/hash.ts index cd965db5..906b6fad 100644 --- a/frontend/sync-client/src/utils/hash.ts +++ b/frontend/sync-client/src/utils/hash.ts @@ -1,12 +1,12 @@ // https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript export function hash(content: Uint8Array): string { - let result = 0; - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let i = 0; i < content.length; i++) { - result = (result << 5) - result + content[i]; - result |= 0; // Convert to 32bit integer - } - return Math.abs(result).toString(16).padStart(8, "0"); + let result = 0; + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (let i = 0; i < content.length; i++) { + result = (result << 5) - result + content[i]; + result |= 0; // Convert to 32bit integer + } + return Math.abs(result).toString(16).padStart(8, "0"); } export const EMPTY_HASH = hash(new Uint8Array(0)); diff --git a/frontend/sync-client/src/utils/is-binary.ts b/frontend/sync-client/src/utils/is-binary.ts index 9e2de954..aac92711 100644 --- a/frontend/sync-client/src/utils/is-binary.ts +++ b/frontend/sync-client/src/utils/is-binary.ts @@ -1,16 +1,16 @@ // Text is unlikely to contain null bytes, so we can use that to distinguish binary files. export function isBinary(content: Uint8Array): boolean { - for (const byte of content) { - if (byte === 0) { - return true; - } - } + for (const byte of content) { + if (byte === 0) { + return true; + } + } - try { - new TextDecoder("utf-8", { fatal: true }).decode(content); - } catch { - return true; - } + try { + new TextDecoder("utf-8", { fatal: true }).decode(content); + } catch { + return true; + } - return false; + return false; } diff --git a/frontend/sync-client/src/utils/is-file-type-mergable.test.ts b/frontend/sync-client/src/utils/is-file-type-mergable.test.ts index a2268d19..fd316588 100644 --- a/frontend/sync-client/src/utils/is-file-type-mergable.test.ts +++ b/frontend/sync-client/src/utils/is-file-type-mergable.test.ts @@ -4,70 +4,70 @@ import { isFileTypeMergable } from "./is-file-type-mergable"; const mergableExtensions = ["md", "txt"]; describe("isFileTypeMergable", () => { - it("should return true for .md files", () => { - assert.strictEqual(isFileTypeMergable(".md", mergableExtensions), true); - assert.strictEqual( - isFileTypeMergable("hi.md", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable("my/path/to/my/document.md", mergableExtensions), - true - ); - }); + it("should return true for .md files", () => { + assert.strictEqual(isFileTypeMergable(".md", mergableExtensions), true); + assert.strictEqual( + isFileTypeMergable("hi.md", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable("my/path/to/my/document.md", mergableExtensions), + true + ); + }); - it("should return true for .txt files", () => { - assert.strictEqual( - isFileTypeMergable(".txt", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable("hi.txt", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable( - "my/path/to/my/document.txt", - mergableExtensions - ), - true - ); - }); + it("should return true for .txt files", () => { + assert.strictEqual( + isFileTypeMergable(".txt", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable("hi.txt", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable( + "my/path/to/my/document.txt", + mergableExtensions + ), + true + ); + }); - it("should be case insensitive", () => { - assert.strictEqual( - isFileTypeMergable("hi.MD", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable("my/path/to/my/DOCUMENT.MD", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable("hi.TXT", mergableExtensions), - true - ); - assert.strictEqual( - isFileTypeMergable( - "my/path/to/my/DOCUMENT.TXT", - mergableExtensions - ), - true - ); - }); + it("should be case insensitive", () => { + assert.strictEqual( + isFileTypeMergable("hi.MD", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable("my/path/to/my/DOCUMENT.MD", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable("hi.TXT", mergableExtensions), + true + ); + assert.strictEqual( + isFileTypeMergable( + "my/path/to/my/DOCUMENT.TXT", + mergableExtensions + ), + true + ); + }); - it("should return false for non-mergable file types", () => { - assert.strictEqual( - isFileTypeMergable(".json", mergableExtensions), - false - ); - assert.strictEqual( - isFileTypeMergable("HELLO.JSON", mergableExtensions), - false - ); - assert.strictEqual( - isFileTypeMergable("my/config.yml", mergableExtensions), - false - ); - }); + it("should return false for non-mergable file types", () => { + assert.strictEqual( + isFileTypeMergable(".json", mergableExtensions), + false + ); + assert.strictEqual( + isFileTypeMergable("HELLO.JSON", mergableExtensions), + false + ); + assert.strictEqual( + isFileTypeMergable("my/config.yml", mergableExtensions), + false + ); + }); }); diff --git a/frontend/sync-client/src/utils/is-file-type-mergable.ts b/frontend/sync-client/src/utils/is-file-type-mergable.ts index 4eec2733..a895b3e2 100644 --- a/frontend/sync-client/src/utils/is-file-type-mergable.ts +++ b/frontend/sync-client/src/utils/is-file-type-mergable.ts @@ -1,9 +1,9 @@ export function isFileTypeMergable( - pathOrFileName: string, - mergeableExtensions: string[] + pathOrFileName: string, + mergeableExtensions: string[] ): boolean { - const parts = pathOrFileName.split("."); - const fileExtension = parts.at(-1) ?? ""; + const parts = pathOrFileName.split("."); + const fileExtension = parts.at(-1) ?? ""; - return mergeableExtensions.includes(fileExtension.toLowerCase()); + return mergeableExtensions.includes(fileExtension.toLowerCase()); } diff --git a/frontend/sync-client/src/utils/line-and-column-to-position.test.ts b/frontend/sync-client/src/utils/line-and-column-to-position.test.ts index 82d752c9..e597cc39 100644 --- a/frontend/sync-client/src/utils/line-and-column-to-position.test.ts +++ b/frontend/sync-client/src/utils/line-and-column-to-position.test.ts @@ -3,42 +3,42 @@ import assert from "node:assert"; import { lineAndColumnToPosition } from "./line-and-column-to-position"; describe("lineAndColumnToPosition", () => { - it("should return the correct position for the first line", () => { - const text = "Hello\nWorld"; - const position = lineAndColumnToPosition(text, 0, 3); - assert.strictEqual(position, 3); - }); + it("should return the correct position for the first line", () => { + const text = "Hello\nWorld"; + const position = lineAndColumnToPosition(text, 0, 3); + assert.strictEqual(position, 3); + }); - it("should return the correct position for the second line", () => { - const text = "Hello\nWorld"; - const position = lineAndColumnToPosition(text, 1, 2); - assert.strictEqual(position, 8); - }); + it("should return the correct position for the second line", () => { + const text = "Hello\nWorld"; + const position = lineAndColumnToPosition(text, 1, 2); + assert.strictEqual(position, 8); + }); - it("should return the correct position for an empty string", () => { - const text = ""; - const position = lineAndColumnToPosition(text, 0, 0); - assert.strictEqual(position, 0); - }); + it("should return the correct position for an empty string", () => { + const text = ""; + const position = lineAndColumnToPosition(text, 0, 0); + assert.strictEqual(position, 0); + }); - it("with carrige return", () => { - assert.strictEqual(lineAndColumnToPosition("a\nb", 1, 1), 3); - assert.strictEqual(lineAndColumnToPosition("a\r\nb", 1, 1), 3); - }); + it("with carrige return", () => { + assert.strictEqual(lineAndColumnToPosition("a\nb", 1, 1), 3); + assert.strictEqual(lineAndColumnToPosition("a\r\nb", 1, 1), 3); + }); - it("should handle multi-line strings with varying lengths", () => { - const text = "Line1\nLongerLine2\nShort3"; - const position = lineAndColumnToPosition(text, 2, 4); - assert.strictEqual(position, 22); - }); + it("should handle multi-line strings with varying lengths", () => { + const text = "Line1\nLongerLine2\nShort3"; + const position = lineAndColumnToPosition(text, 2, 4); + assert.strictEqual(position, 22); + }); - it("should throw an error if the line number is out of range", () => { - const text = "Line1\nLine2"; - assert.throws(() => lineAndColumnToPosition(text, 3, 0)); - }); + it("should throw an error if the line number is out of range", () => { + const text = "Line1\nLine2"; + assert.throws(() => lineAndColumnToPosition(text, 3, 0)); + }); - it("should throw an error if the column number is out of range", () => { - const text = "Line1\nLine2"; - assert.throws(() => lineAndColumnToPosition(text, 1, 10)); - }); + it("should throw an error if the column number is out of range", () => { + const text = "Line1\nLine2"; + assert.throws(() => lineAndColumnToPosition(text, 1, 10)); + }); }); diff --git a/frontend/sync-client/src/utils/line-and-column-to-position.ts b/frontend/sync-client/src/utils/line-and-column-to-position.ts index 2ee6b2a4..05ac7be8 100644 --- a/frontend/sync-client/src/utils/line-and-column-to-position.ts +++ b/frontend/sync-client/src/utils/line-and-column-to-position.ts @@ -9,26 +9,26 @@ * @throws Error if column number is out of range */ export function lineAndColumnToPosition( - text: string, - line: number, - column: number + text: string, + line: number, + column: number ): number { - const lines = text.replaceAll("\r", "").split("\n"); + const lines = text.replaceAll("\r", "").split("\n"); - if (line >= lines.length) { - throw new Error(`Line number ${line} is out of range.`); - } + if (line >= lines.length) { + throw new Error(`Line number ${line} is out of range.`); + } - if (column > lines[line].length) { - throw new Error(`Column number ${column} is out of range.`); - } + if (column > lines[line].length) { + throw new Error(`Column number ${column} is out of range.`); + } - let position = 0; - for (let i = 0; i < line; i++) { - position += lines[i].length + 1; - } + let position = 0; + for (let i = 0; i < line; i++) { + position += lines[i].length + 1; + } - position += column; + position += column; - return position; + return position; } diff --git a/frontend/sync-client/src/utils/position-to-line-and-column.test.ts b/frontend/sync-client/src/utils/position-to-line-and-column.test.ts index 2341b7c5..2797bd8e 100644 --- a/frontend/sync-client/src/utils/position-to-line-and-column.test.ts +++ b/frontend/sync-client/src/utils/position-to-line-and-column.test.ts @@ -3,86 +3,86 @@ import assert from "node:assert"; import { positionToLineAndColumn } from "./position-to-line-and-column"; describe("positionToLineAndColumn", () => { - test("converts position to line and column in multi-line text", () => { - const text = "ab\ncd\n"; - assert.deepStrictEqual(positionToLineAndColumn(text, 0), { - line: 0, - column: 0 - }); - assert.deepStrictEqual(positionToLineAndColumn(text, 1), { - line: 0, - column: 1 - }); - assert.deepStrictEqual(positionToLineAndColumn(text, 2), { - line: 0, - column: 2 - }); - assert.deepStrictEqual(positionToLineAndColumn(text, 3), { - line: 1, - column: 0 - }); - assert.deepStrictEqual(positionToLineAndColumn(text, 4), { - line: 1, - column: 1 - }); - assert.deepStrictEqual(positionToLineAndColumn(text, 6), { - line: 2, - column: 0 - }); - }); + test("converts position to line and column in multi-line text", () => { + const text = "ab\ncd\n"; + assert.deepStrictEqual(positionToLineAndColumn(text, 0), { + line: 0, + column: 0 + }); + assert.deepStrictEqual(positionToLineAndColumn(text, 1), { + line: 0, + column: 1 + }); + assert.deepStrictEqual(positionToLineAndColumn(text, 2), { + line: 0, + column: 2 + }); + assert.deepStrictEqual(positionToLineAndColumn(text, 3), { + line: 1, + column: 0 + }); + assert.deepStrictEqual(positionToLineAndColumn(text, 4), { + line: 1, + column: 1 + }); + assert.deepStrictEqual(positionToLineAndColumn(text, 6), { + line: 2, + column: 0 + }); + }); - test("with carrige returns", () => { - assert.deepStrictEqual(positionToLineAndColumn("a\nb", 3), { - line: 1, - column: 1 - }); + test("with carrige returns", () => { + assert.deepStrictEqual(positionToLineAndColumn("a\nb", 3), { + line: 1, + column: 1 + }); - assert.deepStrictEqual(positionToLineAndColumn("a\r\nb", 3), { - line: 1, - column: 1 - }); - }); + assert.deepStrictEqual(positionToLineAndColumn("a\r\nb", 3), { + line: 1, + column: 1 + }); + }); - test("with multiple carriage returns", () => { - // Test that all \r characters are removed, not just the first one - const text = "line1\r\nline2\r\nline3\r\n"; + test("with multiple carriage returns", () => { + // Test that all \r characters are removed, not just the first one + const text = "line1\r\nline2\r\nline3\r\n"; - assert.deepStrictEqual(positionToLineAndColumn(text, 0), { - line: 0, - column: 0 - }); + assert.deepStrictEqual(positionToLineAndColumn(text, 0), { + line: 0, + column: 0 + }); - // Position 6 = start of 'line2' after all \r removed - assert.deepStrictEqual(positionToLineAndColumn(text, 6), { - line: 1, - column: 0 - }); + // Position 6 = start of 'line2' after all \r removed + assert.deepStrictEqual(positionToLineAndColumn(text, 6), { + line: 1, + column: 0 + }); - // Position 12 = start of 'line3' after all \r removed - assert.deepStrictEqual(positionToLineAndColumn(text, 12), { - line: 2, - column: 0 - }); - }); + // Position 12 = start of 'line3' after all \r removed + assert.deepStrictEqual(positionToLineAndColumn(text, 12), { + line: 2, + column: 0 + }); + }); - test("handles empty input", () => { - assert.deepStrictEqual(positionToLineAndColumn("", 0), { - line: 0, - column: 0 - }); - }); + test("handles empty input", () => { + assert.deepStrictEqual(positionToLineAndColumn("", 0), { + line: 0, + column: 0 + }); + }); - test("handles positions at the end of text", () => { - const text = "End"; - assert.deepStrictEqual(positionToLineAndColumn(text, 3), { - line: 0, - column: 3 - }); - }); + test("handles positions at the end of text", () => { + const text = "End"; + assert.deepStrictEqual(positionToLineAndColumn(text, 3), { + line: 0, + column: 3 + }); + }); - test("throws error for position out of range", () => { - const text = "Short text"; - assert.throws(() => positionToLineAndColumn(text, 15)); - assert.throws(() => positionToLineAndColumn(text, -1)); - }); + test("throws error for position out of range", () => { + const text = "Short text"; + assert.throws(() => positionToLineAndColumn(text, 15)); + assert.throws(() => positionToLineAndColumn(text, -1)); + }); }); diff --git a/frontend/sync-client/src/utils/position-to-line-and-column.ts b/frontend/sync-client/src/utils/position-to-line-and-column.ts index 116b9f15..969171d8 100644 --- a/frontend/sync-client/src/utils/position-to-line-and-column.ts +++ b/frontend/sync-client/src/utils/position-to-line-and-column.ts @@ -7,27 +7,27 @@ * @throws Will throw an error if the position is negative or exceeds the text length */ export function positionToLineAndColumn( - text: string, - position: number + text: string, + position: number ): { line: number; column: number } { - if (position < 0) { - throw new Error("Position cannot be negative"); - } + if (position < 0) { + throw new Error("Position cannot be negative"); + } - text = text.replaceAll("\r", ""); + text = text.replaceAll("\r", ""); - if (position > text.length) { - // position == text.length accounts for the cursor being after last character - throw new Error( - `Position ${position} exceeds text length ${text.length}` - ); - } + if (position > text.length) { + // position == text.length accounts for the cursor being after last character + throw new Error( + `Position ${position} exceeds text length ${text.length}` + ); + } - const textUpToPosition = text.substring(0, position); - const lines = textUpToPosition.split("\n"); + const textUpToPosition = text.substring(0, position); + const lines = textUpToPosition.split("\n"); - const line = lines.length - 1; - const column = lines[lines.length - 1].length; + const line = lines.length - 1; + const column = lines[lines.length - 1].length; - return { line, column }; + return { line, column }; } diff --git a/frontend/sync-client/src/utils/rate-limit.test.ts b/frontend/sync-client/src/utils/rate-limit.test.ts index e0b77dc4..6c7fb434 100644 --- a/frontend/sync-client/src/utils/rate-limit.test.ts +++ b/frontend/sync-client/src/utils/rate-limit.test.ts @@ -3,62 +3,62 @@ import { describe, it, beforeEach, afterEach, mock } from "node:test"; import assert from "node:assert"; describe("rateLimit", () => { - beforeEach(() => { - mock.timers.enable({ apis: ["setTimeout"] }); - }); + beforeEach(() => { + mock.timers.enable({ apis: ["setTimeout"] }); + }); - afterEach(() => { - mock.timers.reset(); - }); + afterEach(() => { + mock.timers.reset(); + }); - it("should call the function immediately on first invocation", async () => { - const mockFn = mock.fn<() => Promise>(); - mockFn.mock.mockImplementation(async () => "result"); - const rateLimited = rateLimit(mockFn, 100); + it("should call the function immediately on first invocation", async () => { + const mockFn = mock.fn<() => Promise>(); + mockFn.mock.mockImplementation(async () => "result"); + const rateLimited = rateLimit(mockFn, 100); - const promise = rateLimited(); - assert.strictEqual(mockFn.mock.callCount(), 1); + const promise = rateLimited(); + assert.strictEqual(mockFn.mock.callCount(), 1); - await promise; - }); + await promise; + }); - it("should call the function again after the interval has passed", async () => { - const mockFn = mock.fn<(value: number) => Promise>(); - mockFn.mock.mockImplementation(async () => "result"); + it("should call the function again after the interval has passed", async () => { + const mockFn = mock.fn<(value: number) => Promise>(); + mockFn.mock.mockImplementation(async () => "result"); - const rateLimited = rateLimit(mockFn, 100); + const rateLimited = rateLimit(mockFn, 100); - const promise1 = rateLimited(1); - await promise1; + const promise1 = rateLimited(1); + await promise1; - mock.timers.tick(200); + mock.timers.tick(200); - const promise2 = rateLimited(2); - await promise2; + const promise2 = rateLimited(2); + await promise2; - assert.strictEqual(mockFn.mock.callCount(), 2); - assert.deepStrictEqual(mockFn.mock.calls[1].arguments, [2]); - }); + assert.strictEqual(mockFn.mock.callCount(), 2); + assert.deepStrictEqual(mockFn.mock.calls[1].arguments, [2]); + }); - it("should use the most recent arguments if multiple calls are made within interval", async () => { - const mockFn = mock.fn<(value: string) => Promise>(); - mockFn.mock.mockImplementation(async (val: string) => `${val}-result`); - const rateLimited = rateLimit(mockFn, 100); + it("should use the most recent arguments if multiple calls are made within interval", async () => { + const mockFn = mock.fn<(value: string) => Promise>(); + mockFn.mock.mockImplementation(async (val: string) => `${val}-result`); + const rateLimited = rateLimit(mockFn, 100); - const promise1 = rateLimited("first"); - mock.timers.tick(10); - const promise2 = rateLimited("second"); - mock.timers.tick(10); - const promise3 = rateLimited("third"); + const promise1 = rateLimited("first"); + mock.timers.tick(10); + const promise2 = rateLimited("second"); + mock.timers.tick(10); + const promise3 = rateLimited("third"); - mock.timers.tick(1000); + mock.timers.tick(1000); - assert.strictEqual(await promise1, "first-result"); - assert.strictEqual(await promise2, "third-result"); - assert.strictEqual(await promise3, undefined); + assert.strictEqual(await promise1, "first-result"); + assert.strictEqual(await promise2, "third-result"); + assert.strictEqual(await promise3, undefined); - assert.strictEqual(mockFn.mock.callCount(), 2); - assert.deepStrictEqual(mockFn.mock.calls[0].arguments, ["first"]); - assert.deepStrictEqual(mockFn.mock.calls[1].arguments, ["third"]); - }); + assert.strictEqual(mockFn.mock.callCount(), 2); + assert.deepStrictEqual(mockFn.mock.calls[0].arguments, ["first"]); + assert.deepStrictEqual(mockFn.mock.calls[1].arguments, ["third"]); + }); }); diff --git a/frontend/sync-client/src/utils/rate-limit.ts b/frontend/sync-client/src/utils/rate-limit.ts index 2c6d018b..52cbbce7 100644 --- a/frontend/sync-client/src/utils/rate-limit.ts +++ b/frontend/sync-client/src/utils/rate-limit.ts @@ -16,48 +16,48 @@ import { sleep } from "./sleep"; * Returns the original function's return type when executed, or undefined if the call was superseded by a newer one. */ export function rateLimit< - R, - T extends ( - ...args: any // eslint-disable-line @typescript-eslint/no-explicit-any - ) => Promise + R, + T extends ( + ...args: any // eslint-disable-line @typescript-eslint/no-explicit-any + ) => Promise >( - fn: T, - minIntervalMs: number | (() => number) + fn: T, + minIntervalMs: number | (() => number) ): (...args: Parameters) => Promise { - let newArgs: Parameters | undefined = undefined; - let running: Promise | undefined = undefined; + let newArgs: Parameters | undefined = undefined; + let running: Promise | undefined = undefined; - const decoratedFn = async ( - ...args: Parameters - ): Promise => { - if (running !== undefined) { - newArgs = args; - await running; + const decoratedFn = async ( + ...args: Parameters + ): Promise => { + if (running !== undefined) { + newArgs = args; + await running; - // args might have changed while we were waiting - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (newArgs === undefined) { - // we weren't the first one to wake up, that means a newer - // invocation is running now, we can just bail - return; - } - args = newArgs; - newArgs = undefined; - } + // args might have changed while we were waiting + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (newArgs === undefined) { + // we weren't the first one to wake up, that means a newer + // invocation is running now, we can just bail + return; + } + args = newArgs; + newArgs = undefined; + } - const [promise, resolve] = createPromise(); - running = promise; - sleep( - typeof minIntervalMs === "function" - ? minIntervalMs() - : minIntervalMs - ) - .then(resolve) - .catch(() => { - // sleep cannot fail - }); - return fn(...args); - }; + const [promise, resolve] = createPromise(); + running = promise; + sleep( + typeof minIntervalMs === "function" + ? minIntervalMs() + : minIntervalMs + ) + .then(resolve) + .catch(() => { + // sleep cannot fail + }); + return fn(...args); + }; - return decoratedFn; + return decoratedFn; } diff --git a/frontend/sync-client/src/utils/set-up-telemetry.ts b/frontend/sync-client/src/utils/set-up-telemetry.ts index 6c8e4a4a..d9e73a79 100644 --- a/frontend/sync-client/src/utils/set-up-telemetry.ts +++ b/frontend/sync-client/src/utils/set-up-telemetry.ts @@ -4,38 +4,38 @@ import * as Sentry from "@sentry/browser"; const packageVersion = __CURRENT_VERSION__; // eslint-disable-line export const setUpTelemetry = (): (() => void) => { - Sentry.init({ - dsn: "https://a9bb2b9151bb450ca86b936436e356c4@bugs.schmelczer.dev/1", - release: `sync-client@${packageVersion}`, - sendDefaultPii: true, - integrations: [], - tracesSampleRate: 0 - }); + Sentry.init({ + dsn: "https://a9bb2b9151bb450ca86b936436e356c4@bugs.schmelczer.dev/1", + release: `sync-client@${packageVersion}`, + sendDefaultPii: true, + integrations: [], + tracesSampleRate: 0 + }); - Sentry.captureMessage("Initialised telemetry"); + Sentry.captureMessage("Initialised telemetry"); - const onError = (event: ErrorEvent): void => { - Sentry.captureException(event.error, { - extra: { - message: event.message, - filename: event.filename, - lineno: event.lineno, - colno: event.colno - } - }); - }; - window.addEventListener("error", onError); + const onError = (event: ErrorEvent): void => { + Sentry.captureException(event.error, { + extra: { + message: event.message, + filename: event.filename, + lineno: event.lineno, + colno: event.colno + } + }); + }; + window.addEventListener("error", onError); - const onUnhandledRejection = (event: PromiseRejectionEvent): void => { - Sentry.captureException(event.reason); - }; - window.addEventListener("unhandledrejection", onUnhandledRejection); + const onUnhandledRejection = (event: PromiseRejectionEvent): void => { + Sentry.captureException(event.reason); + }; + window.addEventListener("unhandledrejection", onUnhandledRejection); - return (): void => { - window.removeEventListener("error", onError); - window.removeEventListener("unhandledrejection", onUnhandledRejection); - Sentry.close(5000).catch(() => { - // Ignore errors during shutdown - }); - }; + return (): void => { + window.removeEventListener("error", onError); + window.removeEventListener("unhandledrejection", onUnhandledRejection); + Sentry.close(5000).catch(() => { + // Ignore errors during shutdown + }); + }; }; diff --git a/frontend/sync-client/src/utils/sleep.ts b/frontend/sync-client/src/utils/sleep.ts index 638fc019..ff474799 100644 --- a/frontend/sync-client/src/utils/sleep.ts +++ b/frontend/sync-client/src/utils/sleep.ts @@ -1,3 +1,3 @@ export async function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/frontend/sync-client/tsconfig.json b/frontend/sync-client/tsconfig.json index c49baa45..92caf072 100644 --- a/frontend/sync-client/tsconfig.json +++ b/frontend/sync-client/tsconfig.json @@ -1,18 +1,18 @@ { - "compilerOptions": { - "module": "ESNext", - "target": "ESNext", - "strict": true, - "allowSyntheticDefaultImports": true, - "moduleResolution": "bundler", - "lib": [ - "DOM", // to get `fetch` & `WebSocket` - "ES2024" - ], - "declaration": true, - "declarationDir": "./dist/types" - }, - "exclude": [ - "./dist" - ] -} \ No newline at end of file + "compilerOptions": { + "module": "ESNext", + "target": "ESNext", + "strict": true, + "allowSyntheticDefaultImports": true, + "moduleResolution": "bundler", + "lib": [ + "DOM", // to get `fetch` & `WebSocket` + "ES2024" + ], + "declaration": true, + "declarationDir": "./dist/types" + }, + "exclude": [ + "./dist" + ] +} diff --git a/frontend/sync-client/webpack.config.js b/frontend/sync-client/webpack.config.js index d84a5cd4..b7c3a3fd 100644 --- a/frontend/sync-client/webpack.config.js +++ b/frontend/sync-client/webpack.config.js @@ -4,68 +4,68 @@ const webpack = require("webpack"); const packageJson = require("./package.json"); const common = { - entry: "./src/index.ts", - module: { - rules: [ - { - test: /\.ts$/, - use: ["ts-loader"] - }, - { - test: /\.wasm$/, - type: "asset/inline" - } - ] - }, - plugins: [ - new webpack.DefinePlugin({ - __CURRENT_VERSION__: JSON.stringify(packageJson.version) - }) - ], - optimization: { - // the consuming project should take care of minification - minimize: false - }, - resolve: { - extensions: [".ts", ".js"], - alias: { - root: __dirname, - src: path.resolve(__dirname, "src") - } - }, - performance: { - hints: false // it's a library, no need to warn about its size - } + entry: "./src/index.ts", + module: { + rules: [ + { + test: /\.ts$/, + use: ["ts-loader"] + }, + { + test: /\.wasm$/, + type: "asset/inline" + } + ] + }, + plugins: [ + new webpack.DefinePlugin({ + __CURRENT_VERSION__: JSON.stringify(packageJson.version) + }) + ], + optimization: { + // the consuming project should take care of minification + minimize: false + }, + resolve: { + extensions: [".ts", ".js"], + alias: { + root: __dirname, + src: path.resolve(__dirname, "src") + } + }, + performance: { + hints: false // it's a library, no need to warn about its size + } }; module.exports = [ - merge(common, { - target: "web", - output: { - path: path.resolve(__dirname, "dist"), - filename: "sync-client.web.js", - library: { - name: "SyncClient", - type: "umd" - }, - globalObject: "this" - }, - resolve: { - fallback: { - ws: false // Exclude `ws` from the browser bundle - } - } - }), - merge(common, { - target: "node", - output: { - path: path.resolve(__dirname, "dist"), - filename: "sync-client.node.js", - libraryTarget: "commonjs2" - }, - externals: { - bufferutil: "bufferutil", - "utf-8-validate": "utf-8-validate" // required for ws: https://github.com/websockets/ws/issues/2245#issuecomment-2250318733 - } - }) + merge(common, { + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "sync-client.web.js", + library: { + name: "SyncClient", + type: "umd" + }, + globalObject: "this" + }, + resolve: { + fallback: { + ws: false // Exclude `ws` from the browser bundle + } + } + }), + merge(common, { + target: "node", + output: { + path: path.resolve(__dirname, "dist"), + filename: "sync-client.node.js", + libraryTarget: "commonjs2" + }, + externals: { + bufferutil: "bufferutil", + "utf-8-validate": "utf-8-validate" // required for ws: https://github.com/websockets/ws/issues/2245#issuecomment-2250318733 + } + }) ]; diff --git a/frontend/test-client/src/agent/mock-agent.ts b/frontend/test-client/src/agent/mock-agent.ts index 824f5eee..7926672e 100644 --- a/frontend/test-client/src/agent/mock-agent.ts +++ b/frontend/test-client/src/agent/mock-agent.ts @@ -11,357 +11,357 @@ import { withTimeout } from "../utils/with-timeout"; const TIMEOUT_MS = 10 * 60 * 1000; export class MockAgent extends MockClient { - private readonly writtenContents: string[] = []; - private readonly pendingActions: Promise[] = []; + private readonly writtenContents: string[] = []; + private readonly pendingActions: Promise[] = []; - // The renamed file finding algorithm isn't too smart so we can't both update and rename the same file - private readonly doNotTouchWhileOffline: string[] = []; + // The renamed file finding algorithm isn't too smart so we can't both update and rename the same file + private readonly doNotTouchWhileOffline: string[] = []; - public constructor( - initialSettings: Partial, - public readonly name: string, - private readonly doDeletes: boolean, - private readonly doResets: boolean, - useSlowFileEvents: boolean, - private readonly jitterScaleInSeconds: number - ) { - super(initialSettings, useSlowFileEvents); - } + public constructor( + initialSettings: Partial, + public readonly name: string, + private readonly doDeletes: boolean, + private readonly doResets: boolean, + useSlowFileEvents: boolean, + private readonly jitterScaleInSeconds: number + ) { + super(initialSettings, useSlowFileEvents); + } - public async init(): Promise { - await super.init( - debugging.slowFetchFactory(this.jitterScaleInSeconds), - debugging.slowWebSocketFactory( - this.jitterScaleInSeconds, - new Logger() // this logger isn't wired anywhere, so messages to it will be ignored - ) - ); + public async init(): Promise { + await super.init( + debugging.slowFetchFactory(this.jitterScaleInSeconds), + debugging.slowWebSocketFactory( + this.jitterScaleInSeconds, + new Logger() // this logger isn't wired anywhere, so messages to it will be ignored + ) + ); - assert( - (await this.client.checkConnection()).isSuccessful, - "Connection check failed" - ); + assert( + (await this.client.checkConnection()).isSuccessful, + "Connection check failed" + ); - this.client.logger.addOnMessageListener((logLine: LogLine) => { - const state = this.client.getSettings().isSyncEnabled - ? "(online) " - : "(offline)"; - const formatted = `[${this.name} ${state}] ${logLine.timestamp.toISOString()} ${logLine.level} ${logLine.message}`; + this.client.logger.addOnMessageListener((logLine: LogLine) => { + const state = this.client.getSettings().isSyncEnabled + ? "(online) " + : "(offline)"; + const formatted = `[${this.name} ${state}] ${logLine.timestamp.toISOString()} ${logLine.level} ${logLine.message}`; - // HACK: we have to ensure the file has been synced if we want to change it offline without data loss - const historyEntry = /.*History entry: (.*.md).*/.exec( - logLine.message - ); + // HACK: we have to ensure the file has been synced if we want to change it offline without data loss + const historyEntry = /.*History entry: (.*.md).*/.exec( + logLine.message + ); - if (historyEntry) { - utils.removeFromArray( - this.doNotTouchWhileOffline, - historyEntry[1] - ); - } - switch (logLine.level) { - case LogLevel.ERROR: - console.error(formatted); + if (historyEntry) { + utils.removeFromArray( + this.doNotTouchWhileOffline, + historyEntry[1] + ); + } + switch (logLine.level) { + case LogLevel.ERROR: + console.error(formatted); - if (!this.useSlowFileEvents) { - // Let's wait for the error to be caught if there was one - // eslint-disable-next-line @typescript-eslint/no-floating-promises - sleep(100).then(() => process.exit(1)); - } + if (!this.useSlowFileEvents) { + // Let's wait for the error to be caught if there was one + // eslint-disable-next-line @typescript-eslint/no-floating-promises + sleep(100).then(() => process.exit(1)); + } - break; - case LogLevel.WARNING: - console.warn(formatted); - break; - case LogLevel.INFO: - console.info(formatted); - break; - case LogLevel.DEBUG: - console.debug(formatted); - break; - } - }); + break; + case LogLevel.WARNING: + console.warn(formatted); + break; + case LogLevel.INFO: + console.info(formatted); + break; + case LogLevel.DEBUG: + console.debug(formatted); + break; + } + }); - this.client.logger.info("Agent initialized"); - } + this.client.logger.info("Agent initialized"); + } - public async act(): Promise { - const options: (() => Promise)[] = [ - this.createFileAction.bind(this) - ]; + public async act(): Promise { + const options: (() => Promise)[] = [ + this.createFileAction.bind(this) + ]; - if (this.client.getSettings().isSyncEnabled) { - if (this.doNotTouchWhileOffline.length === 0) { - options.push(this.disableSyncAction.bind(this)); - } - } else { - options.push(this.enableSyncAction.bind(this)); - } + if (this.client.getSettings().isSyncEnabled) { + if (this.doNotTouchWhileOffline.length === 0) { + options.push(this.disableSyncAction.bind(this)); + } + } else { + options.push(this.enableSyncAction.bind(this)); + } - const files = await this.listFilesRecursively(); + const files = await this.listFilesRecursively(); - if (files.length > 0) { - options.push( - this.renameFileAction.bind(this, files), - this.updateFileAction.bind(this, files) - ); + if (files.length > 0) { + options.push( + this.renameFileAction.bind(this, files), + this.updateFileAction.bind(this, files) + ); - if (this.doDeletes) { - options.push(this.deleteFileAction.bind(this, files)); - } - } + if (this.doDeletes) { + options.push(this.deleteFileAction.bind(this, files)); + } + } - if (Math.random() < 0.015 && this.doResets) { - // we can't just queue this up as once it's destroyed, no more method calls can go to SyncClient - await this.resetClient(); - } else { - this.pendingActions.push( - (async (): Promise => { - try { - return await choose(options)(); - } catch (error) { - this.client.logger.error( - `Failed to perform an action: ${error}` - ); - this.client.logger.info( - JSON.stringify(this.data, null, 2) - ); - this.client.logger.info( - JSON.stringify(this.localFiles, null, 2) - ); - throw error; - } - })() - ); - } - } + if (Math.random() < 0.015 && this.doResets) { + // we can't just queue this up as once it's destroyed, no more method calls can go to SyncClient + await this.resetClient(); + } else { + this.pendingActions.push( + (async (): Promise => { + try { + return await choose(options)(); + } catch (error) { + this.client.logger.error( + `Failed to perform an action: ${error}` + ); + this.client.logger.info( + JSON.stringify(this.data, null, 2) + ); + this.client.logger.info( + JSON.stringify(this.localFiles, null, 2) + ); + throw error; + } + })() + ); + } + } - public async finish(): Promise { - await withTimeout( - (async (): Promise => { - await this.client.setSetting("isSyncEnabled", true); - await utils.awaitAll(this.pendingActions); - await this.client.waitUntilFinished(); - })(), - TIMEOUT_MS, - "finish()" - ); - } + public async finish(): Promise { + await withTimeout( + (async (): Promise => { + await this.client.setSetting("isSyncEnabled", true); + await utils.awaitAll(this.pendingActions); + await this.client.waitUntilFinished(); + })(), + TIMEOUT_MS, + "finish()" + ); + } - public async destroy(): Promise { - await withTimeout( - (async (): Promise => { - await this.client.waitUntilFinished(); - await this.client.destroy(); - })(), - TIMEOUT_MS, - "destroy()" - ); - } + public async destroy(): Promise { + await withTimeout( + (async (): Promise => { + await this.client.waitUntilFinished(); + await this.client.destroy(); + })(), + TIMEOUT_MS, + "destroy()" + ); + } - public assertFileSystemsAreConsistent(otherAgent: MockAgent): void { - const globalFiles = Array.from(otherAgent.localFiles.keys()); - const localFiles = Array.from(this.localFiles.keys()); + public assertFileSystemsAreConsistent(otherAgent: MockAgent): void { + const globalFiles = Array.from(otherAgent.localFiles.keys()); + const localFiles = Array.from(this.localFiles.keys()); - const missingInOther = localFiles.filter( - (file) => !otherAgent.localFiles.has(file) - ); - const missingInLocal = globalFiles.filter( - (file) => !this.localFiles.has(file) - ); + const missingInOther = localFiles.filter( + (file) => !otherAgent.localFiles.has(file) + ); + const missingInLocal = globalFiles.filter( + (file) => !this.localFiles.has(file) + ); - try { - assert( - missingInOther.length === 0, - `Files from ${this.name} missing in ${otherAgent.name}: ${missingInOther.join(", ")}` - ); - assert( - missingInLocal.length === 0, - `Files from ${otherAgent.name} missing in ${this.name}: ${missingInLocal.join(", ")}` - ); + try { + assert( + missingInOther.length === 0, + `Files from ${this.name} missing in ${otherAgent.name}: ${missingInOther.join(", ")}` + ); + assert( + missingInLocal.length === 0, + `Files from ${otherAgent.name} missing in ${this.name}: ${missingInLocal.join(", ")}` + ); - for (const file of globalFiles) { - const localContent = new TextDecoder().decode( - this.localFiles.get(file) - ); - const otherContent = new TextDecoder().decode( - otherAgent.localFiles.get(file) - ); - assert( - localContent === otherContent, - `Content mismatch for file ${file}:\n${localContent}\n${otherContent}` - ); - } - } catch (e) { - this.client.logger.info( - "Local data: " + JSON.stringify(this.data, null, 2) - ); - this.client.logger.info( - "Local files: " + - Array.from(otherAgent.localFiles.keys()).join(", ") - ); - otherAgent.client.logger.info( - "Local data: " + JSON.stringify(otherAgent.data, null, 2) - ); - otherAgent.client.logger.info( - "Local files: " + - Array.from(otherAgent.localFiles.keys()).join(", ") - ); + for (const file of globalFiles) { + const localContent = new TextDecoder().decode( + this.localFiles.get(file) + ); + const otherContent = new TextDecoder().decode( + otherAgent.localFiles.get(file) + ); + assert( + localContent === otherContent, + `Content mismatch for file ${file}:\n${localContent}\n${otherContent}` + ); + } + } catch (e) { + this.client.logger.info( + "Local data: " + JSON.stringify(this.data, null, 2) + ); + this.client.logger.info( + "Local files: " + + Array.from(otherAgent.localFiles.keys()).join(", ") + ); + otherAgent.client.logger.info( + "Local data: " + JSON.stringify(otherAgent.data, null, 2) + ); + otherAgent.client.logger.info( + "Local files: " + + Array.from(otherAgent.localFiles.keys()).join(", ") + ); - throw e; - } - } + throw e; + } + } - public assertAllContentIsPresentOnce(): void { - if (this.useSlowFileEvents) { - this.client.logger.info( - // We can't ensure that we have seen every single update - `Skipping content check for ${this.name} because slow file events are enabled` - ); - return; - } + public assertAllContentIsPresentOnce(): void { + if (this.useSlowFileEvents) { + this.client.logger.info( + // We can't ensure that we have seen every single update + `Skipping content check for ${this.name} because slow file events are enabled` + ); + return; + } - for (const content of this.writtenContents) { - const found = Array.from(this.localFiles.keys()).filter((key) => { - return new TextDecoder() - .decode(this.localFiles.get(key)) - .includes(content); - }); + for (const content of this.writtenContents) { + const found = Array.from(this.localFiles.keys()).filter((key) => { + return new TextDecoder() + .decode(this.localFiles.get(key)) + .includes(content); + }); - if (this.doDeletes) { - assert( - found.length <= 1, - `[${this.name}] Content ${content} found in ${found.join(", ")}` - ); - } else { - assert( - found.length >= 1, - `[${this.name}] Content ${content} not found in any files` - ); + if (this.doDeletes) { + assert( + found.length <= 1, + `[${this.name}] Content ${content} found in ${found.join(", ")}` + ); + } else { + assert( + found.length >= 1, + `[${this.name}] Content ${content} not found in any files` + ); - assert( - found.length <= 1, - `[${this.name}] Content ${content} found in multiple files: ${found.join(", ")}` - ); + assert( + found.length <= 1, + `[${this.name}] Content ${content} found in multiple files: ${found.join(", ")}` + ); - const [file] = found; - const fileContent = new TextDecoder().decode( - this.localFiles.get(file) - ); - assert( - fileContent.split(content).length == 2, - `Content ${content} (of ${this.name}) found more than once in '${file}'. File content:\n${fileContent}` - ); - } - } - } + const [file] = found; + const fileContent = new TextDecoder().decode( + this.localFiles.get(file) + ); + assert( + fileContent.split(content).length == 2, + `Content ${content} (of ${this.name}) found more than once in '${file}'. File content:\n${fileContent}` + ); + } + } + } - private async resetClient(): Promise { - this.client.logger.info(`Resetting client ${this.name}`); - await this.client.destroy(); - await this.init(); - } + private async resetClient(): Promise { + this.client.logger.info(`Resetting client ${this.name}`); + await this.client.destroy(); + await this.init(); + } - private async createFileAction(): Promise { - const file = this.getFileName(); + private async createFileAction(): Promise { + const file = this.getFileName(); - if ( - (!this.client.getSettings().isSyncEnabled && - this.doNotTouchWhileOffline.includes(file)) || - (await this.exists(file)) - ) { - return; - } + if ( + (!this.client.getSettings().isSyncEnabled && + this.doNotTouchWhileOffline.includes(file)) || + (await this.exists(file)) + ) { + return; + } - const content = this.getContent(); - this.client.logger.info( - `Decided to create file ${file} with content ${content}` - ); + const content = this.getContent(); + this.client.logger.info( + `Decided to create file ${file} with content ${content}` + ); - return this.create(file, new TextEncoder().encode(` ${content} `)); - } + return this.create(file, new TextEncoder().encode(` ${content} `)); + } - private async disableSyncAction(): Promise { - this.client.logger.info(`Decided to disable sync`); - await this.client.setSetting("isSyncEnabled", false); - } + private async disableSyncAction(): Promise { + this.client.logger.info(`Decided to disable sync`); + await this.client.setSetting("isSyncEnabled", false); + } - private async enableSyncAction(): Promise { - this.client.logger.info(`Decided to enable sync`); - await this.client.setSetting("isSyncEnabled", true); - } + private async enableSyncAction(): Promise { + this.client.logger.info(`Decided to enable sync`); + await this.client.setSetting("isSyncEnabled", true); + } - private async renameFileAction(files: RelativePath[]): Promise { - const file = choose(files); + private async renameFileAction(files: RelativePath[]): Promise { + const file = choose(files); - // We can't edit files offline that have been updated while offline. - // Otherwise, the resolution logic couldn't handle it. - if ( - !this.client.getSettings().isSyncEnabled && - this.doNotTouchWhileOffline.includes(file) - ) { - this.client.logger.info( - `Skipping file ${file} because it has been updated while offline` - ); - return; - } + // We can't edit files offline that have been updated while offline. + // Otherwise, the resolution logic couldn't handle it. + if ( + !this.client.getSettings().isSyncEnabled && + this.doNotTouchWhileOffline.includes(file) + ) { + this.client.logger.info( + `Skipping file ${file} because it has been updated while offline` + ); + return; + } - const newName = this.getFileName(); + const newName = this.getFileName(); - if ( - (!this.client.getSettings().isSyncEnabled && - this.doNotTouchWhileOffline.includes(newName)) || - (await this.exists(newName)) - ) { - return; - } + if ( + (!this.client.getSettings().isSyncEnabled && + this.doNotTouchWhileOffline.includes(newName)) || + (await this.exists(newName)) + ) { + return; + } - this.client.logger.info(`Decided to rename file ${file} to ${newName}`); - this.doNotTouchWhileOffline.push(file, newName); + this.client.logger.info(`Decided to rename file ${file} to ${newName}`); + this.doNotTouchWhileOffline.push(file, newName); - return this.rename(file, newName); - } + return this.rename(file, newName); + } - private async updateFileAction(files: RelativePath[]): Promise { - const file = choose(files); + private async updateFileAction(files: RelativePath[]): Promise { + const file = choose(files); - // We can't edit files offline that have been updated while offline. - // Otherwise, the resolution logic couldn't handle it. - if ( - !this.client.getSettings().isSyncEnabled && - this.doNotTouchWhileOffline.includes(file) - ) { - this.client.logger.info( - `Skipping file ${file} because it has been updated while offline` - ); - return; - } + // We can't edit files offline that have been updated while offline. + // Otherwise, the resolution logic couldn't handle it. + if ( + !this.client.getSettings().isSyncEnabled && + this.doNotTouchWhileOffline.includes(file) + ) { + this.client.logger.info( + `Skipping file ${file} because it has been updated while offline` + ); + return; + } - const content = this.getContent(); - this.client.logger.info( - `Decided to update file ${file} with ${content}` - ); - this.doNotTouchWhileOffline.push(file); - await this.atomicUpdateText(file, (old) => ({ - text: old.text + ` ${content} `, - cursors: [] - })); - } + const content = this.getContent(); + this.client.logger.info( + `Decided to update file ${file} with ${content}` + ); + this.doNotTouchWhileOffline.push(file); + await this.atomicUpdateText(file, (old) => ({ + text: old.text + ` ${content} `, + cursors: [] + })); + } - private async deleteFileAction(files: RelativePath[]): Promise { - const file = choose(files); - this.client.logger.info(`Decided to delete file ${file}`); - return this.delete(file); - } + private async deleteFileAction(files: RelativePath[]): Promise { + const file = choose(files); + this.client.logger.info(`Decided to delete file ${file}`); + return this.delete(file); + } - private getContent(): string { - const uuid = uuidv4(); - this.writtenContents.push(uuid); - return uuid; - } + private getContent(): string { + const uuid = uuidv4(); + this.writtenContents.push(uuid); + return uuid; + } - private getFileName(): string { - // Simulate name collisions between the clients - return `file-${Math.floor(Math.random() * 64)}.md`; - } + private getFileName(): string { + // Simulate name collisions between the clients + return `file-${Math.floor(Math.random() * 64)}.md`; + } } diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 3121db29..c814879a 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -1,197 +1,197 @@ import type { StoredDatabase, TextWithCursors } from "sync-client"; import { assert } from "../utils/assert"; import { - type RelativePath, - type FileSystemOperations, - type SyncSettings, - SyncClient + type RelativePath, + type FileSystemOperations, + type SyncSettings, + SyncClient } from "sync-client"; export class MockClient implements FileSystemOperations { - protected readonly localFiles = new Map(); - protected client!: SyncClient; + protected readonly localFiles = new Map(); + protected client!: SyncClient; - protected data: Partial<{ - settings: Partial; - database: Partial; - }> = { - database: { - // Assume all clients start at the same time so there's no need to fetch - // any shared state. - hasInitialSyncCompleted: true - } - }; + protected data: Partial<{ + settings: Partial; + database: Partial; + }> = { + database: { + // Assume all clients start at the same time so there's no need to fetch + // any shared state. + hasInitialSyncCompleted: true + } + }; - public constructor( - initialSettings: Partial, - protected readonly useSlowFileEvents: boolean - ) { - this.data.settings = initialSettings; - } + public constructor( + initialSettings: Partial, + protected readonly useSlowFileEvents: boolean + ) { + this.data.settings = initialSettings; + } - public async init( - fetchImplementation: typeof globalThis.fetch, - webSocketImplementation: typeof globalThis.WebSocket - ): Promise { - this.client = await SyncClient.create({ - fs: this, - persistence: { - load: async () => this.data, - save: async (data) => void (this.data = data) - }, - fetch: fetchImplementation, - webSocket: webSocketImplementation - }); + public async init( + fetchImplementation: typeof globalThis.fetch, + webSocketImplementation: typeof globalThis.WebSocket + ): Promise { + this.client = await SyncClient.create({ + fs: this, + persistence: { + load: async () => this.data, + save: async (data) => void (this.data = data) + }, + fetch: fetchImplementation, + webSocket: webSocketImplementation + }); - await this.client.start(); - } + await this.client.start(); + } - public async listFilesRecursively( - _root: RelativePath | undefined = undefined // we don't use multi-level paths during tests - ): Promise { - return Array.from(this.localFiles.keys()); - } + public async listFilesRecursively( + _root: RelativePath | undefined = undefined // we don't use multi-level paths during tests + ): Promise { + return Array.from(this.localFiles.keys()); + } - public async read(path: RelativePath): Promise { - const file = this.localFiles.get(path); - if (!file) { - throw new Error(`File ${path} does not exist`); - } - return file; - } + public async read(path: RelativePath): Promise { + const file = this.localFiles.get(path); + if (!file) { + throw new Error(`File ${path} does not exist`); + } + return file; + } - public async getFileSize(path: RelativePath): Promise { - return (await this.read(path)).length; - } + public async getFileSize(path: RelativePath): Promise { + return (await this.read(path)).length; + } - public async exists(path: RelativePath): Promise { - return this.localFiles.has(path); - } + public async exists(path: RelativePath): Promise { + return this.localFiles.has(path); + } - public async create( - path: RelativePath, - newContent: Uint8Array - ): Promise { - if (this.localFiles.has(path)) { - throw new Error(`File ${path} already exists`); - } - this.client.logger.info( - `Creating file ${path} with content ${new TextDecoder().decode(newContent)}` - ); - this.localFiles.set(path, newContent); + public async create( + path: RelativePath, + newContent: Uint8Array + ): Promise { + if (this.localFiles.has(path)) { + throw new Error(`File ${path} already exists`); + } + this.client.logger.info( + `Creating file ${path} with content ${new TextDecoder().decode(newContent)}` + ); + this.localFiles.set(path, newContent); - this.executeFileOperation(async () => - this.client.syncLocallyCreatedFile(path) - ); - } + this.executeFileOperation(async () => + this.client.syncLocallyCreatedFile(path) + ); + } - public async createDirectory(_path: RelativePath): Promise { - // This doesn't mean anything in our virtual FS representation - } + public async createDirectory(_path: RelativePath): Promise { + // This doesn't mean anything in our virtual FS representation + } - public async atomicUpdateText( - path: RelativePath, - updater: (currentContent: TextWithCursors) => TextWithCursors - ): Promise { - const file = this.localFiles.get(path); - if (!file) { - throw new Error(`File ${path} does not exist`); - } - const currentContent = new TextDecoder().decode(file); - const newContent = updater({ text: currentContent, cursors: [] }).text; - const newContentUint8Array = new TextEncoder().encode(newContent); - this.localFiles.set(path, newContentUint8Array); + public async atomicUpdateText( + path: RelativePath, + updater: (currentContent: TextWithCursors) => TextWithCursors + ): Promise { + const file = this.localFiles.get(path); + if (!file) { + throw new Error(`File ${path} does not exist`); + } + const currentContent = new TextDecoder().decode(file); + const newContent = updater({ text: currentContent, cursors: [] }).text; + const newContentUint8Array = new TextEncoder().encode(newContent); + this.localFiles.set(path, newContentUint8Array); - if (!this.useSlowFileEvents) { - const existingParts = currentContent - .split(" ") - .map((part) => part.trim()); - const newParts = newContent.split(" ").map((part) => part.trim()); - existingParts.forEach((part) => - // all changes should be additive - { - assert( - newParts.includes(part), - `Part ${part} not found in new content: ${newContent}` - ); - } - ); - } + if (!this.useSlowFileEvents) { + const existingParts = currentContent + .split(" ") + .map((part) => part.trim()); + const newParts = newContent.split(" ").map((part) => part.trim()); + existingParts.forEach((part) => + // all changes should be additive + { + assert( + newParts.includes(part), + `Part ${part} not found in new content: ${newContent}` + ); + } + ); + } - this.client.logger.info( - `Updated file ${path} with:\n current content: ${currentContent}\n new content: ${newContent}` - ); + this.client.logger.info( + `Updated file ${path} with:\n current content: ${currentContent}\n new content: ${newContent}` + ); - this.executeFileOperation(async () => - this.client.syncLocallyUpdatedFile({ - relativePath: path - }) - ); + this.executeFileOperation(async () => + this.client.syncLocallyUpdatedFile({ + relativePath: path + }) + ); - return newContent; - } + return newContent; + } - public async write(path: RelativePath, content: Uint8Array): Promise { - const hasExisted = this.localFiles.has(path); - this.localFiles.set(path, content); + public async write(path: RelativePath, content: Uint8Array): Promise { + const hasExisted = this.localFiles.has(path); + this.localFiles.set(path, content); - this.client.logger.info( - `Updated file ${path} with:\n new content: ${new TextDecoder().decode(content)}` - ); + this.client.logger.info( + `Updated file ${path} with:\n new content: ${new TextDecoder().decode(content)}` + ); - this.executeFileOperation(async () => { - if (hasExisted) { - return this.client.syncLocallyUpdatedFile({ - relativePath: path - }); - } else { - return this.client.syncLocallyCreatedFile(path); - } - }); - } + this.executeFileOperation(async () => { + if (hasExisted) { + return this.client.syncLocallyUpdatedFile({ + relativePath: path + }); + } else { + return this.client.syncLocallyCreatedFile(path); + } + }); + } - public async delete(path: RelativePath): Promise { - this.client.logger.info( - `Deleting file: ${path} with:\n content ${new TextDecoder().decode(this.localFiles.get(path))}` - ); - this.localFiles.delete(path); + public async delete(path: RelativePath): Promise { + this.client.logger.info( + `Deleting file: ${path} with:\n content ${new TextDecoder().decode(this.localFiles.get(path))}` + ); + this.localFiles.delete(path); - this.executeFileOperation(async () => - this.client.syncLocallyDeletedFile(path) - ); - } + this.executeFileOperation(async () => + this.client.syncLocallyDeletedFile(path) + ); + } - public async rename( - oldPath: RelativePath, - newPath: RelativePath - ): Promise { - const file = this.localFiles.get(oldPath); - if (!file) { - throw new Error(`File ${oldPath} does not exist`); - } - this.localFiles.set(newPath, file); - if (oldPath !== newPath) { - this.localFiles.delete(oldPath); - } + public async rename( + oldPath: RelativePath, + newPath: RelativePath + ): Promise { + const file = this.localFiles.get(oldPath); + if (!file) { + throw new Error(`File ${oldPath} does not exist`); + } + this.localFiles.set(newPath, file); + if (oldPath !== newPath) { + this.localFiles.delete(oldPath); + } - this.client.logger.info( - `Renamed file: ${oldPath} -> ${newPath} with:\n content ${new TextDecoder().decode(file)}` - ); + this.client.logger.info( + `Renamed file: ${oldPath} -> ${newPath} with:\n content ${new TextDecoder().decode(file)}` + ); - this.executeFileOperation(async () => - this.client.syncLocallyUpdatedFile({ - oldPath, - relativePath: newPath - }) - ); - } + this.executeFileOperation(async () => + this.client.syncLocallyUpdatedFile({ + oldPath, + relativePath: newPath + }) + ); + } - private executeFileOperation(callback: () => unknown): void { - if (this.useSlowFileEvents) { - // we aren't the best client and it takes some time to notice changes - setTimeout(callback, Math.random() * 100); - } else { - callback(); - } - } + private executeFileOperation(callback: () => unknown): void { + if (this.useSlowFileEvents) { + // we aren't the best client and it takes some time to notice changes + setTimeout(callback, Math.random() * 100); + } else { + callback(); + } + } } diff --git a/frontend/test-client/src/cli.ts b/frontend/test-client/src/cli.ts index ca433300..70817a24 100644 --- a/frontend/test-client/src/cli.ts +++ b/frontend/test-client/src/cli.ts @@ -11,180 +11,180 @@ const TEST_ITERATIONS = 5; let slowFileEvents = false; async function runTest({ - agentCount, - concurrency, - iterations, - doDeletes, - doResets, - useSlowFileEvents, - jitterScaleInSeconds + agentCount, + concurrency, + iterations, + doDeletes, + doResets, + useSlowFileEvents, + jitterScaleInSeconds }: { - agentCount: number; - concurrency: number; - iterations: number; - doDeletes: boolean; - doResets: boolean; - useSlowFileEvents: boolean; - jitterScaleInSeconds: number; + agentCount: number; + concurrency: number; + iterations: number; + doDeletes: boolean; + doResets: boolean; + useSlowFileEvents: boolean; + jitterScaleInSeconds: number; }): Promise { - slowFileEvents = useSlowFileEvents; + slowFileEvents = useSlowFileEvents; - const settings = `with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}, doResets ${doResets}, jitterScaleInSeconds ${jitterScaleInSeconds}, useSlowFileEvents ${useSlowFileEvents}`; - console.info(`Running test ${settings}`); + const settings = `with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}, doResets ${doResets}, jitterScaleInSeconds ${jitterScaleInSeconds}, useSlowFileEvents ${useSlowFileEvents}`; + console.info(`Running test ${settings}`); - const vaultName = uuidv4(); - console.info(`Using vault name: ${vaultName}`); - const initialSettings: Partial = { - isSyncEnabled: true, - token: " test-token-change-me ", // same as in sync-server/config-e2e.yml with spaces - vaultName: randomCasing(vaultName) + (Math.random() > 0.5 ? " " : ""), // extra spaces shouldn't matter - syncConcurrency: concurrency, - remoteUri: "http://localhost:3000" - }; + const vaultName = uuidv4(); + console.info(`Using vault name: ${vaultName}`); + const initialSettings: Partial = { + isSyncEnabled: true, + token: " test-token-change-me ", // same as in sync-server/config-e2e.yml with spaces + vaultName: randomCasing(vaultName) + (Math.random() > 0.5 ? " " : ""), // extra spaces shouldn't matter + syncConcurrency: concurrency, + remoteUri: "http://localhost:3000" + }; - const clients: MockAgent[] = []; - for (let i = 0; i < agentCount; i++) { - clients.push( - new MockAgent( - initialSettings, - `agent-${i}`, - doDeletes, - doResets, - useSlowFileEvents, - jitterScaleInSeconds - ) - ); - } + const clients: MockAgent[] = []; + for (let i = 0; i < agentCount; i++) { + clients.push( + new MockAgent( + initialSettings, + `agent-${i}`, + doDeletes, + doResets, + useSlowFileEvents, + jitterScaleInSeconds + ) + ); + } - try { - await utils.awaitAll(clients.map(async (client) => client.init())); + try { + await utils.awaitAll(clients.map(async (client) => client.init())); - for (let i = 0; i < iterations; i++) { - console.info(`Iteration ${i + 1}/${iterations}`); - await utils.awaitAll(clients.map(async (client) => client.act())); - await sleep(Math.random() * 200); - } + for (let i = 0; i < iterations; i++) { + console.info(`Iteration ${i + 1}/${iterations}`); + await utils.awaitAll(clients.map(async (client) => client.act())); + await sleep(Math.random() * 200); + } - console.info("Stopping agents"); + console.info("Stopping agents"); - // Each agent can have unpushed changes which might conflict with eachother so each has to resolve the conflicts & push, and - for (const client of clients) { - try { - console.info(`Finishing up ${client.name}`); - await client.finish(); - } catch (err) { - if (!slowFileEvents) { - throw err; - } - } - } + // Each agent can have unpushed changes which might conflict with eachother so each has to resolve the conflicts & push, and + for (const client of clients) { + try { + console.info(`Finishing up ${client.name}`); + await client.finish(); + } catch (err) { + if (!slowFileEvents) { + throw err; + } + } + } - // then we need a second pass to ensure that all agents pull the same state. - for (const client of clients) { - try { - console.info(`Destroying ${client.name}`); - await client.destroy(); - } catch (err) { - if (!slowFileEvents) { - throw err; - } - } - } + // then we need a second pass to ensure that all agents pull the same state. + for (const client of clients) { + try { + console.info(`Destroying ${client.name}`); + await client.destroy(); + } catch (err) { + if (!slowFileEvents) { + throw err; + } + } + } - console.info("Agents finished successfully"); + console.info("Agents finished successfully"); - clients.slice(0, -1).forEach((client, i) => { - console.info( - `Checking consistency between ${client.name} and ${clients[i + 1].name}` - ); - client.assertFileSystemsAreConsistent(clients[i]); - console.info(`Consistency check for ${client.name} passed`); - }); + clients.slice(0, -1).forEach((client, i) => { + console.info( + `Checking consistency between ${client.name} and ${clients[i + 1].name}` + ); + client.assertFileSystemsAreConsistent(clients[i]); + console.info(`Consistency check for ${client.name} passed`); + }); - console.info("File systems found to be consistent"); + console.info("File systems found to be consistent"); - clients.forEach((client) => { - console.info(`Checking content for ${client.name}`); - client.assertAllContentIsPresentOnce(); - console.info(`Content check for ${client.name} passed`); - }); + clients.forEach((client) => { + console.info(`Checking content for ${client.name}`); + client.assertAllContentIsPresentOnce(); + console.info(`Content check for ${client.name} passed`); + }); - console.info(`Test passed ${settings}`); - } catch (err) { - console.error(`Test failed ${settings}`); - throw err; - } + console.info(`Test passed ${settings}`); + } catch (err) { + console.error(`Test failed ${settings}`); + throw err; + } } async function runTests(): Promise { - for (let i = 0; i < TEST_ITERATIONS; i++) { - for (const useSlowFileEvents of [false, true]) { - for (const concurrency of [ - 16, - 1 // test with concurrency 1 to check for deadlocks - ]) { - for (const doDeletes of [false, true]) { - await runTest({ - agentCount: 2, - concurrency, - iterations: 100, - doDeletes, - doResets: false, - useSlowFileEvents, - jitterScaleInSeconds: 0.75 - }); - } - } - } + for (let i = 0; i < TEST_ITERATIONS; i++) { + for (const useSlowFileEvents of [false, true]) { + for (const concurrency of [ + 16, + 1 // test with concurrency 1 to check for deadlocks + ]) { + for (const doDeletes of [false, true]) { + await runTest({ + agentCount: 2, + concurrency, + iterations: 100, + doDeletes, + doResets: false, + useSlowFileEvents, + jitterScaleInSeconds: 0.75 + }); + } + } + } - await runTest({ - agentCount: 2, - concurrency: 16, - iterations: 100, - doDeletes: true, - doResets: true, - useSlowFileEvents: true, - jitterScaleInSeconds: 0.75 - }); - } + await runTest({ + agentCount: 2, + concurrency: 16, + iterations: 100, + doDeletes: true, + doResets: true, + useSlowFileEvents: true, + jitterScaleInSeconds: 0.75 + }); + } } process.on("uncaughtException", (error) => { - if (slowFileEvents) { - return; - } + if (slowFileEvents) { + return; + } - if ( - error instanceof Error && - error.message.includes( - "WebSocket was closed before the connection was established" - ) - ) { - return; - } + if ( + error instanceof Error && + error.message.includes( + "WebSocket was closed before the connection was established" + ) + ) { + return; + } - console.error("Uncaught exception:", error); - process.exit(1); + console.error("Uncaught exception:", error); + process.exit(1); }); process.on("unhandledRejection", (error, _promise) => { - if (error instanceof Error && error.message === "Sync was reset") { - return; - } + if (error instanceof Error && error.message === "Sync was reset") { + return; + } - if (slowFileEvents) { - return; - } + if (slowFileEvents) { + return; + } - console.error("Unhandled rejection:", error); - process.exit(1); + console.error("Unhandled rejection:", error); + process.exit(1); }); runTests() - .then(() => { - process.exit(0); - }) - .catch((err: unknown) => { - console.error(err); - process.exit(1); - }); + .then(() => { + process.exit(0); + }) + .catch((err: unknown) => { + console.error(err); + process.exit(1); + }); diff --git a/frontend/test-client/src/utils/assert.ts b/frontend/test-client/src/utils/assert.ts index e1e3bb98..4e709060 100644 --- a/frontend/test-client/src/utils/assert.ts +++ b/frontend/test-client/src/utils/assert.ts @@ -1,5 +1,5 @@ export function assert(value: boolean, message: string): asserts value { - if (!value) { - throw new Error(message); - } + if (!value) { + throw new Error(message); + } } diff --git a/frontend/test-client/src/utils/choose.ts b/frontend/test-client/src/utils/choose.ts index adb1dc7c..09d8339f 100644 --- a/frontend/test-client/src/utils/choose.ts +++ b/frontend/test-client/src/utils/choose.ts @@ -1,3 +1,3 @@ export function choose(values: T[]): T { - return values[Math.floor(Math.random() * values.length)]; + return values[Math.floor(Math.random() * values.length)]; } diff --git a/frontend/test-client/src/utils/random-casing.test.ts b/frontend/test-client/src/utils/random-casing.test.ts index 67033305..33217525 100644 --- a/frontend/test-client/src/utils/random-casing.test.ts +++ b/frontend/test-client/src/utils/random-casing.test.ts @@ -3,11 +3,11 @@ import assert from "node:assert"; import { randomCasing } from "./random-casing"; describe("randomCasing", () => { - it("simple test", () => { - const input = - "hello, this is a really long string with a lot of characters"; - const result = randomCasing(input); - assert.strictEqual(result.toLowerCase(), input.toLowerCase()); - assert.notStrictEqual(result, input); - }); + it("simple test", () => { + const input = + "hello, this is a really long string with a lot of characters"; + const result = randomCasing(input); + assert.strictEqual(result.toLowerCase(), input.toLowerCase()); + assert.notStrictEqual(result, input); + }); }); diff --git a/frontend/test-client/src/utils/random-casing.ts b/frontend/test-client/src/utils/random-casing.ts index bf9f99dc..ba09dace 100644 --- a/frontend/test-client/src/utils/random-casing.ts +++ b/frontend/test-client/src/utils/random-casing.ts @@ -1,10 +1,10 @@ export function randomCasing(str: string): string { - const chars = str.split(""); - const randomCasedChars = chars.map((char) => { - if (Math.random() < 0.5) { - return char.toUpperCase(); - } - return char.toLowerCase(); - }); - return randomCasedChars.join(""); + const chars = str.split(""); + const randomCasedChars = chars.map((char) => { + if (Math.random() < 0.5) { + return char.toUpperCase(); + } + return char.toLowerCase(); + }); + return randomCasedChars.join(""); } diff --git a/frontend/test-client/src/utils/sleep.ts b/frontend/test-client/src/utils/sleep.ts index 638fc019..ff474799 100644 --- a/frontend/test-client/src/utils/sleep.ts +++ b/frontend/test-client/src/utils/sleep.ts @@ -1,3 +1,3 @@ export async function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/frontend/test-client/src/utils/with-timeout.ts b/frontend/test-client/src/utils/with-timeout.ts index 7d20dc18..71c9568b 100644 --- a/frontend/test-client/src/utils/with-timeout.ts +++ b/frontend/test-client/src/utils/with-timeout.ts @@ -1,16 +1,16 @@ export async function withTimeout( - promise: Promise, - timeoutMs: number, - operationName: string + promise: Promise, + timeoutMs: number, + operationName: string ): Promise { - return Promise.race([ - promise, - new Promise((_, reject) => - setTimeout(() => { - reject( - new Error(`${operationName} timed out after ${timeoutMs}ms`) - ); - }, timeoutMs) - ) - ]); + return Promise.race([ + promise, + new Promise((_, reject) => + setTimeout(() => { + reject( + new Error(`${operationName} timed out after ${timeoutMs}ms`) + ); + }, timeoutMs) + ) + ]); } diff --git a/frontend/test-client/tsconfig.json b/frontend/test-client/tsconfig.json index 7b38e409..e86df89d 100644 --- a/frontend/test-client/tsconfig.json +++ b/frontend/test-client/tsconfig.json @@ -1,17 +1,17 @@ { - "compilerOptions": { - "baseUrl": ".", - "strict": true, - "target": "ES2022", - "module": "CommonJS", - "esModuleInterop": true, - "lib": [ - "DOM", - "ES2024", - ], - "moduleResolution": "node" - }, - "exclude": [ - "./dist" - ] -} \ No newline at end of file + "compilerOptions": { + "baseUrl": ".", + "strict": true, + "target": "ES2022", + "module": "CommonJS", + "esModuleInterop": true, + "lib": [ + "DOM", + "ES2024", + ], + "moduleResolution": "node" + }, + "exclude": [ + "./dist" + ] +} diff --git a/frontend/test-client/webpack.config.js b/frontend/test-client/webpack.config.js index b2324b9b..6aee1547 100644 --- a/frontend/test-client/webpack.config.js +++ b/frontend/test-client/webpack.config.js @@ -2,29 +2,29 @@ const path = require("path"); const webpack = require("webpack"); module.exports = { - entry: "./src/cli.ts", - target: "node", - mode: "production", - optimization: { - minimize: false - }, - module: { - rules: [ - { - test: /\.ts$/, - use: "ts-loader" - } - ] - }, - resolve: { - extensions: [".ts", ".js"] - }, - output: { - globalObject: "this", - filename: "cli.js", - path: path.resolve(__dirname, "dist") - }, - plugins: [ - new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) - ] + entry: "./src/cli.ts", + target: "node", + mode: "production", + optimization: { + minimize: false + }, + module: { + rules: [ + { + test: /\.ts$/, + use: "ts-loader" + } + ] + }, + resolve: { + extensions: [".ts", ".js"] + }, + output: { + globalObject: "this", + filename: "cli.js", + path: path.resolve(__dirname, "dist") + }, + plugins: [ + new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) + ] }; diff --git a/manifest.json b/manifest.json index 68d1568b..c8ee915b 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { - "id": "vault-link", - "name": "VaultLink", - "version": "0.12.0", - "minAppVersion": "0.0.0", - "description": "Self-hosted synchronization and collaboration for your Vault.", - "author": "Andras Schmelczer", - "authorUrl": "https://schmelczer.dev", - "isDesktopOnly": false -} \ No newline at end of file + "id": "vault-link", + "name": "VaultLink", + "version": "0.12.0", + "minAppVersion": "0.0.0", + "description": "Self-hosted synchronization and collaboration for your Vault.", + "author": "Andras Schmelczer", + "authorUrl": "https://schmelczer.dev", + "isDesktopOnly": false +} diff --git a/scripts/build-sync-server-binaries.sh b/scripts/build-sync-server-binaries.sh index 80d8d5e2..8d690935 100755 --- a/scripts/build-sync-server-binaries.sh +++ b/scripts/build-sync-server-binaries.sh @@ -16,29 +16,29 @@ rm -f artifacts/sync-server-* for target in $targets; do echo "Building $target..." - + # Set linkers for cross-compilation case "$target" in - aarch64-unknown-linux-gnu) + aarch64-unknown-linux-gnu) export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc ;; - x86_64-unknown-linux-musl) + x86_64-unknown-linux-musl) export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc ;; - x86_64-pc-windows-gnu) + x86_64-pc-windows-gnu) export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc ;; esac - + rustup target add "$target" 2>/dev/null || true - + cargo build --release --target "$target" ext="" [[ "$target" == *windows* ]] && ext=".exe" - + name="sync-server-${target//-/_}$ext" name="${name//x86_64_unknown_linux_gnu/linux-x86_64}" name="${name//x86_64_unknown_linux_musl/linux-x86_64-musl}" name="${name//aarch64_unknown_linux_gnu/linux-aarch64}" name="${name//x86_64_pc_windows_gnu/windows-x86_64}" - + cp "target/$target/release/sync_server$ext" "artifacts/$name" echo "✓ Built $name" done diff --git a/sync-server/rust-toolchain.toml b/sync-server/rust-toolchain.toml index 635d09fb..010956cc 100644 --- a/sync-server/rust-toolchain.toml +++ b/sync-server/rust-toolchain.toml @@ -1,7 +1,7 @@ [toolchain] channel = "1.89.0" -targets = [ - "x86_64-unknown-linux-gnu", +targets = [ + "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-gnu", diff --git a/sync-server/src/app_state/database.rs b/sync-server/src/app_state/database.rs index 41097925..25dabfa2 100644 --- a/sync-server/src/app_state/database.rs +++ b/sync-server/src/app_state/database.rs @@ -321,8 +321,8 @@ impl Database { from latest_document_versions where relative_path = ? and is_deleted = false order by vault_update_id desc -- `latest_document_versions` only contains a single latest version of each document, however, - -- multiple documents can have the same `relative_path`, if they have been deleted. That's - -- why we only care about the latest version of the document with the given relative path. + -- multiple documents can have the same `relative_path`, if they have been deleted. That's + -- why we only care about the latest version of the document with the given relative path. limit 1 "#, relative_path diff --git a/sync-server/src/config/user_config.rs b/sync-server/src/config/user_config.rs index cdfed838..8b2537f0 100644 --- a/sync-server/src/config/user_config.rs +++ b/sync-server/src/config/user_config.rs @@ -21,7 +21,7 @@ where if let Some(existing_name) = user_token_map.get_by_right(&user.token) { return Err(D::Error::custom(format!( "Duplicate user token found: `{}` for users `{}` and `{}`. User tokens must be \ - unique.", + unique.", user.token, existing_name, user.name ))); } diff --git a/sync-server/src/server/fetch_document_version.rs b/sync-server/src/server/fetch_document_version.rs index 67e72ca4..c30f1d76 100644 --- a/sync-server/src/server/fetch_document_version.rs +++ b/sync-server/src/server/fetch_document_version.rs @@ -54,7 +54,7 @@ pub async fn fetch_document_version( if result.document_id != document_id { return Err(not_found_error(anyhow!( "Document with document id `{document_id}` does not have a version with id \ - `{vault_update_id}`", + `{vault_update_id}`", ))); } diff --git a/sync-server/src/server/fetch_document_version_content.rs b/sync-server/src/server/fetch_document_version_content.rs index a74e88ec..9fdd0ad8 100644 --- a/sync-server/src/server/fetch_document_version_content.rs +++ b/sync-server/src/server/fetch_document_version_content.rs @@ -54,7 +54,7 @@ pub async fn fetch_document_version_content( if result.document_id != document_id { return Err(not_found_error(anyhow!( "Document with document id `{document_id}` does not have a version with id \ - `{vault_update_id}`", + `{vault_update_id}`", ))); }