From 63a2079773e17109d840c5f0a73f9fe0e773a238 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 13 Dec 2025 12:03:35 +0000 Subject: [PATCH 01/25] Extract const --- frontend/local-client-cli/src/cli.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/local-client-cli/src/cli.ts b/frontend/local-client-cli/src/cli.ts index 0f8262f7..48fd8954 100644 --- a/frontend/local-client-cli/src/cli.ts +++ b/frontend/local-client-cli/src/cli.ts @@ -35,6 +35,8 @@ const LOG_LEVEL_ORDER = { [LogLevel.ERROR]: 3 }; +const HEALTH_CHECK_INTERVAL_MS = 30 * 1000; + async function main(): Promise { const args = parseArgs(process.argv); const absolutePath = path.resolve(args.localPath); @@ -147,7 +149,7 @@ async function main(): Promise { void client.checkConnection().then((status) => { writeHealthStatus(healthFile, status); }); - }, 30 * 1000); // every 30 seconds + }, HEALTH_CHECK_INTERVAL_MS); const clearHealthInterval = (): void => { clearInterval(healthInterval); }; From c638ded53a42d561a88e4eff89a0ed24529fed2d Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 10:55:46 +0000 Subject: [PATCH 02/25] Always kill server --- .github/workflows/e2e.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0e437cbd..196e02f3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -47,9 +47,16 @@ jobs: run: | cd sync-server cargo run config-e2e.yml --color never & + SERVER_PID=$! cd .. scripts/e2e.sh 8 + EXIT_CODE=$? + + kill $SERVER_PID 2>/dev/null || true + wait $SERVER_PID 2>/dev/null || true + + exit $EXIT_CODE - name: Cleanup if: always() From 2a53fd3b5906f277b2c8b60300d1d19c81c7f0a3 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 10:55:54 +0000 Subject: [PATCH 03/25] Don't publish PRs --- .github/workflows/publish-plugin.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml index 9e74c60d..92dd199b 100644 --- a/.github/workflows/publish-plugin.yml +++ b/.github/workflows/publish-plugin.yml @@ -3,8 +3,6 @@ name: Publish Obsidian plugin on: push: tags: ["*"] - pull_request: - branches: ["main"] env: CARGO_TERM_COLOR: always From 19022c5b5f207a021dd14bf0bb88bbd41d2919e8 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:05:36 +0000 Subject: [PATCH 04/25] Reject pending locks on reset --- .../src/utils/data-structures/locks.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index 8ad60429..3f676667 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -1,3 +1,4 @@ +import { SyncResetError } from "../../services/sync-reset-error"; import type { Logger } from "../../tracing/logger"; import { awaitAll } from "../await-all"; @@ -12,9 +13,9 @@ export class Locks { private readonly locked = new Set(); /** Queue of resolve functions waiting for each key */ - private readonly waiters = new Map unknown)[]>(); + private readonly waiters = new Map unknown, (err: unknown) => 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. @@ -67,6 +68,13 @@ export class Locks { } public reset(): void { + // Resolve all waiting promises before clearing to prevent deadlock + // Any operation waiting for a lock will be granted access immediately + for (const waiting of this.waiters.values()) { + for (const [_, reject] of waiting) { + reject(new SyncResetError()); + } + } this.locked.clear(); this.waiters.clear(); } @@ -102,7 +110,7 @@ export class Locks { this.logger?.debug(`Waiting for lock on ${key}`); - return new Promise((resolve) => { + return new Promise((resolve, reject) => { // DefaultDict behavior let waiting = this.waiters.get(key); if (!waiting) { @@ -110,7 +118,7 @@ export class Locks { this.waiters.set(key, waiting); } - waiting.push(resolve); + waiting.push([resolve, reject]); }); } @@ -127,11 +135,11 @@ export class Locks { } // Remove first waiter to ensure FIFO order - const nextWaiting = this.waiters.get(key)?.shift(); + const [resolveNextWaiting, _] = this.waiters.get(key)?.shift() ?? []; - if (nextWaiting) { + if (resolveNextWaiting) { this.logger?.debug(`Granted lock on ${key}`); - nextWaiting(); + resolveNextWaiting(); } else { this.locked.delete(key); } From 9c5882e5fbb29ffe1bf1fdc79d5db1116c9d2482 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:05:55 +0000 Subject: [PATCH 05/25] Handle websocket race condition --- frontend/sync-client/src/services/websocket-manager.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index f8dc59d4..0cc4d15e 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -188,6 +188,11 @@ export class WebSocketManager { this.webSocket = new this.webSocketFactoryImplementation(wsUri); this.webSocket.onopen = (): void => { + // Check if we've been stopped while connecting + if (this.isStopped) { + this.webSocket?.close(1000, "WebSocketManager was stopped during connection"); + return; + } this.logger.info("WebSocket connection opened"); this.onWebSocketStatusChanged.trigger(true); }; From 45505a4bf720d58bcefae8ff4a5d4b5f5fe0a95e Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:06:49 +0000 Subject: [PATCH 06/25] Wait for idle instead --- frontend/sync-client/src/sync-operations/syncer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 78cef699..709b9f62 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -171,7 +171,7 @@ export class Syncer { // in that case, we mustn't move it again. if ( this.database.getLatestDocumentByRelativePath(relativePath) === - undefined || + undefined || this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === true ) { @@ -264,7 +264,7 @@ export class Syncer { public async waitUntilFinished(): Promise { await this.runningScheduleSyncForOfflineChanges; - await this.syncQueue.onEmpty(); + await this.syncQueue.onIdle(); // Wait for queue to be empty and running tasks to finish } public async syncRemotelyUpdatedFile( From d91993f249a06d8ccffed43651396d26cff7a454 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:31:48 +0000 Subject: [PATCH 07/25] Unsubscribe in SyncClient --- frontend/sync-client/src/sync-client.ts | 43 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 1544a1e0..633d20b5 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -34,6 +34,8 @@ export class SyncClient { private hasStarted = false; private hasBeenDestroyed = false; private unloadTelemetry?: () => void; + private isDestroying = false; + private readonly eventUnsubscribers: (() => void)[] = []; private constructor( private readonly history: SyncHistory, @@ -53,8 +55,9 @@ export class SyncClient { settings: Partial; database: Partial; }> - > - ) {} + >, + ) { + } public get documentCount(): number { return this.database.length; @@ -159,11 +162,6 @@ export class SyncClient { settings.getSettings().isSyncEnabled, logger ); - settings.onSettingsChanged.add((newSettings, oldSettings) => { - if (oldSettings.isSyncEnabled != newSettings.isSyncEnabled) { - fetchController.canFetch = newSettings.isSyncEnabled; - } - }); const syncService = new SyncService( deviceId, @@ -258,13 +256,23 @@ export class SyncClient { this.unloadTelemetry = setUpTelemetry(); } - this.logger.onLogEmitted.add((log): void => { - if (log.level === LogLevel.ERROR && Sentry.isInitialized()) { - Sentry.captureMessage(log.message); + this.eventUnsubscribers.push(this.settings.onSettingsChanged.add((newSettings, oldSettings) => { + if (oldSettings.isSyncEnabled != newSettings.isSyncEnabled) { + this.fetchController.canFetch = newSettings.isSyncEnabled; } - }); + })); - this.settings.onSettingsChanged.add(this.onSettingsChange.bind(this)); + this.eventUnsubscribers.push( + this.logger.onLogEmitted.add((log): void => { + if (log.level === LogLevel.ERROR && Sentry.isInitialized()) { + Sentry.captureMessage(log.message); + } + }) + ); + + this.eventUnsubscribers.push( + this.settings.onSettingsChanged.add(this.onSettingsChange.bind(this)) + ); if (this.settings.getSettings().isSyncEnabled) { this.logger.info("Starting SyncClient"); @@ -431,6 +439,13 @@ export class SyncClient { public async destroy(): Promise { this.checkIfDestroyed("destroy"); + // Prevent concurrent destroy calls + if (this.isDestroying) { + this.logger.warn("destroy() called while already destroying, ignoring"); + return; + } + this.isDestroying = true; + // cancel everything that's in progress await this.pause(); @@ -438,6 +453,10 @@ export class SyncClient { this.resetInMemoryState(); + // Clean up event listeners to prevent memory leaks + this.eventUnsubscribers.forEach((unsubscribe) => unsubscribe()); + this.eventUnsubscribers.length = 0; + this.logger.info("SyncClient has been successfully disposed"); this.unloadTelemetry?.(); From f431bea1afb9511c9aa529a086963f38d1cd0887 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:43:57 +0000 Subject: [PATCH 08/25] Add lock tests --- .../src/utils/data-structures/locks.test.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) 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 0c09c062..c1a4fb4b 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.test.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.test.ts @@ -5,6 +5,7 @@ import type { RelativePath } from "../../persistence/database"; import { Locks } from "./locks"; import { awaitAll } from "../await-all"; import { sleep } from "../sleep"; +import { SyncResetError } from "../../services/sync-reset-error"; describe("withLock", () => { const testPath: RelativePath = "test/document/path"; @@ -230,3 +231,62 @@ describe("withLock", () => { ]); }); }); + +describe("reset", () => { + const testPath: RelativePath = "test/document/path"; + const logger = new Logger(); + + // eslint-disable-next-line @typescript-eslint/init-declarations + let locks: Locks; + + beforeEach(() => { + locks = new Locks(logger); + }); + + it("should reject pending waiters with SyncResetError while running operation completes", async () => { + const firstPromise = locks.withLock(testPath, async () => { + await sleep(2); + return "first"; + }); + + await sleep(1); + + const secondPromise = locks.withLock(testPath, async () => "second"); + void secondPromise.catch(() => { }); + + locks.reset(); + + assert.strictEqual(await firstPromise, "first"); + + await assert.rejects(secondPromise, (err: Error) => { + assert.ok(err instanceof SyncResetError); + return true; + }); + }); + + it("should allow locks to work normally after reset", async () => { + const firstPromise = locks.withLock(testPath, async () => { + await sleep(1); + return "first"; + }); + + await sleep(1); + + const secondPromise = locks.withLock(testPath, async () => "second"); + void secondPromise.catch(() => { }); + + locks.reset(); + + await firstPromise; + + const result = await locks.withLock(testPath, () => "after-reset"); + assert.strictEqual(result, "after-reset"); + }); + + it("should handle reset with no pending operations", async () => { + locks.reset(); + + const result = await locks.withLock(testPath, () => "success"); + assert.strictEqual(result, "success"); + }); +}); From c7507a3e7ac785c42ffbcc9e3ac81eb8cb899b98 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 11:47:47 +0000 Subject: [PATCH 09/25] Upload logs instead of printing them --- .github/workflows/e2e.yml | 8 ++++++++ scripts/e2e.sh | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 196e02f3..19a44428 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -58,6 +58,14 @@ jobs: exit $EXIT_CODE + - name: Upload e2e logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-logs + path: logs/ + retention-days: 30 + - name: Cleanup if: always() run: scripts/clean-up.sh diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 49f320a0..77a3d19c 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -51,7 +51,7 @@ for i in $(seq 1 $process_count); do echo "Started process $i with PID: $pid" # Read from pipe, prefix with PID - (sed "s/^/[PID $pid] /" < "$pipe" | tee "../logs/log_${i}.log"; rm "$pipe") & + (sed "s/^/[PID $pid] /" < "$pipe" > "../logs/log_${i}.log"; rm "$pipe") & done cd .. From e25306c4c14b6d8b8e3c172e4558d90f369c046a Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 13:53:35 +0000 Subject: [PATCH 10/25] Check node version --- .github/workflows/check.yml | 8 +------- scripts/build-docs.sh | 2 ++ scripts/check.sh | 7 +++++++ scripts/e2e.sh | 6 +----- scripts/utils/check-node.sh | 9 +++++++++ 5 files changed, 20 insertions(+), 12 deletions(-) create mode 100755 scripts/utils/check-node.sh diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index cf890830..9aa71fb4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -5,6 +5,7 @@ on: branches: ["main"] pull_request: branches: ["main"] + workflow_dispatch: env: CARGO_TERM_COLOR: always @@ -31,12 +32,5 @@ jobs: toolchain: "1.89.0" components: clippy, rustfmt - - name: Setup rust - run: | - which sqlx || cargo install sqlx-cli - cd sync-server - sqlx database create --database-url sqlite://db.sqlite3 - sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 - - name: Lint & test run: scripts/check.sh diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh index 9f3c76d4..c87144a9 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -2,6 +2,8 @@ set -e +./scripts/utils/check-node.sh + cd docs npm ci diff --git a/scripts/check.sh b/scripts/check.sh index 2a13953a..bac8f3c3 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -8,8 +8,15 @@ if [[ "$1" == "--fix" ]]; then echo "Running in fix mode - will automatically fix linting and formatting issues" fi +./scripts/utils/check-node.sh + echo "Running checks in sync-server" + cd sync-server +which sqlx || cargo install sqlx-cli +sqlx database create --database-url sqlite://db.sqlite3 +sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 + cargo test --verbose if [[ "$FIX_MODE" == true ]]; then diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 77a3d19c..6c66e835 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -6,11 +6,7 @@ set -o pipefail NO_COLOR=1 FORCE_COLOR=0 -node_version=$(node -v | sed 's/^v\([0-9]*\).*/\1/') -if [ "$node_version" != "22" ]; then - echo "Error: This script requires Node.js version 22, found: $node_version" - exit 1 -fi +./scripts/utils/check-node.sh # Check if the argument is provided if [ $# -eq 0 ]; then diff --git a/scripts/utils/check-node.sh b/scripts/utils/check-node.sh new file mode 100755 index 00000000..c9ede47e --- /dev/null +++ b/scripts/utils/check-node.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +node_version=$(node -v | sed 's/^v\([0-9]*\).*/\1/') +if [ "$node_version" != "22" ]; then + echo "Error: This script requires Node.js version 22, found: $node_version" + exit 1 +fi From 16bb5042d578264be0ecf68c1504977170596cee Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 13:55:23 +0000 Subject: [PATCH 11/25] Format & lint --- .github/workflows/e2e.yml | 1 + .../src/services/websocket-manager.ts | 5 +++- frontend/sync-client/src/sync-client.ts | 29 ++++++++++++------- .../sync-client/src/sync-operations/syncer.ts | 2 +- .../src/utils/data-structures/locks.test.ts | 4 +-- .../src/utils/data-structures/locks.ts | 7 +++-- scripts/check.sh | 6 +--- 7 files changed, 32 insertions(+), 22 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 19a44428..7d0a2a0f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -7,6 +7,7 @@ on: branches: ["main"] schedule: - cron: '0 * * * *' + workflow_dispatch: concurrency: group: e2e-tests diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 0cc4d15e..09787bce 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -190,7 +190,10 @@ export class WebSocketManager { this.webSocket.onopen = (): void => { // Check if we've been stopped while connecting if (this.isStopped) { - this.webSocket?.close(1000, "WebSocketManager was stopped during connection"); + this.webSocket?.close( + 1000, + "WebSocketManager was stopped during connection" + ); return; } this.logger.info("WebSocket connection opened"); diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 633d20b5..2a272c86 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -55,9 +55,8 @@ export class SyncClient { settings: Partial; database: Partial; }> - >, - ) { - } + > + ) {} public get documentCount(): number { return this.database.length; @@ -256,11 +255,13 @@ export class SyncClient { this.unloadTelemetry = setUpTelemetry(); } - this.eventUnsubscribers.push(this.settings.onSettingsChanged.add((newSettings, oldSettings) => { - if (oldSettings.isSyncEnabled != newSettings.isSyncEnabled) { - this.fetchController.canFetch = newSettings.isSyncEnabled; - } - })); + this.eventUnsubscribers.push( + this.settings.onSettingsChanged.add((newSettings, oldSettings) => { + if (oldSettings.isSyncEnabled != newSettings.isSyncEnabled) { + this.fetchController.canFetch = newSettings.isSyncEnabled; + } + }) + ); this.eventUnsubscribers.push( this.logger.onLogEmitted.add((log): void => { @@ -271,7 +272,9 @@ export class SyncClient { ); this.eventUnsubscribers.push( - this.settings.onSettingsChanged.add(this.onSettingsChange.bind(this)) + this.settings.onSettingsChanged.add( + this.onSettingsChange.bind(this) + ) ); if (this.settings.getSettings().isSyncEnabled) { @@ -441,7 +444,9 @@ export class SyncClient { // Prevent concurrent destroy calls if (this.isDestroying) { - this.logger.warn("destroy() called while already destroying, ignoring"); + this.logger.warn( + "destroy() called while already destroying, ignoring" + ); return; } this.isDestroying = true; @@ -454,7 +459,9 @@ export class SyncClient { this.resetInMemoryState(); // Clean up event listeners to prevent memory leaks - this.eventUnsubscribers.forEach((unsubscribe) => unsubscribe()); + this.eventUnsubscribers.forEach((unsubscribe) => { + unsubscribe(); + }); this.eventUnsubscribers.length = 0; this.logger.info("SyncClient has been successfully disposed"); diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 709b9f62..71dedd85 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -171,7 +171,7 @@ export class Syncer { // in that case, we mustn't move it again. if ( this.database.getLatestDocumentByRelativePath(relativePath) === - undefined || + undefined || this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === true ) { 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 c1a4fb4b..9beb867a 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.test.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.test.ts @@ -252,7 +252,7 @@ describe("reset", () => { await sleep(1); const secondPromise = locks.withLock(testPath, async () => "second"); - void secondPromise.catch(() => { }); + void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function locks.reset(); @@ -273,7 +273,7 @@ describe("reset", () => { await sleep(1); const secondPromise = locks.withLock(testPath, async () => "second"); - void secondPromise.catch(() => { }); + void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function locks.reset(); diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index 3f676667..e55c76b0 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -13,9 +13,12 @@ export class Locks { private readonly locked = new Set(); /** Queue of resolve functions waiting for each key */ - private readonly waiters = new Map unknown, (err: unknown) => unknown])[]>(); + private readonly waiters = new Map< + T, + [() => unknown, (err: unknown) => 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. diff --git a/scripts/check.sh b/scripts/check.sh index bac8f3c3..7c3c87e5 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -58,8 +58,4 @@ fi cd .. -if [[ "$FIX_MODE" == true ]]; then - $0 -else - echo "Success" -fi +echo "Success" From a212aba755d7576a2848dac1e0f39b04ff42194f Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 13:58:36 +0000 Subject: [PATCH 12/25] Use node 25 --- .github/workflows/check.yml | 2 +- .github/workflows/deploy-docs.yml | 9 ++++----- .github/workflows/e2e.yml | 2 +- .github/workflows/publish-plugin.yml | 2 +- scripts/utils/check-node.sh | 6 ++++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9aa71fb4..a5d3287a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v4.2.0 with: - node-version: "22.x" + node-version: "25.x" check-latest: true - name: Setup Rust toolchain diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b6d369cc..1e20fd1a 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -28,12 +28,11 @@ jobs: with: fetch-depth: 0 - - name: Setup Node - uses: actions/setup-node@v4 + - name: Setup Node.js environment + uses: actions/setup-node@v4.2.0 with: - node-version: 22 - cache: npm - cache-dependency-path: docs/package-lock.json + node-version: "25.x" + check-latest: true - name: Setup Pages uses: actions/configure-pages@v4 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7d0a2a0f..ed3ee27b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v4.2.0 with: - node-version: "22.x" + node-version: "25.x" check-latest: true - name: Setup Rust toolchain diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml index 92dd199b..1eaccd26 100644 --- a/.github/workflows/publish-plugin.yml +++ b/.github/workflows/publish-plugin.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v4.2.0 with: - node-version: "22.x" + node-version: "25.x" check-latest: true - name: Build plugin diff --git a/scripts/utils/check-node.sh b/scripts/utils/check-node.sh index c9ede47e..d93f2f27 100755 --- a/scripts/utils/check-node.sh +++ b/scripts/utils/check-node.sh @@ -2,8 +2,10 @@ set -e +TARGET_NODE_VERSION=25 + node_version=$(node -v | sed 's/^v\([0-9]*\).*/\1/') -if [ "$node_version" != "22" ]; then - echo "Error: This script requires Node.js version 22, found: $node_version" +if [ "$node_version" != "$TARGET_NODE_VERSION" ]; then + echo "Error: This script requires Node.js version $TARGET_NODE_VERSION, found: $node_version" exit 1 fi From 7438108885eeb725c35846b6b1a753298469fa17 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 14:08:48 +0000 Subject: [PATCH 13/25] Bumps --- frontend/local-client-cli/package.json | 10 +- frontend/obsidian-plugin/package.json | 20 +- frontend/package-lock.json | 3921 +++--------------------- frontend/package.json | 25 +- frontend/sync-client/package.json | 16 +- frontend/test-client/package.json | 10 +- 6 files changed, 548 insertions(+), 3454 deletions(-) diff --git a/frontend/local-client-cli/package.json b/frontend/local-client-cli/package.json index 6cfa180c..0cd7da7b 100644 --- a/frontend/local-client-cli/package.json +++ b/frontend/local-client-cli/package.json @@ -16,13 +16,13 @@ "watcher": "^2.3.1" }, "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "webpack": "^5.99.9", + "tsx": "^4.21.0", + "typescript": "5.9.3", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } } diff --git a/frontend/obsidian-plugin/package.json b/frontend/obsidian-plugin/package.json index 219fca41..bc424e82 100644 --- a/frontend/obsidian-plugin/package.json +++ b/frontend/obsidian-plugin/package.json @@ -13,25 +13,25 @@ "author": "", "license": "MIT", "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "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", + "fs-extra": "^11.3.2", + "mini-css-extract-plugin": "^2.9.4", + "obsidian": "1.11.0", "reconcile-text": "^0.8.0", "resolve-url-loader": "^5.0.0", - "sass": "^1.91.0", + "sass": "^1.96.0", "sass-loader": "^16.0.6", "sync-client": "file:../sync-client", - "terser-webpack-plugin": "^5.3.14", - "ts-loader": "^9.5.2", + "terser-webpack-plugin": "^5.3.16", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", + "tsx": "^4.21.0", + "typescript": "5.9.3", "url": "^0.11.4", - "webpack": "^5.99.9", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3e944c5c..0fc0ce40 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,12 +13,11 @@ ], "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" + "eslint": "9.39.2", + "eslint-plugin-unused-imports": "^4.3.0", + "npm-check-updates": "^19.2.0", + "prettier": "^3.7.4", + "typescript-eslint": "8.49.0" } }, "local-client-cli": { @@ -31,13 +30,13 @@ "vaultlink": "dist/cli.js" }, "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "webpack": "^5.99.9", + "tsx": "^4.21.0", + "typescript": "5.9.3", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } }, @@ -52,20 +51,6 @@ "@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, @@ -75,14 +60,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" @@ -92,14 +76,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -109,14 +92,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -126,14 +108,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -143,14 +124,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -160,14 +140,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -177,14 +156,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -194,14 +172,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -211,14 +188,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -228,14 +204,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -245,14 +220,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -262,14 +236,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -279,14 +252,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", "cpu": [ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -296,14 +268,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -313,14 +284,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -330,14 +300,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -347,14 +316,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -364,14 +332,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -381,14 +348,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -398,14 +364,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -415,14 +380,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -432,14 +396,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" @@ -449,14 +412,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" @@ -466,14 +428,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -483,14 +444,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -500,14 +460,13 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -570,24 +529,22 @@ } }, "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==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0" + "@eslint/core": "^0.17.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==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -618,11 +575,10 @@ } }, "node_modules/@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -641,13 +597,12 @@ } }, "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==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -710,6 +665,27 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -759,46 +735,7 @@ "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" - } + "license": "MIT" }, "node_modules/@parcel/watcher": { "version": "2.5.1", @@ -874,82 +811,76 @@ } }, "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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.30.0.tgz", + "integrity": "sha512-dVsHTUbvgaLNetWAQC6yJFnmgD0xUbVgCkmzNB7S28wIP570GcZ4cxFGPOkXbPx6dEBUfoOREeXzLqjJLtJPfg==", "dev": true, - "license": "MIT", "dependencies": { - "@sentry/core": "10.8.0" + "@sentry/core": "10.30.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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.30.0.tgz", + "integrity": "sha512-+bnQZ6SNF265nTXrRlXTmq5Ila1fRfraDOAahlOT/VM4j6zqCvNZzmeDD9J6IbxiAdhlp/YOkrG3zbr5vgYo0A==", "dev": true, - "license": "MIT", "dependencies": { - "@sentry/core": "10.8.0" + "@sentry/core": "10.30.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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.30.0.tgz", + "integrity": "sha512-Pj/fMIZQkXzIw6YWpxKWUE5+GXffKq6CgXwHszVB39al1wYz1gTIrTqJqt31IBLIihfCy8XxYddglR2EW0BVIQ==", "dev": true, - "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "10.8.0", - "@sentry/core": "10.8.0" + "@sentry-internal/browser-utils": "10.30.0", + "@sentry/core": "10.30.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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.30.0.tgz", + "integrity": "sha512-RIlIz+XQ4DUWaN60CjfmicJq2O2JRtDKM5lw0wB++M5ha0TBh6rv+Ojf6BDgiV3LOQ7lZvCM57xhmNUtrGmelg==", "dev": true, - "license": "MIT", "dependencies": { - "@sentry-internal/replay": "10.8.0", - "@sentry/core": "10.8.0" + "@sentry-internal/replay": "10.30.0", + "@sentry/core": "10.30.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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.30.0.tgz", + "integrity": "sha512-7M/IJUMLo0iCMLNxDV/OHTPI0WKyluxhCcxXJn7nrCcolu8A1aq9R8XjKxm0oTCO8ht5pz8bhGXUnYJj4eoEBA==", "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" + "@sentry-internal/browser-utils": "10.30.0", + "@sentry-internal/feedback": "10.30.0", + "@sentry-internal/replay": "10.30.0", + "@sentry-internal/replay-canvas": "10.30.0", + "@sentry/core": "10.30.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==", + "version": "10.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.30.0.tgz", + "integrity": "sha512-IfNuqIoGVO9pwphwbOptAEJJI1SCAfewS5LBU1iL7hjPBHYAnE8tCVzyZN+pooEkQQ47Q4rGanaG1xY8mjTT1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" } @@ -981,9 +912,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "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 }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -991,13 +923,12 @@ "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==", + "version": "25.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.2.tgz", + "integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==", "dev": true, - "license": "MIT", "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/tern": { @@ -1009,18 +940,17 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", "dev": true, - "license": "MIT", + "peer": true, "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", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" @@ -1033,7 +963,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.41.0", + "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -1047,16 +977,16 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, - "license": "MIT", + "peer": true, "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", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "engines": { @@ -1072,14 +1002,13 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.41.0", - "@typescript-eslint/types": "^8.41.0", + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "engines": { @@ -1094,14 +1023,13 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/visitor-keys": "8.41.0" + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1112,11 +1040,10 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1129,15 +1056,14 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.41.0", - "@typescript-eslint/typescript-estree": "8.41.0", - "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1154,11 +1080,10 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1168,21 +1093,19 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", "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", + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -1201,7 +1124,6 @@ "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" } @@ -1211,7 +1133,6 @@ "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" }, @@ -1223,16 +1144,15 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", "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" + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1247,13 +1167,12 @@ } }, "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==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -1452,6 +1371,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1459,6 +1379,18 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "dev": true, @@ -1483,6 +1415,7 @@ "version": "6.12.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1538,68 +1471,6 @@ "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, @@ -1622,156 +1493,27 @@ "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", + "node_modules/baseline-browser-mapping": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", + "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", "dev": true, - "license": "MIT", - "engines": { - "node": "*" + "bin": { + "baseline-browser-mapping": "dist/cli.js" } }, - "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==", + "node_modules/big.js": { + "version": "5.2.2", "dev": true, "license": "MIT", "engines": { @@ -1801,7 +1543,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -1817,12 +1561,13 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -1831,122 +1576,16 @@ "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, @@ -1982,18 +1621,10 @@ "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", + "version": "1.0.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", "dev": true, "funding": [ { @@ -2008,8 +1639,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -2037,16 +1667,6 @@ "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, @@ -2069,74 +1689,6 @@ "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, @@ -2150,26 +1702,6 @@ "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, @@ -2183,35 +1715,6 @@ "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, @@ -2228,16 +1731,6 @@ "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", @@ -2284,20 +1777,12 @@ "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 + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -2366,14 +1851,6 @@ "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, @@ -2390,57 +1867,11 @@ } } }, - "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, @@ -2472,319 +1903,11 @@ "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" + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2799,106 +1922,6 @@ "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, @@ -2955,12 +1978,11 @@ } }, "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==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2968,32 +1990,32 @@ "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" + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" } }, "node_modules/escalade": { @@ -3016,20 +2038,20 @@ } }, "node_modules/eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, - "license": "MIT", + "peer": true, "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/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3076,9 +2098,10 @@ } }, "node_modules/eslint-plugin-unused-imports": { - "version": "4.1.4", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz", + "integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==", "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" @@ -3137,20 +2160,6 @@ "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, @@ -3202,170 +2211,11 @@ "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, @@ -3399,30 +2249,6 @@ "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, @@ -3453,16 +2279,6 @@ "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, @@ -3514,60 +2330,11 @@ "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", + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3577,27 +2344,6 @@ "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", @@ -3664,19 +2410,6 @@ "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", @@ -3690,28 +2423,6 @@ "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, @@ -3723,56 +2434,11 @@ "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" + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, "node_modules/globals": { "version": "14.0.0", @@ -3801,368 +2467,6 @@ "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, @@ -4171,19 +2475,6 @@ "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, @@ -4206,30 +2497,6 @@ "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, @@ -4295,37 +2562,6 @@ "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, @@ -4334,54 +2570,6 @@ "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, @@ -4396,19 +2584,6 @@ "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, @@ -4436,16 +2611,6 @@ "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, @@ -4465,86 +2630,6 @@ "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, @@ -4613,19 +2698,6 @@ "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, @@ -4642,45 +2714,6 @@ "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, @@ -4693,23 +2726,17 @@ "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", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -4743,61 +2770,11 @@ "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, @@ -4806,35 +2783,11 @@ "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, @@ -4866,20 +2819,11 @@ "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", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", + "integrity": "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==", "dev": true, - "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -4899,6 +2843,7 @@ "version": "8.17.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4955,29 +2900,6 @@ "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, @@ -4991,22 +2913,6 @@ "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, @@ -5034,68 +2940,23 @@ "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" - } + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true }, "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==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.2.0.tgz", + "integrity": "sha512-XSIuL0FNgzXPDZa4lje7+OwHjiyEt84qQm6QMsQRbixNY5EHEM9nhgOjxjlK9jIbN+ysvSqOV8DKNS0zydwbdg==", "dev": true, - "license": "Apache-2.0", "bin": { "ncu": "build/cli.js", "npm-check-updates": "build/cli.js" @@ -5105,39 +2966,6 @@ "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, @@ -5149,62 +2977,6 @@ "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, @@ -5221,96 +2993,6 @@ "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, @@ -5340,26 +3022,28 @@ } }, "node_modules/p-queue": { - "version": "8.1.0", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.0.1.tgz", + "integrity": "sha512-RhBdVhSwJb7Ocn3e8ULk4NMwBEuOxe+1zcgphUy9c2e5aR/xbEsdVXxHJ3lynw6Qiqu7OINEyHlZkiblEpaq7w==", "dev": true, - "license": "MIT", "dependencies": { "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" + "p-timeout": "^7.0.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-timeout": { - "version": "6.1.4", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14.16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5384,23 +3068,6 @@ "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, @@ -5409,16 +3076,6 @@ "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, @@ -5507,22 +3164,6 @@ "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, @@ -5541,6 +3182,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -5631,11 +3273,10 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -5646,13 +3287,6 @@ "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/promise-make-counter": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/promise-make-counter/-/promise-make-counter-1.0.2.tgz", @@ -5668,47 +3302,6 @@ "integrity": "sha512-B+b+kQ1YrYS7zO7P7bQcoqqMUizP06BOyNSBEnB5VJKDSWo8fsVuDkfSmwdjF0JsRtaNh83so5MMFJ95soH5jg==", "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, @@ -5731,27 +3324,6 @@ "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, @@ -5760,29 +3332,6 @@ "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, @@ -5818,59 +3367,6 @@ "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, @@ -5887,13 +3383,6 @@ "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, @@ -5940,19 +3429,6 @@ "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", @@ -5978,41 +3454,6 @@ "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, @@ -6040,19 +3481,12 @@ ], "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==", + "version": "1.96.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.96.0.tgz", + "integrity": "sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -6145,31 +3579,6 @@ "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, @@ -6281,43 +3690,6 @@ "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, @@ -6334,47 +3706,6 @@ "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, @@ -6399,16 +3730,6 @@ "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, @@ -6430,8 +3751,7 @@ "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 + "license": "MIT" }, "node_modules/supports-color": { "version": "8.1.1", @@ -6463,101 +3783,16 @@ "link": true }, "node_modules/tapable": { - "version": "2.2.1", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "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" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser": { @@ -6578,9 +3813,10 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -6614,6 +3850,7 @@ "version": "8.17.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6692,38 +3929,6 @@ "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/tiny-readdir": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", @@ -6733,18 +3938,50 @@ "promise-make-counter": "^1.0.2" } }, - "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==", + "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": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/to-regex-range": { @@ -6758,29 +3995,6 @@ "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, @@ -6794,7 +4008,6 @@ "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" }, @@ -6803,9 +4016,10 @@ } }, "node_modules/ts-loader": { - "version": "9.5.2", + "version": "9.5.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", + "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -6835,13 +4049,12 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "~0.25.0", + "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -6866,9 +4079,11 @@ } }, "node_modules/typescript": { - "version": "5.8.3", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6878,16 +4093,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", - "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", "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" + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6901,33 +4115,11 @@ "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" - } + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true }, "node_modules/universalify": { "version": "2.0.1", @@ -6938,7 +4130,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", "dev": true, "funding": [ { @@ -6954,7 +4148,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -6991,20 +4184,6 @@ "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, @@ -7024,93 +4203,16 @@ "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 + "license": "MIT" }, "node_modules/watcher": { "version": "2.3.1", @@ -7123,9 +4225,10 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -7135,34 +4238,37 @@ } }, "node_modules/webpack": { - "version": "5.99.9", + "version": "5.103.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", + "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", "@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", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.26.3", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.3", "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", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -7184,6 +4290,7 @@ "version": "6.0.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -7248,17 +4355,20 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/webpack/node_modules/ajv": { "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7272,8 +4382,9 @@ }, "node_modules/webpack/node_modules/ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -7303,13 +4414,15 @@ }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7338,13 +4451,6 @@ "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, @@ -7374,13 +4480,6 @@ "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", @@ -7403,33 +4502,6 @@ } } }, - "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, @@ -7438,13 +4510,6 @@ "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, @@ -7486,64 +4551,82 @@ "version": "0.13.1", "license": "MIT", "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "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", + "fs-extra": "^11.3.2", + "mini-css-extract-plugin": "^2.9.4", + "obsidian": "1.11.0", "reconcile-text": "^0.8.0", "resolve-url-loader": "^5.0.0", - "sass": "^1.91.0", + "sass": "^1.96.0", "sass-loader": "^16.0.6", "sync-client": "file:../sync-client", - "terser-webpack-plugin": "^5.3.14", - "ts-loader": "^9.5.2", + "terser-webpack-plugin": "^5.3.16", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", + "tsx": "^4.21.0", + "typescript": "5.9.3", "url": "^0.11.4", - "webpack": "^5.99.9", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } }, + "obsidian-plugin/node_modules/@codemirror/view": { + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", + "dev": true, + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "obsidian-plugin/node_modules/obsidian": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.11.0.tgz", + "integrity": "sha512-lVqN9AmDWHzhNATi2tDnjqVgI6WUYKeT+lIsAycAyLt4XCC6zRsWzb+tFCiB7Rn3PpttefjoovilhYwvS4Iqxw==", + "dev": true, + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.6" + } + }, "sync-client": { "version": "0.13.1", "devDependencies": { - "@sentry/browser": "^10.8.0", - "@types/node": "^24.8.1", + "@sentry/browser": "^10.30.0", + "@types/node": "^25.0.2", "byte-base64": "^1.1.0", - "minimatch": "^10.0.1", - "p-queue": "^8.1.0", + "minimatch": "^10.1.1", + "p-queue": "^9.0.1", "reconcile-text": "^0.8.0", - "ts-loader": "^9.5.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", + "tsx": "^4.21.0", + "typescript": "5.9.3", "uuid": "^13.0.0", - "webpack": "^5.99.9", + "webpack": "^5.103.0", "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", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -7558,14 +4641,14 @@ "test-client": "dist/cli.js" }, "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", + "tsx": "^4.21.0", + "typescript": "5.9.3", "uuid": "^13.0.0", - "webpack": "^5.99.9", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } } diff --git a/frontend/package.json b/frontend/package.json index df167a5e..37961661 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,19 @@ "trailingComma": "none", "tabWidth": 4, "useTabs": false, - "endOfLine": "lf" + "endOfLine": "lf", + "overrides": [ + { + "files": [ + "*.yml", + "*.yaml", + "*.md" + ], + "options": { + "tabWidth": 2 + } + } + ] }, "scripts": { "build": "npm run build --workspaces", @@ -22,11 +34,10 @@ }, "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" + "eslint": "9.39.2", + "eslint-plugin-unused-imports": "^4.3.0", + "npm-check-updates": "^19.2.0", + "prettier": "^3.7.4", + "typescript-eslint": "8.49.0" } } diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index 1ae9b8f0..a106d870 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -14,19 +14,19 @@ }, "devDependencies": { "byte-base64": "^1.1.0", - "minimatch": "^10.0.1", - "p-queue": "^8.1.0", + "minimatch": "^10.1.1", + "p-queue": "^9.0.1", "reconcile-text": "^0.8.0", "uuid": "^13.0.0", - "@types/node": "^24.8.1", - "ts-loader": "^9.5.2", + "@types/node": "^25.0.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", - "webpack": "^5.99.9", + "tsx": "^4.21.0", + "typescript": "5.9.3", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1", - "@sentry/browser": "^10.8.0", + "@sentry/browser": "^10.30.0", "ws": "^8.18.3" } } diff --git a/frontend/test-client/package.json b/frontend/test-client/package.json index ca4c1479..4979130c 100644 --- a/frontend/test-client/package.json +++ b/frontend/test-client/package.json @@ -11,14 +11,14 @@ "test": "tsx --test 'src/**/*.test.ts'" }, "devDependencies": { - "@types/node": "^24.8.1", + "@types/node": "^25.0.2", "sync-client": "file:../sync-client", - "ts-loader": "^9.5.2", + "ts-loader": "^9.5.4", "tslib": "2.8.1", - "tsx": "^4.20.6", - "typescript": "5.8.3", + "tsx": "^4.21.0", + "typescript": "5.9.3", "uuid": "^13.0.0", - "webpack": "^5.99.9", + "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } } From d13abc115d5d05b3385b64445730ed1d6d1800de Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 14:14:07 +0000 Subject: [PATCH 14/25] Format without eclint --- .github/workflows/deploy-docs.yml | 4 +- .github/workflows/e2e.yml | 2 +- .vscode/settings.json | 4 +- CLAUDE.md | 13 + README.md | 8 +- docs/.cspell.json | 7 +- docs/architecture/data-flow.md | 58 +- docs/config/authentication.md | 6 +- docs/package-lock.json | 5960 ++++++++--------- frontend/local-client-cli/README.md | 36 +- frontend/local-client-cli/src/file-watcher.ts | 4 +- .../local-client-cli/src/node-filesystem.ts | 8 +- frontend/local-client-cli/tsconfig.json | 4 +- frontend/obsidian-plugin/README.md | 26 +- .../obsidian-plugin/src/vault-link-plugin.ts | 6 +- .../src/views/settings/settings-tab.ts | 5 +- frontend/obsidian-plugin/tsconfig.json | 9 +- frontend/obsidian-plugin/webpack.config.js | 2 +- .../src/file-operations/file-operations.ts | 32 +- .../safe-filesystem-operations.ts | 8 +- .../sync-client/src/persistence/database.ts | 2 +- .../src/services/fetch-controller.ts | 52 +- .../services/types/CreateDocumentVersion.ts | 10 +- .../types/FetchLatestDocumentsResponse.ts | 4 +- .../src/services/types/PingResponse.ts | 20 +- frontend/sync-client/src/sync-client.ts | 22 +- .../sync-client/src/sync-operations/syncer.ts | 8 +- .../sync-operations/unrestricted-syncer.ts | 58 +- .../sync-client/src/tracing/sync-history.ts | 10 +- .../sync-client/src/utils/create-client-id.ts | 4 +- .../utils/data-structures/event-listeners.ts | 42 +- .../src/utils/data-structures/locks.ts | 92 +- frontend/sync-client/tsconfig.json | 4 +- frontend/test-client/tsconfig.json | 9 +- sync-server/config-e2e.yml | 32 +- 35 files changed, 3273 insertions(+), 3298 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 1e20fd1a..bb25e463 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -5,8 +5,8 @@ on: branches: - main paths: - - 'docs/**' - - '.github/workflows/deploy-docs.yml' + - "docs/**" + - ".github/workflows/deploy-docs.yml" workflow_dispatch: permissions: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ed3ee27b..d1b8d10b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -6,7 +6,7 @@ on: pull_request: branches: ["main"] schedule: - - cron: '0 * * * *' + - cron: "0 * * * *" workflow_dispatch: concurrency: diff --git a/.vscode/settings.json b/.vscode/settings.json index 88d395f5..98187650 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,6 @@ "**/dist": true, "**/node_modules": true, "**/.sqlx": true, - "**/target": true, - }, + "**/target": true + } } diff --git a/CLAUDE.md b/CLAUDE.md index c77b091b..75324418 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,6 +24,7 @@ VaultLink is a self-hosted Obsidian plugin for real-time collaborative file sync ## Development Commands ### Server Development + ```bash cd sync-server cargo run config-e2e.yml # Start development server @@ -36,6 +37,7 @@ cargo machete --with-metadata # Detect unused dependencies ``` ### Frontend Development + ```bash cd frontend npm run dev # Start development mode (watches sync-client and obsidian-plugin) @@ -45,6 +47,7 @@ npm run lint # Lint and format TypeScript code ``` ### Database Setup (Development) + ```bash cd sync-server sqlx database create --database-url sqlite://db.sqlite3 @@ -53,12 +56,14 @@ cargo sqlx prepare --workspace ``` ### Initial Setup + ```bash # Install required cargo tools cargo install sqlx-cli cargo-machete cargo-edit ``` ### Scripts + - `scripts/check.sh`: Full CI check (builds, lints, tests both server and frontend) - `scripts/check.sh --fix`: Same as above but auto-fixes linting and formatting issues - `scripts/e2e.sh`: End-to-end testing @@ -69,16 +74,20 @@ cargo install sqlx-cli cargo-machete cargo-edit ## Code Structure ### Workspace Configuration + The frontend uses npm workspaces with four packages: + - `sync-client`: Core synchronization logic - `obsidian-plugin`: Obsidian-specific integration - `test-client`: Testing utilities - `local-client-cli`: Standalone CLI for VaultLink sync client ### Type Generation + Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/bindings/` and used by frontend packages. ### Key Files + - `sync-server/src/`: Rust server implementation with WebSocket handlers - `frontend/sync-client/src/sync-client.ts`: Main sync client entry point - `frontend/obsidian-plugin/src/vault-link-plugin.ts`: Main Obsidian plugin class @@ -87,11 +96,13 @@ Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/b ## Testing ### Running Tests + - Server: `cargo test --verbose` - Frontend: `npm run test` (runs Jest across all workspaces) - E2E: `scripts/e2e.sh` ### Test Structure + - Rust: Unit tests alongside source files - TypeScript: `.test.ts` files using Jest - E2E: Uses test-client to simulate multiple concurrent users @@ -99,12 +110,14 @@ Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/b ## Code Style ### Rust + - Uses extensive Clippy lints (see Cargo.toml) - Follows pedantic linting rules - Forbids unsafe code - Uses cargo fmt with default settings ### TypeScript + - Prettier configuration: 4-space tabs, trailing commas removed, LF line endings - ESLint with unused imports plugin - Consistent across all three frontend packages diff --git a/README.md b/README.md index f5da9b61..74c6ee97 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ ## Develop -### Install [nvm](https://github.com/nvm-sh/nvm) +### Set up Node.JS 25 with [nvm](https://github.com/nvm-sh/nvm) - `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash` -- `nvm install 22` -- `nvm use 22` -- Optionally set the system-wide default: `nvm alias default 22` +- `nvm install 25` +- `nvm use 25` +- Optionally, set the system-wide default: `nvm alias default 25` ### Set up Rust diff --git a/docs/.cspell.json b/docs/.cspell.json index 4967ec16..1177e1e1 100644 --- a/docs/.cspell.json +++ b/docs/.cspell.json @@ -2,12 +2,7 @@ "version": "0.2", "language": "en-GB", "dictionaries": ["en-gb"], - "ignorePaths": [ - "node_modules", - ".vitepress/dist", - ".vitepress/cache", - "package-lock.json" - ], + "ignorePaths": ["node_modules", ".vitepress/dist", ".vitepress/cache", "package-lock.json"], "words": [ "VaultLink", "Obsidian", diff --git a/docs/architecture/data-flow.md b/docs/architecture/data-flow.md index 832c5624..167be524 100644 --- a/docs/architecture/data-flow.md +++ b/docs/architecture/data-flow.md @@ -361,11 +361,11 @@ VALUES (?, ?, ?); ```json { - "type": "upload_file", - "path": "notes/example.md", - "content": "File content here...", - "base_version": 10, - "timestamp": "2024-01-01T12:00:00Z" + "type": "upload_file", + "path": "notes/example.md", + "content": "File content here...", + "base_version": 10, + "timestamp": "2024-01-01T12:00:00Z" } ``` @@ -373,8 +373,8 @@ VALUES (?, ?, ?); ```json { - "type": "download_file", - "path": "notes/example.md" + "type": "download_file", + "path": "notes/example.md" } ``` @@ -382,8 +382,8 @@ VALUES (?, ?, ?); ```json { - "type": "delete_file", - "path": "notes/old.md" + "type": "delete_file", + "path": "notes/old.md" } ``` @@ -391,8 +391,8 @@ VALUES (?, ?, ?); ```json { - "type": "list_files", - "since_version": 0 + "type": "list_files", + "since_version": 0 } ``` @@ -402,11 +402,11 @@ VALUES (?, ?, ?); ```json { - "type": "file_updated", - "path": "notes/example.md", - "version": 11, - "size": 1024, - "hash": "abc123..." + "type": "file_updated", + "path": "notes/example.md", + "version": 11, + "size": 1024, + "hash": "abc123..." } ``` @@ -414,10 +414,10 @@ VALUES (?, ?, ?); ```json { - "type": "file_content", - "path": "notes/example.md", - "content": "Updated content...", - "version": 11 + "type": "file_content", + "path": "notes/example.md", + "content": "Updated content...", + "version": 11 } ``` @@ -425,9 +425,9 @@ VALUES (?, ?, ?); ```json { - "type": "file_deleted", - "path": "notes/old.md", - "version": 12 + "type": "file_deleted", + "path": "notes/old.md", + "version": 12 } ``` @@ -435,9 +435,9 @@ VALUES (?, ?, ?); ```json { - "type": "sync_complete", - "total_files": 150, - "current_version": 200 + "type": "sync_complete", + "total_files": 150, + "current_version": 200 } ``` @@ -445,9 +445,9 @@ VALUES (?, ?, ?); ```json { - "type": "error", - "message": "File too large", - "code": "FILE_TOO_LARGE" + "type": "error", + "message": "File too large", + "code": "FILE_TOO_LARGE" } ``` diff --git a/docs/config/authentication.md b/docs/config/authentication.md index 11425b5b..74977be7 100644 --- a/docs/config/authentication.md +++ b/docs/config/authentication.md @@ -243,9 +243,9 @@ users: 2. Client sends authentication message: ```json { - "type": "auth", - "token": "user-token", - "vault": "vault-name" + "type": "auth", + "token": "user-token", + "vault": "vault-name" } ``` 3. Server validates: diff --git a/docs/package-lock.json b/docs/package-lock.json index dcd4f3b0..d078bbe6 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1,2989 +1,2989 @@ { - "name": "docs", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { "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": { - "@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": { - "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": { - "@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": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "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": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "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": { - "@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": { - "@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": { - "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": { - "@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": { - "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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@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": { - "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": { - "@algolia/client-common": "5.44.0" - }, - "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": { - "@algolia/client-common": "5.44.0" - }, - "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": { - "@algolia/client-common": "5.44.0" - }, - "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": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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": { - "@cspell/dict-ada": "^4.1.1", - "@cspell/dict-al": "^1.1.1", - "@cspell/dict-aws": "^4.0.16", - "@cspell/dict-bash": "^4.2.2", - "@cspell/dict-companies": "^3.2.7", - "@cspell/dict-cpp": "^6.0.14", - "@cspell/dict-cryptocurrencies": "^5.0.5", - "@cspell/dict-csharp": "^4.0.7", - "@cspell/dict-css": "^4.0.18", - "@cspell/dict-dart": "^2.3.1", - "@cspell/dict-data-science": "^2.0.11", - "@cspell/dict-django": "^4.1.5", - "@cspell/dict-docker": "^1.1.16", - "@cspell/dict-dotnet": "^5.0.10", - "@cspell/dict-elixir": "^4.0.8", - "@cspell/dict-en_us": "^4.4.24", - "@cspell/dict-en-common-misspellings": "^2.1.8", - "@cspell/dict-en-gb-mit": "^3.1.14", - "@cspell/dict-filetypes": "^3.0.14", - "@cspell/dict-flutter": "^1.1.1", - "@cspell/dict-fonts": "^4.0.5", - "@cspell/dict-fsharp": "^1.1.1", - "@cspell/dict-fullstack": "^3.2.7", - "@cspell/dict-gaming-terms": "^1.1.2", - "@cspell/dict-git": "^3.0.7", - "@cspell/dict-golang": "^6.0.24", - "@cspell/dict-google": "^1.0.9", - "@cspell/dict-haskell": "^4.0.6", - "@cspell/dict-html": "^4.0.12", - "@cspell/dict-html-symbol-entities": "^4.0.4", - "@cspell/dict-java": "^5.0.12", - "@cspell/dict-julia": "^1.1.1", - "@cspell/dict-k8s": "^1.0.12", - "@cspell/dict-kotlin": "^1.1.1", - "@cspell/dict-latex": "^4.0.4", - "@cspell/dict-lorem-ipsum": "^4.0.5", - "@cspell/dict-lua": "^4.0.8", - "@cspell/dict-makefile": "^1.0.5", - "@cspell/dict-markdown": "^2.0.12", - "@cspell/dict-monkeyc": "^1.0.11", - "@cspell/dict-node": "^5.0.8", - "@cspell/dict-npm": "^5.2.22", - "@cspell/dict-php": "^4.1.0", - "@cspell/dict-powershell": "^5.0.15", - "@cspell/dict-public-licenses": "^2.0.15", - "@cspell/dict-python": "^4.2.21", - "@cspell/dict-r": "^2.1.1", - "@cspell/dict-ruby": "^5.0.9", - "@cspell/dict-rust": "^4.0.12", - "@cspell/dict-scala": "^5.0.8", - "@cspell/dict-shell": "^1.1.2", - "@cspell/dict-software-terms": "^5.1.13", - "@cspell/dict-sql": "^2.2.1", - "@cspell/dict-svelte": "^1.0.7", - "@cspell/dict-swift": "^2.0.6", - "@cspell/dict-terraform": "^1.1.3", - "@cspell/dict-typescript": "^3.2.3", - "@cspell/dict-vue": "^3.0.5", - "@cspell/dict-zig": "^1.0.0" - }, - "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": { - "@cspell/cspell-types": "9.3.2" - }, - "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": { - "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": { - "global-directory": "^4.0.1" - }, - "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": { - "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": { - "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" - }, - "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" - }, - "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" - }, - "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": { - "@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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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": { - "@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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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": { - "@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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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": { - "@cspell/url": "9.3.2", - "import-meta-resolve": "^4.2.0" - }, - "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": { - "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": { - "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": { - "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" - }, - "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": { - "@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": { - "@algolia/autocomplete-core": "1.17.7", - "@algolia/autocomplete-preset-algolia": "1.17.7", - "@docsearch/css": "3.8.2", - "algoliasearch": "^5.14.2" - }, - "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": { - "@types/react": { - "optional": true + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "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" + } }, - "react": { - "optional": true + "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": { + "@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": { + "node": ">= 14.0.0" + } }, - "react-dom": { - "optional": true + "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": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } }, - "search-insights": { - "optional": true + "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": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "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": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "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": { + "@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": { + "@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": { + "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": { + "@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": { + "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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@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": { + "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": { + "@algolia/client-common": "5.44.0" + }, + "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": { + "@algolia/client-common": "5.44.0" + }, + "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": { + "@algolia/client-common": "5.44.0" + }, + "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": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@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": { + "@cspell/dict-ada": "^4.1.1", + "@cspell/dict-al": "^1.1.1", + "@cspell/dict-aws": "^4.0.16", + "@cspell/dict-bash": "^4.2.2", + "@cspell/dict-companies": "^3.2.7", + "@cspell/dict-cpp": "^6.0.14", + "@cspell/dict-cryptocurrencies": "^5.0.5", + "@cspell/dict-csharp": "^4.0.7", + "@cspell/dict-css": "^4.0.18", + "@cspell/dict-dart": "^2.3.1", + "@cspell/dict-data-science": "^2.0.11", + "@cspell/dict-django": "^4.1.5", + "@cspell/dict-docker": "^1.1.16", + "@cspell/dict-dotnet": "^5.0.10", + "@cspell/dict-elixir": "^4.0.8", + "@cspell/dict-en_us": "^4.4.24", + "@cspell/dict-en-common-misspellings": "^2.1.8", + "@cspell/dict-en-gb-mit": "^3.1.14", + "@cspell/dict-filetypes": "^3.0.14", + "@cspell/dict-flutter": "^1.1.1", + "@cspell/dict-fonts": "^4.0.5", + "@cspell/dict-fsharp": "^1.1.1", + "@cspell/dict-fullstack": "^3.2.7", + "@cspell/dict-gaming-terms": "^1.1.2", + "@cspell/dict-git": "^3.0.7", + "@cspell/dict-golang": "^6.0.24", + "@cspell/dict-google": "^1.0.9", + "@cspell/dict-haskell": "^4.0.6", + "@cspell/dict-html": "^4.0.12", + "@cspell/dict-html-symbol-entities": "^4.0.4", + "@cspell/dict-java": "^5.0.12", + "@cspell/dict-julia": "^1.1.1", + "@cspell/dict-k8s": "^1.0.12", + "@cspell/dict-kotlin": "^1.1.1", + "@cspell/dict-latex": "^4.0.4", + "@cspell/dict-lorem-ipsum": "^4.0.5", + "@cspell/dict-lua": "^4.0.8", + "@cspell/dict-makefile": "^1.0.5", + "@cspell/dict-markdown": "^2.0.12", + "@cspell/dict-monkeyc": "^1.0.11", + "@cspell/dict-node": "^5.0.8", + "@cspell/dict-npm": "^5.2.22", + "@cspell/dict-php": "^4.1.0", + "@cspell/dict-powershell": "^5.0.15", + "@cspell/dict-public-licenses": "^2.0.15", + "@cspell/dict-python": "^4.2.21", + "@cspell/dict-r": "^2.1.1", + "@cspell/dict-ruby": "^5.0.9", + "@cspell/dict-rust": "^4.0.12", + "@cspell/dict-scala": "^5.0.8", + "@cspell/dict-shell": "^1.1.2", + "@cspell/dict-software-terms": "^5.1.13", + "@cspell/dict-sql": "^2.2.1", + "@cspell/dict-svelte": "^1.0.7", + "@cspell/dict-swift": "^2.0.6", + "@cspell/dict-terraform": "^1.1.3", + "@cspell/dict-typescript": "^3.2.3", + "@cspell/dict-vue": "^3.0.5", + "@cspell/dict-zig": "^1.0.0" + }, + "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": { + "@cspell/cspell-types": "9.3.2" + }, + "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": { + "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": { + "global-directory": "^4.0.1" + }, + "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": { + "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": { + "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" + }, + "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" + }, + "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" + }, + "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": { + "@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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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": { + "@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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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": { + "@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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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": { + "@cspell/url": "9.3.2", + "import-meta-resolve": "^4.2.0" + }, + "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": { + "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": { + "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": { + "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" + }, + "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": { + "@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": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "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": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "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": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "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": { + "@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" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@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": [ + "x64" + ], + "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": [ + "x64" + ], + "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": { + "@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": { + "@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": { + "@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": { + "@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": { + "@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": { + "@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": { + "@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" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/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": { + "@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" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/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": { + "@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" + }, + "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" + }, + "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" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "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": { + "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": { + "@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": { + "@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": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", + "estree-walker": "^2.0.2", + "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": { + "@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": { + "@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": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "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": { + "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": { + "@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": { + "@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": { + "@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": { + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24" + }, + "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" + }, + "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": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "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": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "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": { + "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": { + "vue": "^3.5.13" + }, + "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": { + "@algolia/abtesting": "1.10.0", + "@algolia/client-abtesting": "5.44.0", + "@algolia/client-analytics": "5.44.0", + "@algolia/client-common": "5.44.0", + "@algolia/client-insights": "5.44.0", + "@algolia/client-personalization": "5.44.0", + "@algolia/client-query-suggestions": "5.44.0", + "@algolia/client-search": "5.44.0", + "@algolia/ingestion": "1.44.0", + "@algolia/monitoring": "1.44.0", + "@algolia/recommend": "5.44.0", + "@algolia/requester-browser-xhr": "5.44.0", + "@algolia/requester-fetch": "5.44.0", + "@algolia/requester-node-http": "5.44.0" + }, + "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" + }, + "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": { + "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": { + "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": { + "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": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "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": { + "chalk": "^5.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "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": { + "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": { + "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": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + }, + "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": { + "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": { + "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": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "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": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "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" + }, + "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": { + "@cspell/cspell-json-reporter": "9.3.2", + "@cspell/cspell-pipe": "9.3.2", + "@cspell/cspell-types": "9.3.2", + "@cspell/dynamic-import": "9.3.2", + "@cspell/url": "9.3.2", + "chalk": "^5.6.2", + "chalk-template": "^1.1.2", + "commander": "^14.0.2", + "cspell-config-lib": "9.3.2", + "cspell-dictionary": "9.3.2", + "cspell-gitignore": "9.3.2", + "cspell-glob": "9.3.2", + "cspell-io": "9.3.2", + "cspell-lib": "9.3.2", + "fast-json-stable-stringify": "^2.1.0", + "flatted": "^3.3.3", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=20" + }, + "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": { + "@cspell/cspell-types": "9.3.2", + "comment-json": "^4.4.1", + "smol-toml": "^1.5.2", + "yaml": "^2.8.1" + }, + "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": { + "@cspell/cspell-pipe": "9.3.2", + "@cspell/cspell-types": "9.3.2", + "cspell-trie-lib": "9.3.2", + "fast-equals": "^5.3.3" + }, + "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": { + "@cspell/url": "9.3.2", + "cspell-glob": "9.3.2", + "cspell-io": "9.3.2" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "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": { + "@cspell/url": "9.3.2", + "picomatch": "^4.0.3" + }, + "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": { + "@cspell/cspell-pipe": "9.3.2", + "@cspell/cspell-types": "9.3.2" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "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": { + "@cspell/cspell-service-bus": "9.3.2", + "@cspell/url": "9.3.2" + }, + "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": { + "@cspell/cspell-bundled-dicts": "9.3.2", + "@cspell/cspell-pipe": "9.3.2", + "@cspell/cspell-resolver": "9.3.2", + "@cspell/cspell-types": "9.3.2", + "@cspell/dynamic-import": "9.3.2", + "@cspell/filetypes": "9.3.2", + "@cspell/strong-weak-map": "9.3.2", + "@cspell/url": "9.3.2", + "clear-module": "^4.1.2", + "cspell-config-lib": "9.3.2", + "cspell-dictionary": "9.3.2", + "cspell-glob": "9.3.2", + "cspell-grammar": "9.3.2", + "cspell-io": "9.3.2", + "cspell-trie-lib": "9.3.2", + "env-paths": "^3.0.0", + "gensequence": "^8.0.8", + "import-fresh": "^3.3.1", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.1.0", + "xdg-basedir": "^5.1.0" + }, + "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": { + "@cspell/cspell-pipe": "9.3.2", + "@cspell/cspell-types": "9.3.2", + "gensequence": "^8.0.8" + }, + "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" + }, + "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": { + "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": { + "dequal": "^2.0.0" + }, + "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" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "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": { + "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": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@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": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "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" + }, + "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": { + "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" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/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": { + "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": [ + "darwin" + ], + "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": { + "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": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "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": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "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": { + "@types/hast": "^3.0.0" + }, + "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" + }, + "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": { + "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": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "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": { + "callsites": "^3.0.0" + }, + "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": { + "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": { + "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": { + "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": { + "node": ">=18" + }, + "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": { + "@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" + }, + "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": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "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": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + }, + "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" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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": { + "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": { + "callsites": "^3.1.0" + }, + "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" + }, + "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" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/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": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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": { + "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": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "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": { + "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": { + "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": { + "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" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.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": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@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 + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@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": { + "node": ">= 18" + }, + "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": { + "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": { + "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": { + "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": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "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": { + "copy-anything": "^4" + }, + "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" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/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": { + "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": { + "@types/unist": "^3.0.0" + }, + "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": { + "@types/unist": "^3.0.0" + }, + "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": { + "@types/unist": "^3.0.0" + }, + "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": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "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": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "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": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "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": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "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": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "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": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "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" + }, + "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" + }, + "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": { + "@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": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "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": { + "node": ">=12" + }, + "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": { + "yaml": "bin.mjs" + }, + "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": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } - }, - "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": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "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": { - "@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" - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@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": [ - "x64" - ], - "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": [ - "x64" - ], - "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": { - "@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": { - "@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": { - "@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": { - "@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": { - "@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": { - "@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": { - "@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" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/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": { - "@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" - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/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": { - "@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" - }, - "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" - }, - "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" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "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": { - "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": { - "@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": { - "@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": { - "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.24", - "@vue/compiler-dom": "3.5.24", - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24", - "estree-walker": "^2.0.2", - "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": { - "@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": { - "@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": { - "@vue/devtools-shared": "^7.7.9", - "birpc": "^2.3.0", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "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": { - "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": { - "@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": { - "@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": { - "@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": { - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24" - }, - "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" - }, - "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": { - "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "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": { - "@vueuse/core": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "async-validator": "^4", - "axios": "^1", - "change-case": "^5", - "drauu": "^0.4", - "focus-trap": "^7", - "fuse.js": "^7", - "idb-keyval": "^6", - "jwt-decode": "^4", - "nprogress": "^0.2", - "qrcode": "^1.5", - "sortablejs": "^1", - "universal-cookie": "^7" - }, - "peerDependenciesMeta": { - "async-validator": { - "optional": true - }, - "axios": { - "optional": true - }, - "change-case": { - "optional": true - }, - "drauu": { - "optional": true - }, - "focus-trap": { - "optional": true - }, - "fuse.js": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "jwt-decode": { - "optional": true - }, - "nprogress": { - "optional": true - }, - "qrcode": { - "optional": true - }, - "sortablejs": { - "optional": true - }, - "universal-cookie": { - "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": { - "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": { - "vue": "^3.5.13" - }, - "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": { - "@algolia/abtesting": "1.10.0", - "@algolia/client-abtesting": "5.44.0", - "@algolia/client-analytics": "5.44.0", - "@algolia/client-common": "5.44.0", - "@algolia/client-insights": "5.44.0", - "@algolia/client-personalization": "5.44.0", - "@algolia/client-query-suggestions": "5.44.0", - "@algolia/client-search": "5.44.0", - "@algolia/ingestion": "1.44.0", - "@algolia/monitoring": "1.44.0", - "@algolia/recommend": "5.44.0", - "@algolia/requester-browser-xhr": "5.44.0", - "@algolia/requester-fetch": "5.44.0", - "@algolia/requester-node-http": "5.44.0" - }, - "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" - }, - "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": { - "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": { - "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": { - "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": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "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": { - "chalk": "^5.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "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": { - "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": { - "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": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - }, - "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": { - "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": { - "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": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1" - }, - "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": { - "is-what": "^5.2.0" - }, - "engines": { - "node": ">=18" - }, - "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" - }, - "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": { - "@cspell/cspell-json-reporter": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/url": "9.3.2", - "chalk": "^5.6.2", - "chalk-template": "^1.1.2", - "commander": "^14.0.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-gitignore": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2", - "cspell-lib": "9.3.2", - "fast-json-stable-stringify": "^2.1.0", - "flatted": "^3.3.3", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15" - }, - "bin": { - "cspell": "bin.mjs", - "cspell-esm": "bin.mjs" - }, - "engines": { - "node": ">=20" - }, - "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": { - "@cspell/cspell-types": "9.3.2", - "comment-json": "^4.4.1", - "smol-toml": "^1.5.2", - "yaml": "^2.8.1" - }, - "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": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "cspell-trie-lib": "9.3.2", - "fast-equals": "^5.3.3" - }, - "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": { - "@cspell/url": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2" - }, - "bin": { - "cspell-gitignore": "bin.mjs" - }, - "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": { - "@cspell/url": "9.3.2", - "picomatch": "^4.0.3" - }, - "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": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2" - }, - "bin": { - "cspell-grammar": "bin.mjs" - }, - "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": { - "@cspell/cspell-service-bus": "9.3.2", - "@cspell/url": "9.3.2" - }, - "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": { - "@cspell/cspell-bundled-dicts": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-resolver": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/filetypes": "9.3.2", - "@cspell/strong-weak-map": "9.3.2", - "@cspell/url": "9.3.2", - "clear-module": "^4.1.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-grammar": "9.3.2", - "cspell-io": "9.3.2", - "cspell-trie-lib": "9.3.2", - "env-paths": "^3.0.0", - "gensequence": "^8.0.8", - "import-fresh": "^3.3.1", - "resolve-from": "^5.0.0", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-uri": "^3.1.0", - "xdg-basedir": "^5.1.0" - }, - "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": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "gensequence": "^8.0.8" - }, - "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" - }, - "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": { - "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": { - "dequal": "^2.0.0" - }, - "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" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "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": { - "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": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@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": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "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" - }, - "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": { - "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" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/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": { - "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": [ - "darwin" - ], - "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": { - "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": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "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": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "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": { - "@types/hast": "^3.0.0" - }, - "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" - }, - "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": { - "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": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "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": { - "callsites": "^3.0.0" - }, - "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": { - "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": { - "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": { - "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": { - "node": ">=18" - }, - "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": { - "@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" - }, - "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": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "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": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "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": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "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": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "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": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "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": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "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" - }, - "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" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/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": { - "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": { - "callsites": "^3.1.0" - }, - "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" - }, - "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" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/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": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/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": { - "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": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "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": { - "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": { - "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": { - "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" - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.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": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.3", - "@rollup/rollup-android-arm64": "4.53.3", - "@rollup/rollup-darwin-arm64": "4.53.3", - "@rollup/rollup-darwin-x64": "4.53.3", - "@rollup/rollup-freebsd-arm64": "4.53.3", - "@rollup/rollup-freebsd-x64": "4.53.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", - "@rollup/rollup-linux-arm-musleabihf": "4.53.3", - "@rollup/rollup-linux-arm64-gnu": "4.53.3", - "@rollup/rollup-linux-arm64-musl": "4.53.3", - "@rollup/rollup-linux-loong64-gnu": "4.53.3", - "@rollup/rollup-linux-ppc64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-musl": "4.53.3", - "@rollup/rollup-linux-s390x-gnu": "4.53.3", - "@rollup/rollup-linux-x64-gnu": "4.53.3", - "@rollup/rollup-linux-x64-musl": "4.53.3", - "@rollup/rollup-openharmony-arm64": "4.53.3", - "@rollup/rollup-win32-arm64-msvc": "4.53.3", - "@rollup/rollup-win32-ia32-msvc": "4.53.3", - "@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 - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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": { - "@shikijs/core": "2.5.0", - "@shikijs/engine-javascript": "2.5.0", - "@shikijs/engine-oniguruma": "2.5.0", - "@shikijs/langs": "2.5.0", - "@shikijs/themes": "2.5.0", - "@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": { - "node": ">= 18" - }, - "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": { - "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": { - "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": { - "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": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "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": { - "copy-anything": "^4" - }, - "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" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/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": { - "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": { - "@types/unist": "^3.0.0" - }, - "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": { - "@types/unist": "^3.0.0" - }, - "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": { - "@types/unist": "^3.0.0" - }, - "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": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "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": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "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": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "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": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "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": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "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": { - "@docsearch/css": "3.8.2", - "@docsearch/js": "3.8.2", - "@iconify-json/simple-icons": "^1.2.21", - "@shikijs/core": "^2.1.0", - "@shikijs/transformers": "^2.1.0", - "@shikijs/types": "^2.1.0", - "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^5.2.1", - "@vue/devtools-api": "^7.7.0", - "@vue/shared": "^3.5.13", - "@vueuse/core": "^12.4.0", - "@vueuse/integrations": "^12.4.0", - "focus-trap": "^7.6.4", - "mark.js": "8.11.1", - "minisearch": "^7.1.1", - "shiki": "^2.1.0", - "vite": "^5.4.14", - "vue": "^3.5.13" - }, - "bin": { - "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { - "markdown-it-mathjax3": "^4", - "postcss": "^8" - }, - "peerDependenciesMeta": { - "markdown-it-mathjax3": { - "optional": true - }, - "postcss": { - "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" - }, - "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" - }, - "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": { - "@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": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "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": { - "node": ">=12" - }, - "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": { - "yaml": "bin.mjs" - }, - "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": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } } diff --git a/frontend/local-client-cli/README.md b/frontend/local-client-cli/README.md index 0585bacc..731160e6 100644 --- a/frontend/local-client-cli/README.md +++ b/frontend/local-client-cli/README.md @@ -47,24 +47,24 @@ vaultlink \ ### Required -| Option | Description | -|--------|-------------| -| `-l, --local-path ` | Local directory to sync | -| `-r, --remote-uri ` | Remote server WebSocket URI (ws:// or wss://) | -| `-t, --token ` | Authentication token | -| `-v, --vault-name ` | Vault name on server | +| Option | Description | +| ------------------------- | --------------------------------------------- | +| `-l, --local-path ` | Local directory to sync | +| `-r, --remote-uri ` | Remote server WebSocket URI (ws:// or wss://) | +| `-t, --token ` | Authentication token | +| `-v, --vault-name ` | Vault name on server | ### Optional -| Option | Default | Description | -|--------|---------|-------------| -| `--sync-concurrency ` | `1` | Concurrent sync operations | -| `--max-file-size-mb ` | `10` | Maximum file size in MB | -| `--ignore-pattern ` | - | Glob pattern to ignore (repeatable) | -| `--websocket-retry-interval-ms ` | `3500` | WebSocket reconnection interval | -| `--log-level ` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR | -| `-h, --help` | - | Show help | -| `-V, --version` | - | Show version | +| Option | Default | Description | +| ------------------------------------ | ------- | -------------------------------------- | +| `--sync-concurrency ` | `1` | Concurrent sync operations | +| `--max-file-size-mb ` | `10` | Maximum file size in MB | +| `--ignore-pattern ` | - | Glob pattern to ignore (repeatable) | +| `--websocket-retry-interval-ms ` | `3500` | WebSocket reconnection interval | +| `--log-level ` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR | +| `-h, --help` | - | Show help | +| `-V, --version` | - | Show version | ### Auto-Ignored Patterns @@ -74,11 +74,13 @@ vaultlink \ ### Examples Basic usage: + ```bash vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default ``` With ignore patterns: + ```bash vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \ --ignore-pattern "*.tmp" \ @@ -87,6 +89,7 @@ vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \ ``` With debug logging: + ```bash vaultlink -l ./vault -r wss://sync.example.com -t token123 -v default \ --log-level DEBUG @@ -176,6 +179,7 @@ services: ## Development Build: + ```bash npm run build # or from the parent folder, run @@ -183,11 +187,13 @@ docker build -f local-client-cli/Dockerfile . ``` Test: + ```bash npm test ``` Docker build: + ```bash cd frontend docker build -f local-client-cli/Dockerfile -t vault-link-cli:test . diff --git a/frontend/local-client-cli/src/file-watcher.ts b/frontend/local-client-cli/src/file-watcher.ts index e781d18f..f1e9198a 100644 --- a/frontend/local-client-cli/src/file-watcher.ts +++ b/frontend/local-client-cli/src/file-watcher.ts @@ -106,8 +106,8 @@ export class FileWatcher { } /** - * Convert a native platform path to forward slashes - */ + * Convert a native platform path to forward slashes + */ private toUnixPath(nativePath: string): string { if (path.sep === "\\") { return nativePath.replace(/\\/g, "/"); diff --git a/frontend/local-client-cli/src/node-filesystem.ts b/frontend/local-client-cli/src/node-filesystem.ts index 3da8fc3a..474d6f58 100644 --- a/frontend/local-client-cli/src/node-filesystem.ts +++ b/frontend/local-client-cli/src/node-filesystem.ts @@ -185,8 +185,8 @@ export class NodeFileSystemOperations implements FileSystemOperations { } /** - * Convert a forward-slash path to native platform path separators - */ + * Convert a forward-slash path to native platform path separators + */ private toNativePath(relativePath: string): string { if (path.sep === "\\") { return relativePath.replace(/\//g, "\\"); @@ -195,8 +195,8 @@ export class NodeFileSystemOperations implements FileSystemOperations { } /** - * Convert a native platform path to forward slashes - */ + * Convert a native platform path to forward slashes + */ private toUnixPath(nativePath: string): string { if (path.sep === "\\") { return nativePath.replace(/\\/g, "/"); diff --git a/frontend/local-client-cli/tsconfig.json b/frontend/local-client-cli/tsconfig.json index 25f249c9..b07ec41a 100644 --- a/frontend/local-client-cli/tsconfig.json +++ b/frontend/local-client-cli/tsconfig.json @@ -18,7 +18,5 @@ "declarationMap": true, "sourceMap": true }, - "exclude": [ - "dist" - ] + "exclude": ["dist"] } diff --git a/frontend/obsidian-plugin/README.md b/frontend/obsidian-plugin/README.md index 93c2cba7..68e10a83 100644 --- a/frontend/obsidian-plugin/README.md +++ b/frontend/obsidian-plugin/README.md @@ -8,6 +8,7 @@ The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definiti **Note:** The Obsidian API is still in early alpha and is subject to change at any time! This sample plugin demonstrates some of the basic functionality the plugin API can do. + - Adds a ribbon icon, which shows a Notice when clicked. - Adds a command "Open Sample Modal" which opens a Modal. - Adds a plugin setting tab to the settings page. @@ -57,31 +58,6 @@ Quick starting guide for new plugin devs: - Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. - -## Funding URL - -You can include funding URLs where people who use your plugin can financially support it. - -The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file: - -```json -{ - "fundingUrl": "https://buymeacoffee.com" -} -``` - -If you have multiple URLs, you can also do: - -```json -{ - "fundingUrl": { - "Buy Me a Coffee": "https://buymeacoffee.com", - "GitHub Sponsor": "https://github.com/sponsors", - "Patreon": "https://www.patreon.com/" - } -} -``` - ## API Documentation See https://github.com/obsidianmd/obsidian-api diff --git a/frontend/obsidian-plugin/src/vault-link-plugin.ts b/frontend/obsidian-plugin/src/vault-link-plugin.ts index 7d91b9f5..3def64f8 100644 --- a/frontend/obsidian-plugin/src/vault-link-plugin.ts +++ b/frontend/obsidian-plugin/src/vault-link-plugin.ts @@ -135,9 +135,9 @@ export default class VaultLinkPlugin extends Plugin { nativeLineEndings: Platform.isWin ? "\r\n" : "\n", ...(IS_DEBUG_BUILD ? { - fetch: debugging.slowFetchFactory(1), - webSocket: debugging.slowWebSocketFactory(1, new Logger()) - } + fetch: debugging.slowFetchFactory(1), + webSocket: debugging.slowWebSocketFactory(1, new Logger()) + } : {}) }); diff --git a/frontend/obsidian-plugin/src/views/settings/settings-tab.ts b/frontend/obsidian-plugin/src/views/settings/settings-tab.ts index 213c0d2c..e38850a2 100644 --- a/frontend/obsidian-plugin/src/views/settings/settings-tab.ts +++ b/frontend/obsidian-plugin/src/views/settings/settings-tab.ts @@ -266,9 +266,8 @@ export class SyncSettingsTab extends PluginSettingTab { new Notice("Checking connection to the server..."); new Notice( - ( - await this.syncClient.checkConnection() - ).serverMessage + (await this.syncClient.checkConnection()) + .serverMessage ); await this.statusDescription.updateConnectionState(); } else { diff --git a/frontend/obsidian-plugin/tsconfig.json b/frontend/obsidian-plugin/tsconfig.json index 81af03a7..7ec2a9cd 100644 --- a/frontend/obsidian-plugin/tsconfig.json +++ b/frontend/obsidian-plugin/tsconfig.json @@ -6,12 +6,7 @@ "strict": true, "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, - "lib": [ - "DOM", - "ES2024" - ] + "lib": ["DOM", "ES2024"] }, - "exclude": [ - "./dist" - ] + "exclude": ["./dist"] } diff --git a/frontend/obsidian-plugin/webpack.config.js b/frontend/obsidian-plugin/webpack.config.js index b749b20d..794f30de 100644 --- a/frontend/obsidian-plugin/webpack.config.js +++ b/frontend/obsidian-plugin/webpack.config.js @@ -46,7 +46,7 @@ module.exports = (env, argv) => ({ 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", + "/volumes/syncthing/Desktop/test/test2/.obsidian/plugins/vault-link" // "/home/andras/obsidian-test/.obsidian/plugins/vault-link" ]; destinations.forEach((destination) => { diff --git a/frontend/sync-client/src/file-operations/file-operations.ts b/frontend/sync-client/src/file-operations/file-operations.ts index 2864bd20..fdf65d35 100644 --- a/frontend/sync-client/src/file-operations/file-operations.ts +++ b/frontend/sync-client/src/file-operations/file-operations.ts @@ -45,11 +45,11 @@ export class FileOperations { } /** - * 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. - */ + * 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 @@ -77,11 +77,11 @@ export class FileOperations { } /** - * 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. - */ + * 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, @@ -239,12 +239,12 @@ export class FileOperations { } /** - * 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 - */ + * 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); 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 904bf805..a3df4ea5 100644 --- a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts @@ -135,10 +135,10 @@ export class SafeFileSystemOperations implements FileSystemOperations { } /** - * 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. - */ + * 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, diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 86b2845c..04e0fce6 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -114,7 +114,7 @@ export class Database { i === 0 ? false : records[i - 1].parallelVersion === - current.parallelVersion + current.parallelVersion ) ) { throw new Error( diff --git a/frontend/sync-client/src/services/fetch-controller.ts b/frontend/sync-client/src/services/fetch-controller.ts index 77b87e3a..08ca6c52 100644 --- a/frontend/sync-client/src/services/fetch-controller.ts +++ b/frontend/sync-client/src/services/fetch-controller.ts @@ -25,18 +25,18 @@ export class FetchController { } /** - * Whether the fetch implementation can immediately send requests once outside of a reset. - */ + * 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 - */ + * 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; @@ -59,9 +59,9 @@ export class FetchController { } /** - * Starts a reset, causing all ongoing and future fetches to be rejected - * with a SyncResetError until finishReset is called. - */ + * 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()); @@ -72,9 +72,9 @@ export class FetchController { } /** - * Finishes a reset, allowing fetches to proceed or wait again depending on - * the current sync settings. - */ + * Finishes a reset, allowing fetches to proceed or wait again depending on + * the current sync settings. + */ public finishReset(): void { if (!this.isResetting) { return; @@ -85,19 +85,19 @@ export class FetchController { } /** - * - * |------------------|---------------|-----------------------------------------------------| - * | | 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 - */ + * + * |------------------|---------------|-----------------------------------------------------| + * | | 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 diff --git a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts index ed921f18..4bae0e50 100644 --- a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts @@ -2,11 +2,11 @@ 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. - */ + * 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/FetchLatestDocumentsResponse.ts b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts index 160c9279..315d701a 100644 --- a/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts +++ b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts @@ -7,7 +7,7 @@ import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutCont export interface FetchLatestDocumentsResponse { latestDocuments: DocumentVersionWithoutContent[]; /** - * The update ID of the latest document in the response. - */ + * 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 6db66354..f96520e9 100644 --- a/frontend/sync-client/src/services/types/PingResponse.ts +++ b/frontend/sync-client/src/services/types/PingResponse.ts @@ -5,21 +5,21 @@ */ export interface PingResponse { /** - * Semantic version of the server. - */ + * Semantic version of the server. + */ serverVersion: string; /** - * Whether the client is authenticated based on the sent Authorization - * header. - */ + * Whether the client is authenticated based on the sent Authorization + * header. + */ isAuthenticated: boolean; /** - * List of file extensions that are allowed to be merged. - */ + * List of file extensions that are allowed to be merged. + */ mergeableFileExtensions: string[]; /** - * API version ensuring backwards & forwards compatibility between the client - * and server. - */ + * API version ensuring backwards & forwards compatibility between the client + * and server. + */ supportedApiVersion: number; } diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 2a272c86..42d70cac 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -285,10 +285,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"); @@ -320,10 +320,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"); @@ -436,9 +436,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 71dedd85..5b6993f9 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -484,10 +484,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/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index e3964d30..7acc246a 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -170,14 +170,14 @@ export class UnrestrictedSyncer { const updateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined ? { - type: SyncType.MOVE, - relativePath: document.relativePath, - movedFrom: oldPath - } + type: SyncType.MOVE, + relativePath: document.relativePath, + movedFrom: oldPath + } : { - type: SyncType.UPDATE, - relativePath: document.relativePath - }; + type: SyncType.UPDATE, + relativePath: document.relativePath + }; await this.executeSync(updateDetails, async () => { const originalRelativePath = document.relativePath; @@ -216,22 +216,22 @@ export class UnrestrictedSyncer { response = isText && cachedVersion !== undefined ? await this.syncService.putText({ - documentId: document.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - content: diff( - new TextDecoder().decode(cachedVersion), - new TextDecoder().decode(contentBytes) - ) - }) + documentId: document.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + content: diff( + new TextDecoder().decode(cachedVersion), + new TextDecoder().decode(contentBytes) + ) + }) : await this.syncService.putBinary({ - documentId: document.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - contentBytes - }); + documentId: document.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + contentBytes + }); } else { if (!force) { this.logger.debug( @@ -336,14 +336,14 @@ export class UnrestrictedSyncer { oldPath !== undefined || response.relativePath != originalRelativePath ? { - type: SyncType.MOVE, - relativePath: response.relativePath, - movedFrom: originalRelativePath - } + type: SyncType.MOVE, + relativePath: response.relativePath, + movedFrom: originalRelativePath + } : { - type: SyncType.UPDATE, - relativePath: response.relativePath - }; + type: SyncType.UPDATE, + relativePath: response.relativePath + }; if (areThereLocalChanges) { this.history.addHistoryEntry({ diff --git a/frontend/sync-client/src/tracing/sync-history.ts b/frontend/sync-client/src/tracing/sync-history.ts index 31f77283..a0e0b348 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/utils/create-client-id.ts b/frontend/sync-client/src/utils/create-client-id.ts index cfa132da..4f32595a 100644 --- a/frontend/sync-client/src/utils/create-client-id.ts +++ b/frontend/sync-client/src/utils/create-client-id.ts @@ -8,8 +8,8 @@ export function createClientId(): string { typeof navigator !== "undefined" ? navigator.platform // eslint-disable-line @typescript-eslint/no-deprecated : typeof process !== "undefined" - ? process.platform - : "unknown"; + ? process.platform + : "unknown"; return `vault-link/${packageVersion} (${uuidv4()}; ${platform})`; } 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 e08ca65e..8b9a08e9 100644 --- a/frontend/sync-client/src/utils/data-structures/event-listeners.ts +++ b/frontend/sync-client/src/utils/data-structures/event-listeners.ts @@ -13,32 +13,32 @@ export class EventListeners any> { } /** - * 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); @@ -46,12 +46,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/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index e55c76b0..d3ab1b26 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -21,34 +21,34 @@ export class Locks { 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 - */ + * 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 @@ -83,12 +83,12 @@ export class Locks { } /** - * 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 - */ + * 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; @@ -100,12 +100,12 @@ export class Locks { } /** - * 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 - */ + * 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(); @@ -126,12 +126,12 @@ export class Locks { } /** - * 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 - */ + * 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; diff --git a/frontend/sync-client/tsconfig.json b/frontend/sync-client/tsconfig.json index 92caf072..98870f32 100644 --- a/frontend/sync-client/tsconfig.json +++ b/frontend/sync-client/tsconfig.json @@ -12,7 +12,5 @@ "declaration": true, "declarationDir": "./dist/types" }, - "exclude": [ - "./dist" - ] + "exclude": ["./dist"] } diff --git a/frontend/test-client/tsconfig.json b/frontend/test-client/tsconfig.json index e86df89d..7558871d 100644 --- a/frontend/test-client/tsconfig.json +++ b/frontend/test-client/tsconfig.json @@ -5,13 +5,8 @@ "target": "ES2022", "module": "CommonJS", "esModuleInterop": true, - "lib": [ - "DOM", - "ES2024", - ], + "lib": ["DOM", "ES2024"], "moduleResolution": "node" }, - "exclude": [ - "./dist" - ] + "exclude": ["./dist"] } diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index 1f235b01..e9d47559 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days From a21b1e8c0331aadabb09a0df65ee07ba397ae9b6 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 14:37:30 +0000 Subject: [PATCH 15/25] Extract errors into module --- .../src/{services => errors}/authentication-error.ts | 0 .../src/{file-operations => errors}/file-not-found-error.ts | 0 .../src/{services => errors}/server-version-mismatch-error.ts | 0 .../sync-client/src/{services => errors}/sync-reset-error.ts | 0 frontend/sync-client/src/index.ts | 4 ++-- frontend/sync-client/src/services/fetch-controller.test.ts | 2 +- frontend/sync-client/src/services/fetch-controller.ts | 2 +- frontend/sync-client/src/services/server-config.ts | 4 ++-- frontend/sync-client/src/services/sync-service.ts | 2 +- .../sync-client/src/sync-operations/unrestricted-syncer.ts | 4 ++-- frontend/sync-client/src/utils/data-structures/locks.test.ts | 2 +- frontend/sync-client/src/utils/data-structures/locks.ts | 2 +- 12 files changed, 11 insertions(+), 11 deletions(-) rename frontend/sync-client/src/{services => errors}/authentication-error.ts (100%) rename frontend/sync-client/src/{file-operations => errors}/file-not-found-error.ts (100%) rename frontend/sync-client/src/{services => errors}/server-version-mismatch-error.ts (100%) rename frontend/sync-client/src/{services => errors}/sync-reset-error.ts (100%) diff --git a/frontend/sync-client/src/services/authentication-error.ts b/frontend/sync-client/src/errors/authentication-error.ts similarity index 100% rename from frontend/sync-client/src/services/authentication-error.ts rename to frontend/sync-client/src/errors/authentication-error.ts diff --git a/frontend/sync-client/src/file-operations/file-not-found-error.ts b/frontend/sync-client/src/errors/file-not-found-error.ts similarity index 100% rename from frontend/sync-client/src/file-operations/file-not-found-error.ts rename to frontend/sync-client/src/errors/file-not-found-error.ts diff --git a/frontend/sync-client/src/services/server-version-mismatch-error.ts b/frontend/sync-client/src/errors/server-version-mismatch-error.ts similarity index 100% rename from frontend/sync-client/src/services/server-version-mismatch-error.ts rename to frontend/sync-client/src/errors/server-version-mismatch-error.ts diff --git a/frontend/sync-client/src/services/sync-reset-error.ts b/frontend/sync-client/src/errors/sync-reset-error.ts similarity index 100% rename from frontend/sync-client/src/services/sync-reset-error.ts rename to frontend/sync-client/src/errors/sync-reset-error.ts diff --git a/frontend/sync-client/src/index.ts b/frontend/sync-client/src/index.ts index cfcc5071..d90da7bf 100644 --- a/frontend/sync-client/src/index.ts +++ b/frontend/sync-client/src/index.ts @@ -27,8 +27,8 @@ export type { PersistenceProvider } from "./persistence/persistence"; export type { CursorSpan } from "./services/types/CursorSpan"; export type { ClientCursors } from "./services/types/ClientCursors"; export type { NetworkConnectionStatus } from "./types/network-connection-status"; -export type { ServerVersionMismatchError } from "./services/server-version-mismatch-error"; -export type { AuthenticationError } from "./services/authentication-error"; +export type { ServerVersionMismatchError } from "./errors/server-version-mismatch-error"; +export type { AuthenticationError } from "./errors/authentication-error"; export type { MaybeOutdatedClientCursors } from "./types/maybe-outdated-client-cursors"; export { DocumentSyncStatus } from "./types/document-sync-status"; export { SyncClient } from "./sync-client"; diff --git a/frontend/sync-client/src/services/fetch-controller.test.ts b/frontend/sync-client/src/services/fetch-controller.test.ts index 94fa8424..a1b791a6 100644 --- a/frontend/sync-client/src/services/fetch-controller.test.ts +++ b/frontend/sync-client/src/services/fetch-controller.test.ts @@ -3,7 +3,7 @@ import { describe, it, mock, beforeEach, afterEach } from "node:test"; import assert from "node:assert"; import { FetchController } from "./fetch-controller"; import { Logger } from "../tracing/logger"; -import { SyncResetError } from "./sync-reset-error"; +import { SyncResetError } from "../errors/sync-reset-error"; import { sleep } from "../utils/sleep"; describe("FetchController", () => { diff --git a/frontend/sync-client/src/services/fetch-controller.ts b/frontend/sync-client/src/services/fetch-controller.ts index 08ca6c52..e30739da 100644 --- a/frontend/sync-client/src/services/fetch-controller.ts +++ b/frontend/sync-client/src/services/fetch-controller.ts @@ -1,6 +1,6 @@ import type { Logger } from "../tracing/logger"; import { createPromise } from "../utils/create-promise"; -import { SyncResetError } from "./sync-reset-error"; +import { SyncResetError } from "../errors/sync-reset-error"; /** * Offers a resettable fetch implementation that waits until syncing is enabled diff --git a/frontend/sync-client/src/services/server-config.ts b/frontend/sync-client/src/services/server-config.ts index 309c637c..b48e9802 100644 --- a/frontend/sync-client/src/services/server-config.ts +++ b/frontend/sync-client/src/services/server-config.ts @@ -1,6 +1,6 @@ import { SUPPORTED_API_VERSION } from "../consts"; -import { AuthenticationError } from "./authentication-error"; -import { ServerVersionMismatchError } from "./server-version-mismatch-error"; +import { AuthenticationError } from "../errors/authentication-error"; +import { ServerVersionMismatchError } from "../errors/server-version-mismatch-error"; import type { SyncService } from "./sync-service"; import type { PingResponse } from "./types/PingResponse"; diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index 8190a638..8dd0de68 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -8,7 +8,7 @@ import type { Logger } from "../tracing/logger"; import type { Settings } from "../persistence/settings"; import type { FetchController } from "./fetch-controller"; import { sleep } from "../utils/sleep"; -import { SyncResetError } from "./sync-reset-error"; +import { SyncResetError } from "../errors/sync-reset-error"; import type { SerializedError } from "./types/SerializedError"; import type { DocumentVersionWithoutContent } from "./types/DocumentVersionWithoutContent"; import type { DocumentUpdateResponse } from "./types/DocumentUpdateResponse"; diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 7acc246a..5b80d75b 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -23,8 +23,8 @@ import { base64ToBytes } from "byte-base64"; import type { Settings } from "../persistence/settings"; import type { FileOperations } from "../file-operations/file-operations"; import { createPromise } from "../utils/create-promise"; -import { FileNotFoundError } from "../file-operations/file-not-found-error"; -import { SyncResetError } from "../services/sync-reset-error"; +import { FileNotFoundError } from "../errors/file-not-found-error"; +import { SyncResetError } from "../errors/sync-reset-error"; import { globsToRegexes } from "../utils/globs-to-regexes"; import type { DocumentVersion } from "../services/types/DocumentVersion"; import type { DocumentUpdateResponse } from "../services/types/DocumentUpdateResponse"; 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 9beb867a..d1dcc6d7 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.test.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.test.ts @@ -5,7 +5,7 @@ import type { RelativePath } from "../../persistence/database"; import { Locks } from "./locks"; import { awaitAll } from "../await-all"; import { sleep } from "../sleep"; -import { SyncResetError } from "../../services/sync-reset-error"; +import { SyncResetError } from "../../errors/sync-reset-error"; describe("withLock", () => { const testPath: RelativePath = "test/document/path"; diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index d3ab1b26..743e8c6a 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -1,4 +1,4 @@ -import { SyncResetError } from "../../services/sync-reset-error"; +import { SyncResetError } from "../../errors/sync-reset-error"; import type { Logger } from "../../tracing/logger"; import { awaitAll } from "../await-all"; From 63867be48a100f171353656ba2d8e8c88328fe7e Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 14:39:16 +0000 Subject: [PATCH 16/25] Remove ws dep --- CLAUDE.md | 167 +++++++++++++----- frontend/local-client-cli/Dockerfile | 4 +- frontend/local-client-cli/package.json | 6 +- frontend/local-client-cli/webpack.config.js | 54 +++--- frontend/package-lock.json | 40 ++--- frontend/sync-client/package.json | 3 +- .../safe-filesystem-operations.ts | 2 +- .../src/services/websocket-manager.test.ts | 10 -- .../src/services/websocket-manager.ts | 20 +-- frontend/sync-client/src/sync-client.ts | 1 - frontend/sync-client/webpack.config.js | 9 - 11 files changed, 172 insertions(+), 144 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 75324418..323681d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -VaultLink is a self-hosted Obsidian plugin for real-time collaborative file syncing. The project consists of a Rust-based sync server and a TypeScript frontend with three main components: an Obsidian plugin, a sync client library, and a test client. +VaultLink is a self-hosted Obsidian plugin for real-time collaborative file syncing. The project consists of a Rust-based sync server and a TypeScript frontend with four main components: an Obsidian plugin, a sync client library, a test client, and a standalone CLI client. ## Architecture @@ -13,22 +13,75 @@ VaultLink is a self-hosted Obsidian plugin for real-time collaborative file sync - **sync-server/**: Rust-based WebSocket server with SQLite database for document versioning and real-time synchronization - **frontend/sync-client/**: TypeScript library providing core sync functionality, WebSocket management, and file operations - **frontend/obsidian-plugin/**: Obsidian plugin that integrates the sync client with Obsidian's API -- **frontend/test-client/**: CLI testing tool for the sync functionality +- **frontend/test-client/**: CLI testing tool for simulating multiple concurrent users +- **frontend/local-client-cli/**: Standalone CLI for VaultLink sync client ### Key Technologies - **Backend**: Rust with Axum framework, SQLite with SQLx, WebSockets for real-time sync -- **Frontend**: TypeScript, Webpack for bundling, Jest for testing +- **Frontend**: TypeScript, Webpack for bundling, Node.js native test runner - **Sync Algorithm**: Uses reconcile-text library for operational transformation +### Architectural Patterns + +**Server Architecture:** + +- `AppState`: Central state container holding `Database`, `Cursors`, and `Broadcasts` +- `Database`: SQLite-backed document versioning with SQLx for compile-time query verification +- `Broadcasts`: WebSocket broadcast system for real-time updates to connected clients +- `Cursors`: Tracks user cursor positions across documents with background cleanup task + +**Client Architecture:** + +- `SyncClient`: Main entry point, orchestrates all sync operations +- `SyncService`: HTTP API client for CRUD operations on documents +- `WebSocketManager`: Manages WebSocket connection and real-time updates +- `Syncer`: Coordinates file synchronization between local filesystem and server +- `CursorTracker`: Manages local and remote cursor positions +- `Database`: Client-side document metadata cache +- `FileOperations`: Abstraction layer for filesystem operations + +**Dual-Bundle Strategy:** +The sync-client builds two separate bundles: + +- `sync-client.web.js`: Browser-compatible UMD bundle (excludes `ws` package) +- `sync-client.node.js`: Node.js CommonJS bundle with WebSocket support + ## Development Commands +### Initial Setup + +**Node.js (requires version 25):** + +```bash +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash +nvm install 25 +nvm use 25 +nvm alias default 25 # Optional: set as system default +``` + +**Rust:** + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +cargo install sqlx-cli cargo-machete cargo-edit cargo-insta +``` + +**Frontend:** + +```bash +cd frontend +npm install +``` + ### Server Development ```bash cd sync-server cargo run config-e2e.yml # Start development server -cargo test --verbose # Run Rust tests +cargo test --verbose # Run all Rust tests +cargo test # Run specific test cargo clippy --all-targets --all-features # Lint Rust code cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged # Auto-fix clippy warnings cargo fmt --all -- --check # Check Rust formatting @@ -42,34 +95,35 @@ cargo machete --with-metadata # Detect unused dependencies cd frontend npm run dev # Start development mode (watches sync-client and obsidian-plugin) npm run build # Build all workspaces -npm run test # Run all tests -npm run lint # Lint and format TypeScript code +npm run build -w sync-client # Build specific workspace +npm run test # Run all tests across all workspaces +npm run test -w sync-client # Run tests for specific workspace +npm run lint # Lint and format TypeScript code with ESLint + Prettier ``` -### Database Setup (Development) +### Database Operations ```bash cd sync-server +# Create/reset database for development +rm -rf db.sqlite* sqlx database create --database-url sqlite://db.sqlite3 sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 cargo sqlx prepare --workspace + +# Add new migration +sqlx migrate add --source src/app_state/database/migrations +sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 ``` -### Initial Setup +### Project Scripts -```bash -# Install required cargo tools -cargo install sqlx-cli cargo-machete cargo-edit -``` - -### Scripts - -- `scripts/check.sh`: Full CI check (builds, lints, tests both server and frontend) +- `scripts/check.sh`: Full CI check (builds, lints, tests both server and frontend). **Run before pushing.** - `scripts/check.sh --fix`: Same as above but auto-fixes linting and formatting issues -- `scripts/e2e.sh`: End-to-end testing +- `scripts/e2e.sh`: End-to-end testing (e.g., `scripts/e2e.sh 8` for 8 concurrent clients) - `scripts/clean-up.sh`: Clean logs and database files -- `scripts/bump-version.sh patch`: Publish new version -- `scripts/update-api-types.sh`: Update TypeScript bindings from Rust types +- `scripts/bump-version.sh patch`: Publish new version (options: patch, minor, major) +- `scripts/update-api-types.sh`: Update TypeScript bindings from Rust types (uses ts-rs) ## Code Structure @@ -77,47 +131,78 @@ cargo install sqlx-cli cargo-machete cargo-edit The frontend uses npm workspaces with four packages: -- `sync-client`: Core synchronization logic +- `sync-client`: Core synchronization logic (builds dual bundles for web and Node.js) - `obsidian-plugin`: Obsidian-specific integration -- `test-client`: Testing utilities +- `test-client`: Testing utilities for E2E tests - `local-client-cli`: Standalone CLI for VaultLink sync client -### Type Generation +### Type Generation and API Updates -Rust structs generate TypeScript types via ts-rs crate, stored in `sync-server/bindings/` and used by frontend packages. +Rust structs generate TypeScript types via ts-rs crate: -### Key Files +1. Rust structs annotated with `#[derive(TS)]` export to `sync-server/bindings/` +2. Run `scripts/update-api-types.sh` to copy bindings to `frontend/sync-client/src/services/types/` +3. Frontend imports these types for type-safe API communication -- `sync-server/src/`: Rust server implementation with WebSocket handlers -- `frontend/sync-client/src/sync-client.ts`: Main sync client entry point -- `frontend/obsidian-plugin/src/vault-link-plugin.ts`: Main Obsidian plugin class -- `frontend/sync-client/src/services/sync-service.ts`: Core synchronization logic +### Important Implementation Details + +**SQLx Compile-Time Verification:** + +- SQLx verifies SQL queries at compile time against the database schema +- Run `cargo sqlx prepare --workspace` after schema changes to update `.sqlx/` directory +- CI builds require prepared query metadata to avoid needing a live database ## Testing ### Running Tests -- Server: `cargo test --verbose` -- Frontend: `npm run test` (runs Jest across all workspaces) -- E2E: `scripts/e2e.sh` +**Server:** + +```bash +cargo test --verbose # All tests +cargo test # Specific test +``` + +**Frontend:** + +```bash +npm run test # All workspaces +npm run test -w sync-client # Specific workspace +``` + +**E2E:** + +```bash +scripts/e2e.sh 8 # 8 concurrent clients +scripts/clean-up.sh # Clean up after tests +``` ### Test Structure -- Rust: Unit tests alongside source files -- TypeScript: `.test.ts` files using Jest -- E2E: Uses test-client to simulate multiple concurrent users +- **Rust**: Unit tests alongside source files, uses `cargo-insta` for snapshot testing +- **TypeScript**: `.test.ts` files using Node.js native test runner (not Jest) +- **E2E**: Uses `test-client` to simulate multiple concurrent users with random operations -## Code Style +## Code Style and Formatting ### Rust -- Uses extensive Clippy lints (see Cargo.toml) -- Follows pedantic linting rules +- Extensive Clippy lints (see `Cargo.toml`) +- Pedantic linting rules enabled - Forbids unsafe code -- Uses cargo fmt with default settings +- Uses `rustfmt.toml` for formatting configuration (4 spaces, Unix line endings) +- Run `cargo fmt --all` to format ### TypeScript -- Prettier configuration: 4-space tabs, trailing commas removed, LF line endings -- ESLint with unused imports plugin -- Consistent across all three frontend packages +- **Prettier**: 4-space indentation, no trailing commas, LF line endings +- **YAML/Markdown override**: 2-space indentation (via prettier config) +- **ESLint**: Strict rules with unused imports detection +- Configuration in `frontend/package.json` +- Run `npm run lint` to format and fix issues + +### EditorConfig + +- `.editorconfig` at project root defines baseline formatting rules +- `rustfmt.toml` and Prettier config explicitly mirror these settings +- Both formatters enforce: 4-space indent (2 for YAML/MD), LF endings, final newline, trim trailing whitespace diff --git a/frontend/local-client-cli/Dockerfile b/frontend/local-client-cli/Dockerfile index 695ab587..0dfa7055 100644 --- a/frontend/local-client-cli/Dockerfile +++ b/frontend/local-client-cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22-slim AS builder +FROM node:25-slim AS builder WORKDIR /build @@ -7,7 +7,7 @@ COPY . . RUN npm ci RUN npm run build -FROM node:22-alpine +FROM node:25-alpine LABEL org.opencontainers.image.title="VaultLink Local CLI" LABEL org.opencontainers.image.description="Standalone CLI for VaultLink sync client" diff --git a/frontend/local-client-cli/package.json b/frontend/local-client-cli/package.json index 0cd7da7b..cc14eaa4 100644 --- a/frontend/local-client-cli/package.json +++ b/frontend/local-client-cli/package.json @@ -11,11 +11,9 @@ "build": "webpack --mode production", "test": "tsx --test 'src/**/*.test.ts'" }, - "dependencies": { - "commander": "^14.0.2", - "watcher": "^2.3.1" - }, "devDependencies": { + "commander": "^14.0.2", + "watcher": "^2.3.1", "@types/node": "^25.0.2", "sync-client": "file:../sync-client", "ts-loader": "^9.5.4", diff --git a/frontend/local-client-cli/webpack.config.js b/frontend/local-client-cli/webpack.config.js index f8f48534..9226b9dc 100644 --- a/frontend/local-client-cli/webpack.config.js +++ b/frontend/local-client-cli/webpack.config.js @@ -2,32 +2,32 @@ const path = require("path"); const webpack = require("webpack"); module.exports = { - entry: { - cli: "./src/cli.ts", - healthcheck: "./src/healthcheck.ts" - }, - target: "node", - mode: "production", - optimization: { - minimize: false - }, - module: { - rules: [ - { - test: /\.ts$/, - use: "ts-loader" - } - ] - }, - resolve: { - extensions: [".ts", ".js"] - }, - output: { - globalObject: "this", - filename: "[name].js", - path: path.resolve(__dirname, "dist") - }, - plugins: [ - new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) + entry: { + cli: "./src/cli.ts", + healthcheck: "./src/healthcheck.ts" + }, + target: "node", + mode: "production", + optimization: { + minimize: false + }, + module: { + rules: [ + { + test: /\.ts$/, + use: "ts-loader" + } ] + }, + resolve: { + extensions: [".ts", ".js"] + }, + output: { + globalObject: "this", + filename: "[name].js", + path: path.resolve(__dirname, "dist") + }, + plugins: [ + new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) + ] }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0fc0ce40..52d4d304 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,20 +22,18 @@ }, "local-client-cli": { "version": "0.13.1", - "dependencies": { - "commander": "^14.0.2", - "watcher": "^2.3.1" - }, "bin": { "vaultlink": "dist/cli.js" }, "devDependencies": { "@types/node": "^25.0.2", + "commander": "^14.0.2", "sync-client": "file:../sync-client", "ts-loader": "^9.5.4", "tslib": "2.8.1", "tsx": "^4.21.0", "typescript": "5.9.3", + "watcher": "^2.3.1", "webpack": "^5.103.0", "webpack-cli": "^6.0.1" } @@ -1735,6 +1733,7 @@ "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" @@ -1888,6 +1887,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/dettle/-/dettle-1.0.5.tgz", "integrity": "sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==", + "dev": true, "license": "MIT" }, "node_modules/dunder-proto": { @@ -3291,6 +3291,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/promise-make-counter/-/promise-make-counter-1.0.2.tgz", "integrity": "sha512-FJAxTBWQuQoAs4ZOYuKX1FHXxEgKLEzBxUvwr4RoOglkTpOjWuM+RXsK3M9q5lMa8kjqctUrhwYeZFT4ygsnag==", + "dev": true, "license": "MIT", "dependencies": { "promise-make-naked": "^3.0.2" @@ -3300,6 +3301,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/promise-make-naked/-/promise-make-naked-3.0.2.tgz", "integrity": "sha512-B+b+kQ1YrYS7zO7P7bQcoqqMUizP06BOyNSBEnB5VJKDSWo8fsVuDkfSmwdjF0JsRtaNh83so5MMFJ95soH5jg==", + "dev": true, "license": "MIT" }, "node_modules/punycode": { @@ -3744,7 +3746,8 @@ "node_modules/stubborn-fs": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", - "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" + "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==", + "dev": true }, "node_modules/style-mod": { "version": "4.1.3", @@ -3933,6 +3936,7 @@ "version": "2.7.4", "resolved": "https://registry.npmjs.org/tiny-readdir/-/tiny-readdir-2.7.4.tgz", "integrity": "sha512-721U+zsYwDirjr8IM6jqpesD/McpZooeFi3Zc6mcjy1pse2C+v19eHPFRqz4chGXZFw7C3KITDjAtHETc2wj7Q==", + "dev": true, "license": "MIT", "dependencies": { "promise-make-counter": "^1.0.2" @@ -4218,6 +4222,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/watcher/-/watcher-2.3.1.tgz", "integrity": "sha512-d3yl+ey35h05r5EFP0TafE2jsmQUJ9cc2aernRVyAkZiu0J3+3TbNugNcqdUJDoWOfL2p+bNsN427stsBC/HnA==", + "dev": true, "dependencies": { "dettle": "^1.0.2", "stubborn-fs": "^1.2.5", @@ -4480,28 +4485,6 @@ "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, @@ -4616,8 +4599,7 @@ "uuid": "^13.0.0", "webpack": "^5.103.0", "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1", - "ws": "^8.18.3" + "webpack-merge": "^6.0.1" } }, "sync-client/node_modules/minimatch": { diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index a106d870..c3d86efb 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -26,7 +26,6 @@ "webpack": "^5.103.0", "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1", - "@sentry/browser": "^10.30.0", - "ws": "^8.18.3" + "@sentry/browser": "^10.30.0" } } 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 a3df4ea5..fc0a1ed5 100644 --- a/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts +++ b/frontend/sync-client/src/file-operations/safe-filesystem-operations.ts @@ -2,7 +2,7 @@ import type { RelativePath } from "../persistence/database"; import type { FileSystemOperations } from "./filesystem-operations"; import type { Logger } from "../tracing/logger"; import { Locks } from "../utils/data-structures/locks"; -import { FileNotFoundError } from "./file-not-found-error"; +import { FileNotFoundError } from "../errors/file-not-found-error"; import type { TextWithCursors } from "reconcile-text"; /** diff --git a/frontend/sync-client/src/services/websocket-manager.test.ts b/frontend/sync-client/src/services/websocket-manager.test.ts index fef901e7..3b61b5a1 100644 --- a/frontend/sync-client/src/services/websocket-manager.test.ts +++ b/frontend/sync-client/src/services/websocket-manager.test.ts @@ -4,8 +4,6 @@ import assert from "node:assert"; import { WebSocketManager } from "./websocket-manager"; import type { Logger } from "../tracing/logger"; import type { Settings } from "../persistence/settings"; -// eslint-disable-next-line @typescript-eslint/no-require-imports -const WebSocket = require("ws") as typeof globalThis.WebSocket; class MockCloseEvent extends Event { public code: number; @@ -91,10 +89,8 @@ function createMockFn unknown>( describe("WebSocketManager", () => { 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 }; @@ -116,7 +112,6 @@ describe("WebSocketManager", () => { it("cleans up promises after message handling", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket @@ -146,7 +141,6 @@ describe("WebSocketManager", () => { it("cleans up cursor position promises", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket @@ -176,7 +170,6 @@ describe("WebSocketManager", () => { it("logs handshake send errors", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket @@ -205,7 +198,6 @@ describe("WebSocketManager", () => { it("completes stop with timeout protection", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket @@ -220,7 +212,6 @@ describe("WebSocketManager", () => { it("clears old handlers on reconnection", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket @@ -257,7 +248,6 @@ describe("WebSocketManager", () => { it("tracks message handling promises", async () => { const manager = new WebSocketManager( - deviceId, mockLogger, mockSettings, MockWebSocket as unknown as typeof WebSocket diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 09787bce..4f47fcbe 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -31,28 +31,12 @@ export class WebSocketManager { private readonly outstandingPromises: Promise[] = []; private webSocket: WebSocket | undefined; - private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket; public constructor( - private readonly deviceId: string, private readonly logger: Logger, private readonly settings: Settings, - webSocketImplementation?: typeof globalThis.WebSocket - ) { - if (webSocketImplementation) { - this.webSocketFactoryImplementation = webSocketImplementation; - } else { - if ( - typeof globalThis !== "undefined" && - typeof globalThis.WebSocket === "undefined" - ) { - // eslint-disable-next-line - this.webSocketFactoryImplementation = require("ws"); // polyfill for WebSocket in Node.js - } else { - this.webSocketFactoryImplementation = WebSocket; - } - } - } + private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket + ) {} public get isWebSocketConnected(): boolean { return ( diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 42d70cac..5c427d7e 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -195,7 +195,6 @@ export class SyncClient { ); const webSocketManager = new WebSocketManager( - deviceId, logger, settings, webSocket diff --git a/frontend/sync-client/webpack.config.js b/frontend/sync-client/webpack.config.js index b7c3a3fd..413bfeba 100644 --- a/frontend/sync-client/webpack.config.js +++ b/frontend/sync-client/webpack.config.js @@ -49,11 +49,6 @@ module.exports = [ type: "umd" }, globalObject: "this" - }, - resolve: { - fallback: { - ws: false // Exclude `ws` from the browser bundle - } } }), merge(common, { @@ -62,10 +57,6 @@ module.exports = [ 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 } }) ]; From 439c066b57cdc6f0c5cce35dff6192650c66ec5c Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 17:08:04 +0000 Subject: [PATCH 17/25] Use rust 1.92 --- .github/workflows/check.yml | 2 +- .github/workflows/e2e.yml | 2 +- .github/workflows/publish-plugin.yml | 2 +- docs/architecture/index.md | 2 +- docs/guide/server-setup.md | 2 +- sync-server/rust-toolchain.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a5d3287a..fc1b1c99 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -29,7 +29,7 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.89.0" + toolchain: "1.92.0" components: clippy, rustfmt - name: Lint & test diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d1b8d10b..98dbfc1f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -34,7 +34,7 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.89.0" + toolchain: "1.92.0" components: clippy, rustfmt - name: Setup rust diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml index 1eaccd26..452bc601 100644 --- a/.github/workflows/publish-plugin.yml +++ b/.github/workflows/publish-plugin.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.89.0" + toolchain: "1.92.0" components: clippy, rustfmt - name: Install cross-compilation tools diff --git a/docs/architecture/index.md b/docs/architecture/index.md index f5eca5e3..bebb6c49 100644 --- a/docs/architecture/index.md +++ b/docs/architecture/index.md @@ -53,7 +53,7 @@ Central authority for synchronisation. Rust + Axum framework. **Technology**: -- **Language**: Rust 1.89+ +- **Language**: Rust 1.92+ - **Framework**: Axum (async web framework) - **Database**: SQLite with SQLx - **Protocol**: WebSockets for real-time communication diff --git a/docs/guide/server-setup.md b/docs/guide/server-setup.md index 7754da54..1848db26 100644 --- a/docs/guide/server-setup.md +++ b/docs/guide/server-setup.md @@ -75,7 +75,7 @@ chmod +x sync_server-linux-x86_64 ### Build from Source -Requirements: Rust 1.89.0+, SQLite development headers, SQLx CLI +Requirements: Rust 1.92.0+, SQLite development headers, SQLx CLI ```bash # Clone the repository diff --git a/sync-server/rust-toolchain.toml b/sync-server/rust-toolchain.toml index 010956cc..e2cf576b 100644 --- a/sync-server/rust-toolchain.toml +++ b/sync-server/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.89.0" +channel = "1.92.0" targets = [ "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", From e103bba12c5a809b9e5f984e1077703969a4e1f5 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 17:08:37 +0000 Subject: [PATCH 18/25] Don't depend on uuid --- frontend/package-lock.json | 443 +----------------- frontend/sync-client/package.json | 3 +- .../sync-client/src/utils/create-client-id.ts | 8 +- sync-server/Cargo.toml | 2 +- 4 files changed, 14 insertions(+), 442 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 52d4d304..b3610aa6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -57,262 +57,6 @@ "node": ">=14.17.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", @@ -329,150 +73,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], - "dev": true, - "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", @@ -733,7 +333,8 @@ "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" + "license": "MIT", + "peer": true }, "node_modules/@parcel/watcher": { "version": "2.5.1", @@ -942,7 +543,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.49.0", @@ -979,7 +579,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", @@ -1369,7 +968,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1413,7 +1011,6 @@ "version": "6.12.6", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1559,7 +1156,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -1781,7 +1377,8 @@ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -2042,7 +1639,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2344,21 +1940,6 @@ "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, @@ -2843,7 +2424,6 @@ "version": "8.17.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3182,7 +2762,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -3488,7 +3067,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.96.0.tgz", "integrity": "sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==", "dev": true, - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -3754,7 +3332,8 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/supports-color": { "version": "8.1.1", @@ -3853,7 +3432,6 @@ "version": "8.17.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3980,7 +3558,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "peer": true, "engines": { "node": ">=12" }, @@ -4087,7 +3664,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4216,7 +3792,8 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/watcher": { "version": "2.3.1", @@ -4247,7 +3824,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -4295,7 +3871,6 @@ "version": "6.0.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -4373,7 +3948,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4596,7 +4170,6 @@ "tslib": "2.8.1", "tsx": "^4.21.0", "typescript": "5.9.3", - "uuid": "^13.0.0", "webpack": "^5.103.0", "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1" diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index c3d86efb..682775e7 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -17,7 +17,6 @@ "minimatch": "^10.1.1", "p-queue": "^9.0.1", "reconcile-text": "^0.8.0", - "uuid": "^13.0.0", "@types/node": "^25.0.2", "ts-loader": "^9.5.4", "tslib": "2.8.1", @@ -28,4 +27,4 @@ "webpack-merge": "^6.0.1", "@sentry/browser": "^10.30.0" } -} +} \ No newline at end of file diff --git a/frontend/sync-client/src/utils/create-client-id.ts b/frontend/sync-client/src/utils/create-client-id.ts index 4f32595a..871ceb56 100644 --- a/frontend/sync-client/src/utils/create-client-id.ts +++ b/frontend/sync-client/src/utils/create-client-id.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; + export function createClientId(): string { // @ts-expect-error, injected by webpack @@ -8,8 +8,8 @@ export function createClientId(): string { typeof navigator !== "undefined" ? navigator.platform // eslint-disable-line @typescript-eslint/no-deprecated : typeof process !== "undefined" - ? process.platform - : "unknown"; + ? process.platform + : "unknown"; - return `vault-link/${packageVersion} (${uuidv4()}; ${platform})`; + return `vault-link/${packageVersion} (${Math.round(Math.random() * 1e10)}; ${platform})`; } diff --git a/sync-server/Cargo.toml b/sync-server/Cargo.toml index c60a65a2..1325c632 100644 --- a/sync-server/Cargo.toml +++ b/sync-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sync_server" -rust-version = "1.89.0" +rust-version = "1.92.0" authors = ["Andras Schmelczer "] edition = "2024" license = "MIT" From c4f992c9d6c76311e94d00b7984b83dd49dcdf1a Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 14 Dec 2025 23:30:04 +0000 Subject: [PATCH 19/25] wip --- frontend/sync-client/src/consts.ts | 4 +- .../src/file-operations/file-operations.ts | 2 +- .../sync-client/src/persistence/database.ts | 31 +++---- .../sync-client/src/services/server-config.ts | 10 +-- .../sync-client/src/services/sync-service.ts | 24 ++--- .../src/services/websocket-manager.ts | 8 +- frontend/sync-client/src/sync-client.ts | 5 +- .../src/sync-operations/cursor-tracker.ts | 2 +- .../sync-client/src/sync-operations/syncer.ts | 62 ++++--------- .../sync-operations/unrestricted-syncer.ts | 87 +++++++++++-------- frontend/sync-client/src/utils/await-all.ts | 2 +- rustfmt.toml | 11 +++ scripts/bump-version.sh | 3 +- scripts/check.sh | 9 +- scripts/update-api-types.sh | 6 +- sync-server/config-e2e.yml | 32 +++---- sync-server/src/consts.rs | 2 +- sync-server/src/errors.rs | 2 +- sync-server/src/server/create_document.rs | 57 +++++++----- sync-server/src/server/requests.rs | 10 +-- sync-server/src/server/update_document.rs | 57 +++++++++--- 21 files changed, 233 insertions(+), 193 deletions(-) create mode 100644 rustfmt.toml diff --git a/frontend/sync-client/src/consts.ts b/frontend/sync-client/src/consts.ts index da70ba47..e0a2d60e 100644 --- a/frontend/sync-client/src/consts.ts +++ b/frontend/sync-client/src/consts.ts @@ -2,5 +2,5 @@ export const TIMEOUT_FOR_MERGING_HISTORY_ENTRIES_IN_SECONDS = 60; export const DIFF_CACHE_SIZE_MB = 2; export const MAX_LOG_MESSAGE_COUNT = 100000; export const MAX_HISTORY_ENTRY_COUNT = 5000; -export const SUPPORTED_API_VERSION = 2; -export const WEBSOCKET_DISCONNECT_TIMEOUT_IN_S = 10; +export const SUPPORTED_API_VERSION = 3; +export const WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS = 10; diff --git a/frontend/sync-client/src/file-operations/file-operations.ts b/frontend/sync-client/src/file-operations/file-operations.ts index fdf65d35..863f62af 100644 --- a/frontend/sync-client/src/file-operations/file-operations.ts +++ b/frontend/sync-client/src/file-operations/file-operations.ts @@ -169,9 +169,9 @@ export class FileOperations { } await this.ensureClearPath(newPath); - this.database.move(oldPath, newPath); await this.fs.rename(oldPath, newPath); + await this.deletingEmptyParentDirectoriesOfDeletedFile(oldPath); } diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 04e0fce6..5b4e943b 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -9,6 +9,7 @@ export type DocumentId = string; export type RelativePath = string; export interface DocumentMetadata { + documentId: DocumentId; parentVersionId: VaultUpdateId; hash: string; remoteRelativePath?: RelativePath; @@ -36,7 +37,6 @@ export interface StoredDatabase { */ export interface DocumentRecord { relativePath: RelativePath; - documentId: DocumentId; metadata: DocumentMetadata | undefined; isDeleted: boolean; updates: Promise[]; @@ -57,9 +57,8 @@ export class Database { this.documents = initialState.documents?.map( - ({ relativePath, documentId, ...metadata }) => ({ + ({ relativePath, ...metadata }) => ({ relativePath, - documentId, metadata, isDeleted: false, updates: [], @@ -114,7 +113,7 @@ export class Database { i === 0 ? false : records[i - 1].parallelVersion === - current.parallelVersion + current.parallelVersion ) ) { throw new Error( @@ -127,6 +126,7 @@ export class Database { public updateDocumentMetadata( metadata: { + documentId: DocumentId; parentVersionId: VaultUpdateId; hash: string; remoteRelativePath: RelativePath; @@ -196,19 +196,18 @@ export class Database { } public createNewPendingDocument( - documentId: DocumentId, relativePath: RelativePath, promise: Promise ): DocumentRecord { this.logger.debug( - `Creating new pending document: ${relativePath} (${documentId})` + `Creating new pending document: ${relativePath}` ); const previousEntry = this.getLatestDocumentByRelativePath(relativePath); const entry = { relativePath, - documentId, + documentId: undefined, metadata: undefined, isDeleted: false, updates: [promise], @@ -231,8 +230,8 @@ export class Database { ): DocumentRecord { const entry = { relativePath, - documentId, metadata: { + documentId, parentVersionId, hash: EMPTY_HASH, remoteRelativePath: relativePath @@ -251,7 +250,7 @@ export class Database { public getDocumentByDocumentId( find: DocumentId ): DocumentRecord | undefined { - return this.documents.find(({ documentId }) => documentId === find); + return this.documents.find(({ metadata }) => metadata?.documentId === find); } public move( @@ -331,8 +330,7 @@ export class Database { public async save(): Promise { return this.saveData({ documents: this.resolvedDocuments.map( - ({ relativePath, documentId, metadata }) => ({ - documentId, + ({ relativePath, metadata }) => ({ relativePath, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...metadata! // `resolvedDocuments` only returns docs with metadata set @@ -346,9 +344,12 @@ export class Database { private ensureConsistency(): void { const idToPath = new Map(); - this.resolvedDocuments.forEach(({ relativePath, documentId }) => { - idToPath.set(documentId, [ - ...(idToPath.get(documentId) ?? []), + this.resolvedDocuments.forEach(({ relativePath, metadata }) => { + if (metadata === undefined) { + return; + } + idToPath.set(metadata.documentId, [ + ...(idToPath.get(metadata.documentId) ?? []), relativePath ]); }); @@ -360,7 +361,7 @@ export class Database { if (duplicates.length > 0) { throw new Error( "Document IDs are not unique, found duplicates: " + - duplicates.join("; ") + duplicates.join("; ") ); } } diff --git a/frontend/sync-client/src/services/server-config.ts b/frontend/sync-client/src/services/server-config.ts index b48e9802..f19b3df8 100644 --- a/frontend/sync-client/src/services/server-config.ts +++ b/frontend/sync-client/src/services/server-config.ts @@ -14,15 +14,14 @@ export class ServerConfig { private response: Promise | undefined; private config: ServerConfigData | undefined; - public constructor(private readonly syncService: SyncService) {} + public constructor(private readonly syncService: SyncService) { } private static validateConfig(config: ServerConfigData): void { if (config.supportedApiVersion !== SUPPORTED_API_VERSION) { const shouldUpgradeClient = config.supportedApiVersion > SUPPORTED_API_VERSION; throw new ServerVersionMismatchError( - `Unsupported API version: ${config.supportedApiVersion}. Consider upgrading the ${ - shouldUpgradeClient ? "client" : "sync-server" + `Unsupported API version: ${config.supportedApiVersion}. Consider upgrading the ${shouldUpgradeClient ? "client" : "sync-server" } to ensure compatibility` ); } @@ -34,11 +33,6 @@ export class ServerConfig { } } - // warm the cache - public async initialize(): Promise { - await this.getConfig(); - } - public async checkConnection(forceUpdate = false): Promise<{ isSuccessful: boolean; message: string; diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index 8dd0de68..e2259876 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -66,27 +66,29 @@ export class SyncService { } public async create({ - documentId, relativePath, - contentBytes + contentBytes, + forceMerge }: { - documentId?: DocumentId; relativePath: RelativePath; contentBytes: Uint8Array; + forceMerge?: boolean; }): Promise { return this.retryForever(async () => { const formData = new FormData(); - if (documentId !== undefined) { - formData.append("document_id", documentId); - } + formData.append("relative_path", relativePath); + if (forceMerge === true) { + formData.append("force_merge", "true"); + } + formData.append( "content", new Blob([new Uint8Array(contentBytes)]) ); this.logger.debug( - `Creating document with id ${documentId} and relative path ${relativePath}` + `Creating document with relative path ${relativePath} (forceMerge: ${forceMerge})` ); const response = await this.client(this.getUrl("/documents"), { @@ -155,8 +157,7 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${result.documentId }}` ); @@ -208,8 +209,7 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${result.documentId }}` ); @@ -336,7 +336,7 @@ export class SyncService { return this.retryForever(async () => { this.logger.debug( "Getting all documents" + - (since != null ? ` since ${since}` : "") + (since != null ? ` since ${since}` : "") ); const url = new URL(this.getUrl("/documents")); diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 4f47fcbe..bbbe8dfe 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -6,7 +6,7 @@ import type { CursorPositionFromClient } from "./types/CursorPositionFromClient" import type { ClientCursors } from "./types/ClientCursors"; import { createPromise } from "../utils/create-promise"; import type { WebSocketVaultUpdate } from "./types/WebSocketVaultUpdate"; -import { WEBSOCKET_DISCONNECT_TIMEOUT_IN_S } from "../consts"; +import { WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS } from "../consts"; import { removeFromArray } from "../utils/remove-from-array"; import { EventListeners } from "../utils/data-structures/event-listeners"; import { awaitAll } from "../utils/await-all"; @@ -36,7 +36,7 @@ export class WebSocketManager { private readonly logger: Logger, private readonly settings: Settings, private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket - ) {} + ) { } public get isWebSocketConnected(): boolean { return ( @@ -69,10 +69,10 @@ export class WebSocketManager { timeoutId = setTimeout(() => { reject( new Error( - `Timeout waiting for WebSocket to close after ${WEBSOCKET_DISCONNECT_TIMEOUT_IN_S} seconds` + `Timeout waiting for WebSocket to close after ${WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS} seconds` ) ); - }, WEBSOCKET_DISCONNECT_TIMEOUT_IN_S * 1000); + }, WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS * 1000); }); try { diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 5c427d7e..f9db6dc5 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -56,7 +56,7 @@ export class SyncClient { database: Partial; }> > - ) {} + ) { } public get documentCount(): number { return this.database.length; @@ -472,7 +472,8 @@ export class SyncClient { this.checkIfDestroyed("startSyncing"); this.fetchController.finishReset(); - await this.serverConfig.initialize(); + // warm the cache + await this.serverConfig.getConfig(); this.webSocketManager.start(); if (!this.hasStartedOfflineSync) { diff --git a/frontend/sync-client/src/sync-operations/cursor-tracker.ts b/frontend/sync-client/src/sync-operations/cursor-tracker.ts index bdd7d9b7..48b8908a 100644 --- a/frontend/sync-client/src/sync-operations/cursor-tracker.ts +++ b/frontend/sync-client/src/sync-operations/cursor-tracker.ts @@ -113,7 +113,7 @@ export class CursorTracker { documentsWithCursors.push({ relative_path: relativePath, - document_id: record.documentId, + document_id: record.metadata.documentId, vault_update_id: record.metadata.parentVersionId, cursors: cursors.map(({ start, end }) => ({ start: Math.min(start, end), diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 5b6993f9..01bba387 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -8,13 +8,12 @@ import type { SyncService } from "../services/sync-service"; import type { Logger } from "../tracing/logger"; import PQueue from "p-queue"; import { hash } from "../utils/hash"; -import { v4 as uuidv4 } from "uuid"; import type { Settings } from "../persistence/settings"; import type { FileOperations } from "../file-operations/file-operations"; import { findMatchingFile } from "../utils/find-matching-file"; import type { UnrestrictedSyncer } from "./unrestricted-syncer"; import { createPromise } from "../utils/create-promise"; -import { SyncResetError } from "../services/sync-reset-error"; +import { SyncResetError } from "../errors/sync-reset-error"; import { Locks } from "../utils/data-structures/locks"; import type { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent"; import type { WebSocketVaultUpdate } from "../services/types/WebSocketVaultUpdate"; @@ -98,9 +97,7 @@ export class Syncer { const [promise, resolve, reject] = createPromise(); - const id = uuidv4(); const document = this.database.createNewPendingDocument( - id, relativePath, promise ); @@ -171,7 +168,7 @@ export class Syncer { // in that case, we mustn't move it again. if ( this.database.getLatestDocumentByRelativePath(relativePath) === - undefined || + undefined || this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === true ) { @@ -391,8 +388,6 @@ export class Syncer { } private async internalScheduleSyncForOfflineChanges(): Promise { - await this.createFakeDocumentsFromRemoteState(); - const allLocalFiles = await this.operations.listFilesRecursively(); this.logger.info( `Scheduling sync for ${allLocalFiles.length} local files` @@ -426,9 +421,19 @@ export class Syncer { // Perhaps the file has been moved; let's check by looking at the deleted files const contentHash = await this.syncQueue.add(async () => { - const contentBytes = - await this.operations.read(relativePath); // this can throw FileNotFoundError - return hash(contentBytes); + try { + const contentBytes = + await this.operations.read(relativePath); // this can throw FileNotFoundError + return hash(contentBytes); + } catch (e) { + if ( + e instanceof Error && + e.name === "FileNotFoundError" + ) { + return undefined; + } + throw e; + } }); if (contentHash == undefined) { @@ -481,42 +486,9 @@ export class Syncer { return this.syncLocallyDeletedFile(relativePath); }) ); - } - - /** - * 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; - } - - const [allLocalFiles, remote] = await awaitAll([ - this.operations.listFilesRecursively(), - this.syncQueue.add(async () => this.syncService.getAll()) - ]); - - if (remote !== undefined) { - remote.latestDocuments - .filter( - (remoteDocument) => - allLocalFiles.includes(remoteDocument.relativePath) && - !remoteDocument.isDeleted && - this.database.getDocumentByDocumentId( - remoteDocument.documentId - ) === undefined - ) - .forEach((remoteDocument) => { - this.database.createNewEmptyDocument( - remoteDocument.documentId, - remoteDocument.vaultUpdateId, - remoteDocument.relativePath - ); - }); - } this.database.setHasInitialSyncCompleted(true); } + + } diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 5b80d75b..f277f637 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -82,9 +82,9 @@ export class UnrestrictedSyncer { const contentHash = hash(contentBytes); const response = await this.syncService.create({ - documentId: document.documentId, relativePath: originalRelativePath, - contentBytes + contentBytes, + forceMerge: !this.database.getHasInitialSyncCompleted() // don't duplicate files on first sync }); // In case a document with the same name (but different ID) had existed remotely that we haven't known about @@ -100,6 +100,7 @@ export class UnrestrictedSyncer { this.database.updateDocumentMetadata( { + documentId: response.documentId, parentVersionId: response.vaultUpdateId, hash: contentHash, remoteRelativePath: response.relativePath @@ -131,13 +132,21 @@ export class UnrestrictedSyncer { }; await this.executeSync(updateDetails, async () => { + if (document.metadata === undefined) { + this.logger.debug( + `Document ${document.relativePath} has no metadata, so it was never synced remotely` + ); + return; + } + const response = await this.syncService.delete({ - documentId: document.documentId, + documentId: document.metadata.documentId, relativePath: document.relativePath }); this.database.updateDocumentMetadata( { + ...document.metadata, parentVersionId: response.vaultUpdateId, hash: EMPTY_HASH, remoteRelativePath: document.relativePath @@ -170,14 +179,14 @@ export class UnrestrictedSyncer { const updateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined ? { - type: SyncType.MOVE, - relativePath: document.relativePath, - movedFrom: oldPath - } + type: SyncType.MOVE, + relativePath: document.relativePath, + movedFrom: oldPath + } : { - type: SyncType.UPDATE, - relativePath: document.relativePath - }; + type: SyncType.UPDATE, + relativePath: document.relativePath + }; await this.executeSync(updateDetails, async () => { const originalRelativePath = document.relativePath; @@ -216,22 +225,22 @@ export class UnrestrictedSyncer { response = isText && cachedVersion !== undefined ? await this.syncService.putText({ - documentId: document.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - content: diff( - new TextDecoder().decode(cachedVersion), - new TextDecoder().decode(contentBytes) - ) - }) + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + content: diff( + new TextDecoder().decode(cachedVersion), + new TextDecoder().decode(contentBytes) + ) + }) : await this.syncService.putBinary({ - documentId: document.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - contentBytes - }); + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + contentBytes + }); } else { if (!force) { this.logger.debug( @@ -241,7 +250,7 @@ export class UnrestrictedSyncer { } response = await this.syncService.get({ - documentId: document.documentId + documentId: document.metadata.documentId }); } @@ -290,6 +299,7 @@ export class UnrestrictedSyncer { this.database.updateDocumentMetadata( { + ...document.metadata, parentVersionId: response.vaultUpdateId, hash: contentHash, remoteRelativePath: response.relativePath @@ -317,6 +327,7 @@ export class UnrestrictedSyncer { } else { this.database.updateDocumentMetadata( { + ...document.metadata, parentVersionId: response.vaultUpdateId, hash: contentHash, remoteRelativePath: response.relativePath @@ -334,16 +345,16 @@ export class UnrestrictedSyncer { const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined || - response.relativePath != originalRelativePath + response.relativePath != originalRelativePath ? { - type: SyncType.MOVE, - relativePath: response.relativePath, - movedFrom: originalRelativePath - } + type: SyncType.MOVE, + relativePath: response.relativePath, + movedFrom: originalRelativePath + } : { - type: SyncType.UPDATE, - relativePath: response.relativePath - }; + type: SyncType.UPDATE, + relativePath: response.relativePath + }; if (areThereLocalChanges) { this.history.addHistoryEntry({ @@ -437,12 +448,12 @@ export class UnrestrictedSyncer { const [promise, resolve] = createPromise(); this.database.updateDocumentMetadata( { + documentId: remoteVersion.documentId, parentVersionId: remoteVersion.vaultUpdateId, hash: hash(contentBytes), remoteRelativePath: remoteVersion.relativePath }, this.database.createNewPendingDocument( - remoteVersion.documentId, remoteVersion.relativePath, promise ) @@ -541,9 +552,8 @@ export class UnrestrictedSyncer { type: SyncType.SKIPPED, relativePath }, - message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${ - maxFileSizeMB - } MB` + message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${maxFileSizeMB + } MB` }; } } @@ -582,6 +592,7 @@ export class UnrestrictedSyncer { this.database.delete(document.relativePath); this.database.updateDocumentMetadata( { + documentId: response.documentId, parentVersionId: response.vaultUpdateId, hash: EMPTY_HASH, remoteRelativePath: response.relativePath diff --git a/frontend/sync-client/src/utils/await-all.ts b/frontend/sync-client/src/utils/await-all.ts index 9406a6b8..43e06ce6 100644 --- a/frontend/sync-client/src/utils/await-all.ts +++ b/frontend/sync-client/src/utils/await-all.ts @@ -9,7 +9,7 @@ type ResolvedTuple = { export const awaitAll = async ( promises: PromiseTuple ): Promise> => { - // eslint-disable-next-line no-restricted-properties + // eslint-disable-next-line no-restricted-properties, @typescript-eslint/await-thenable const result = await Promise.allSettled(promises); for (const res of result) { if (res.status === "rejected") { diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..a9107050 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,11 @@ +# Rustfmt configuration +# This should match the .editorconfig settings + +# Use spaces for indentation (matches .editorconfig indent_style = space) +hard_tabs = false + +# Use 4 spaces for indentation (matches .editorconfig indent_size = 4) +tab_spaces = 4 + +# Use Unix line endings (matches .editorconfig end_of_line = lf) +newline_style = "Unix" diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index fb953e2a..bea3d982 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -35,7 +35,8 @@ cd .. cp frontend/obsidian-plugin/manifest.json manifest.json # for BRAT, otherwise it wouldn't update -git ls-files | xargs npx eclint fix +# Format all files across the project (frontend and backend) +npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" # Commit and tag git add . diff --git a/scripts/check.sh b/scripts/check.sh index 7c3c87e5..f7a3aa57 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -45,10 +45,11 @@ cd frontend npm run build npm run test npm run lint +cd .. -# Use git ls-files to only check tracked files, respecting .gitignore -# We always run in fix mode and then check with git status -git ls-files | xargs npx eclint fix +# Format all files across the project (frontend and backend) +# Prettier respects .gitignore by default +npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" if [[ "$FIX_MODE" == false ]] && [[ $(git status --porcelain) ]]; then git status --porcelain @@ -56,6 +57,4 @@ if [[ "$FIX_MODE" == false ]] && [[ $(git status --porcelain) ]]; then exit 1 fi -cd .. - echo "Success" diff --git a/scripts/update-api-types.sh b/scripts/update-api-types.sh index 4b947ee8..36ca100d 100755 --- a/scripts/update-api-types.sh +++ b/scripts/update-api-types.sh @@ -12,5 +12,7 @@ cp -r sync-server/bindings/* frontend/sync-client/src/services/types/ cd frontend npm run lint -git ls-files | xargs npx eclint fix -cd - +cd .. + +# Format all files across the project (frontend and backend) +npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index e9d47559..1f235b01 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days diff --git a/sync-server/src/consts.rs b/sync-server/src/consts.rs index 98ed1c1f..9e9890c0 100644 --- a/sync-server/src/consts.rs +++ b/sync-server/src/consts.rs @@ -20,4 +20,4 @@ pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; pub const DEFAULT_MERGEABLE_FILE_EXTENSIONS: &[&str] = &["md", "txt"]; -pub const SUPPORTED_API_VERSION: u32 = 2; +pub const SUPPORTED_API_VERSION: u32 = 3; diff --git a/sync-server/src/errors.rs b/sync-server/src/errors.rs index 831b0e86..c505b8ae 100644 --- a/sync-server/src/errors.rs +++ b/sync-server/src/errors.rs @@ -5,7 +5,7 @@ use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; -use log::{debug, error}; +use log::debug; use serde::Serialize; use thiserror::Error; use ts_rs::TS; diff --git a/sync-server/src/server/create_document.rs b/sync-server/src/server/create_document.rs index 859c0db4..20f67193 100644 --- a/sync-server/src/server/create_document.rs +++ b/sync-server/src/server/create_document.rs @@ -11,10 +11,11 @@ use super::{device_id_header::DeviceIdHeader, requests::CreateDocumentVersion}; use crate::{ app_state::{ AppState, - database::models::{DocumentVersionWithoutContent, StoredDocumentVersion, VaultId}, + database::models::{StoredDocumentVersion, VaultId}, }, config::user_config::User, - errors::{SyncServerError, client_error, server_error}, + errors::{SyncServerError, server_error}, + server::{responses::DocumentUpdateResponse, update_document::merge_with_stored_version}, utils::{ find_first_available_path::find_first_available_path, normalize::normalize, sanitize_path::sanitize_path, @@ -37,7 +38,7 @@ pub async fn create_document( TypedHeader(device_id): TypedHeader, State(state): State, TypedMultipart(request): TypedMultipart, -) -> Result, SyncServerError> { +) -> Result, SyncServerError> { debug!("Creating document in vault `{vault_id}`"); let mut transaction = state @@ -46,24 +47,39 @@ pub async fn create_document( .await .map_err(server_error)?; - let document_id = match request.document_id { - Some(document_id) => { - let existing_version = state - .database - .get_latest_document(&vault_id, &document_id, Some(&mut transaction)) - .await - .map_err(server_error)?; + let sanitized_relative_path = sanitize_path(&request.relative_path); - if existing_version.is_some() { - return Err(client_error(anyhow::anyhow!( - "Document with the same ID `{document_id}` already exists" - ))); - } + if request.force_merge.unwrap_or_default() { + let latest_version = state + .database + .get_latest_document_by_path( + &vault_id, + &sanitized_relative_path, + Some(&mut transaction), + ) + .await + .map_err(server_error)?; + if let Some(latest_version) = latest_version { + info!( + "Document already exists at new location: `{sanitized_relative_path}` when trying to create it in vault `{vault_id}`, merging into existing document" + ); - document_id + return merge_with_stored_version( + latest_version.clone(), + latest_version, + vault_id, + user, + device_id, + state, + &sanitized_relative_path, + request.content.contents.to_vec(), + transaction, + ) + .await; } - None => uuid::Uuid::new_v4(), - }; + } + + let document_id = uuid::Uuid::new_v4(); let last_update_id = state .database @@ -71,7 +87,6 @@ pub async fn create_document( .await .map_err(server_error)?; - let sanitized_relative_path = sanitize_path(&request.relative_path); let deduped_path = find_first_available_path( &vault_id, &sanitized_relative_path, @@ -105,5 +120,7 @@ pub async fn create_document( .await .map_err(server_error)?; - Ok(Json(new_version.into())) + Ok(Json(DocumentUpdateResponse::FastForwardUpdate( + new_version.into(), + ))) } diff --git a/sync-server/src/server/requests.rs b/sync-server/src/server/requests.rs index 119ad467..574823f5 100644 --- a/sync-server/src/server/requests.rs +++ b/sync-server/src/server/requests.rs @@ -4,18 +4,16 @@ use reconcile_text::NumberOrText; use serde::{self, Deserialize}; use ts_rs::TS; -use crate::app_state::database::models::{DocumentId, VaultUpdateId}; +use crate::app_state::database::models::VaultUpdateId; #[derive(TS, Debug, TryFromMultipart)] #[ts(export)] pub struct 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. - pub document_id: Option, pub relative_path: String, + // whether to merge with existing document at the same path if it exists + pub force_merge: Option, + #[ts(as = "Vec")] #[form_data(limit = "unlimited")] pub content: FieldData, diff --git a/sync-server/src/server/update_document.rs b/sync-server/src/server/update_document.rs index 00fbd008..c24b62c9 100644 --- a/sync-server/src/server/update_document.rs +++ b/sync-server/src/server/update_document.rs @@ -16,7 +16,10 @@ use super::{ use crate::{ app_state::{ AppState, - database::models::{DocumentId, StoredDocumentVersion, VaultId, VaultUpdateId}, + database::{ + Transaction, + models::{DocumentId, StoredDocumentVersion, VaultId, VaultUpdateId}, + }, }, config::user_config::User, errors::{SyncServerError, client_error, not_found_error, server_error}, @@ -141,12 +144,6 @@ async fn update_document( .await .map_err(server_error)?; - let last_update_id = state - .database - .get_max_update_id_in_vault(&vault_id, Some(&mut transaction)) - .await - .map_err(server_error)?; - let latest_version = state .database .get_latest_document(&vault_id, &document_id, Some(&mut transaction)) @@ -174,12 +171,39 @@ async fn update_document( ))); } + merge_with_stored_version( + parent_document, + latest_version, + vault_id, + user, + device_id, + state, + &sanitized_relative_path, + content, + transaction, + ) + .await +} + +#[allow(clippy::too_many_arguments)] +pub async fn merge_with_stored_version( + parent_document: StoredDocumentVersion, + latest_version: StoredDocumentVersion, + vault_id: VaultId, + user: User, + device_id: DeviceIdHeader, + state: AppState, + sanitized_relative_path: &str, + content: Vec, + mut transaction: Transaction<'_>, +) -> Result, SyncServerError> { // Return the latest version if the content and path are the same as the latest // version if content == latest_version.content && sanitized_relative_path == latest_version.relative_path { info!( - "Document content is the same as the latest version for `{document_id}`, skipping update" + "Document content is the same as the latest version for `{}`, skipping update", + parent_document.document_id ); transaction .rollback() @@ -193,14 +217,17 @@ async fn update_document( } let are_all_participants_mergable = is_file_type_mergable( - &sanitized_relative_path, + sanitized_relative_path, &state.config.server.mergeable_file_extensions, ) && !is_binary(&parent_document.content) && !is_binary(&latest_version.content) && !is_binary(&content); let merged_content = if are_all_participants_mergable { - info!("Merging changes for document `{document_id}` in vault `{vault_id}`"); + info!( + "Merging changes for document `{}` in vault `{vault_id}`", + parent_document.document_id + ); reconcile( str::from_utf8(&parent_document.content) .expect("parent must be valid UTF-8 because it's not binary"), @@ -227,7 +254,7 @@ async fn update_document( { let new_path = find_first_available_path( &vault_id, - &sanitized_relative_path, + sanitized_relative_path, &state.database, &mut transaction, ) @@ -245,8 +272,14 @@ async fn update_document( latest_version.relative_path.clone() }; + let last_update_id = state + .database + .get_max_update_id_in_vault(&vault_id, Some(&mut transaction)) + .await + .map_err(server_error)?; + let new_version = StoredDocumentVersion { - document_id, + document_id: parent_document.document_id, vault_update_id: last_update_id + 1, relative_path: new_relative_path, content: merged_content, From 0d7d36e97180eb2f1adde8825a41ed8479cbc1bd Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 4 Jan 2026 11:02:00 +0000 Subject: [PATCH 20/25] Formatting & small fixes --- .github/workflows/e2e.yml | 2 +- frontend/sync-client/package.json | 2 +- .../sync-client/src/services/server-config.ts | 5 +-- .../sync-client/src/services/sync-service.ts | 6 ++-- .../services/types/CreateDocumentVersion.ts | 8 +---- .../src/services/websocket-manager.ts | 2 +- frontend/sync-client/src/sync-client.ts | 3 +- .../sync-client/src/sync-operations/syncer.ts | 5 --- .../sync-client/src/utils/create-client-id.ts | 6 ++-- .../src/utils/data-structures/locks.test.ts | 4 +-- .../src/utils/data-structures/locks.ts | 2 +- frontend/test-client/src/agent/mock-client.ts | 22 +++++-------- scripts/check.sh | 5 ++- scripts/e2e.sh | 9 ------ sync-server/config-e2e.yml | 32 +++++++++---------- sync-server/src/server/requests.rs | 2 +- sync-server/src/server/update_document.rs | 4 +-- 17 files changed, 47 insertions(+), 72 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 090b0921..98dbfc1f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -6,7 +6,7 @@ on: pull_request: branches: ["main"] schedule: - - cron: '0 * * * *' + - cron: "0 * * * *" workflow_dispatch: concurrency: diff --git a/frontend/sync-client/package.json b/frontend/sync-client/package.json index 57f3f409..45c33764 100644 --- a/frontend/sync-client/package.json +++ b/frontend/sync-client/package.json @@ -27,4 +27,4 @@ "webpack-merge": "^6.0.1", "@sentry/browser": "^10.30.0" } -} \ No newline at end of file +} diff --git a/frontend/sync-client/src/services/server-config.ts b/frontend/sync-client/src/services/server-config.ts index f19b3df8..da804b2f 100644 --- a/frontend/sync-client/src/services/server-config.ts +++ b/frontend/sync-client/src/services/server-config.ts @@ -14,14 +14,15 @@ export class ServerConfig { private response: Promise | undefined; private config: ServerConfigData | undefined; - public constructor(private readonly syncService: SyncService) { } + public constructor(private readonly syncService: SyncService) {} private static validateConfig(config: ServerConfigData): void { if (config.supportedApiVersion !== SUPPORTED_API_VERSION) { const shouldUpgradeClient = config.supportedApiVersion > SUPPORTED_API_VERSION; throw new ServerVersionMismatchError( - `Unsupported API version: ${config.supportedApiVersion}. Consider upgrading the ${shouldUpgradeClient ? "client" : "sync-server" + `Unsupported API version: ${config.supportedApiVersion}. Consider upgrading the ${ + shouldUpgradeClient ? "client" : "sync-server" } to ensure compatibility` ); } diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index e2259876..82303bce 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -73,7 +73,7 @@ export class SyncService { relativePath: RelativePath; contentBytes: Uint8Array; forceMerge?: boolean; - }): Promise { + }): Promise { return this.retryForever(async () => { const formData = new FormData(); @@ -105,8 +105,8 @@ export class SyncService { ); } - const result: DocumentVersionWithoutContent = - (await response.json()) as DocumentVersionWithoutContent; // 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(`Created document ${JSON.stringify(result)}`); diff --git a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts index 4bae0e50..6de30bd8 100644 --- a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts @@ -1,13 +1,7 @@ // 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; + force_merge: boolean | null; content: number[]; } diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index bbbe8dfe..71bd083e 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -36,7 +36,7 @@ export class WebSocketManager { private readonly logger: Logger, private readonly settings: Settings, private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket - ) { } + ) {} public get isWebSocketConnected(): boolean { return ( diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index f9db6dc5..9e368a7b 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -56,7 +56,7 @@ export class SyncClient { database: Partial; }> > - ) { } + ) {} public get documentCount(): number { return this.database.length; @@ -205,7 +205,6 @@ export class SyncClient { logger, database, settings, - syncService, webSocketManager, fileOperations, unrestrictedSyncer diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 01bba387..e4017ca5 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -4,7 +4,6 @@ import type { DocumentRecord, RelativePath } from "../persistence/database"; -import type { SyncService } from "../services/sync-service"; import type { Logger } from "../tracing/logger"; import PQueue from "p-queue"; import { hash } from "../utils/hash"; @@ -41,7 +40,6 @@ export class Syncer { private readonly logger: Logger, private readonly database: Database, private readonly settings: Settings, - private readonly syncService: SyncService, private readonly webSocketManager: WebSocketManager, private readonly operations: FileOperations, private readonly internalSyncer: UnrestrictedSyncer @@ -487,8 +485,5 @@ export class Syncer { }) ); - this.database.setHasInitialSyncCompleted(true); } - - } diff --git a/frontend/sync-client/src/utils/create-client-id.ts b/frontend/sync-client/src/utils/create-client-id.ts index 871ceb56..03dc2ae9 100644 --- a/frontend/sync-client/src/utils/create-client-id.ts +++ b/frontend/sync-client/src/utils/create-client-id.ts @@ -1,5 +1,3 @@ - - export function createClientId(): string { // @ts-expect-error, injected by webpack const packageVersion = __CURRENT_VERSION__; // eslint-disable-line @@ -8,8 +6,8 @@ export function createClientId(): string { typeof navigator !== "undefined" ? navigator.platform // eslint-disable-line @typescript-eslint/no-deprecated : typeof process !== "undefined" - ? process.platform - : "unknown"; + ? process.platform + : "unknown"; return `vault-link/${packageVersion} (${Math.round(Math.random() * 1e10)}; ${platform})`; } 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 f8cd589e..d1dcc6d7 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.test.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.test.ts @@ -252,7 +252,7 @@ describe("reset", () => { await sleep(1); const secondPromise = locks.withLock(testPath, async () => "second"); - void secondPromise.catch(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function + void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function locks.reset(); @@ -273,7 +273,7 @@ describe("reset", () => { await sleep(1); const secondPromise = locks.withLock(testPath, async () => "second"); - void secondPromise.catch(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function + void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function locks.reset(); diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index 324117e2..743e8c6a 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -18,7 +18,7 @@ export class Locks { [() => unknown, (err: unknown) => 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. diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index c814879a..240869bb 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -14,13 +14,7 @@ export class MockClient implements FileSystemOperations { 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, @@ -108,13 +102,13 @@ export class MockClient implements FileSystemOperations { .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}` - ); - } + // all changes should be additive + { + assert( + newParts.includes(part), + `Part ${part} not found in new content: ${newContent}` + ); + } ); } diff --git a/scripts/check.sh b/scripts/check.sh index f7a3aa57..2ee0dd62 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -30,8 +30,11 @@ fi which cargo-machete || cargo install cargo-machete cargo machete --with-metadata +cd .. +scripts/update-api-types.sh # this will dirty up the git state if not up-to-date + echo "Running checks in frontend" -cd ../frontend +cd frontend if [[ "$FIX_MODE" == true ]]; then npm install diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 6c66e835..d0e23260 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -25,15 +25,6 @@ npm run build ../scripts/utils/wait-for-server.sh -cd .. -scripts/update-api-types.sh -if [[ $(git status --porcelain) ]]; then - git status --porcelain - echo "Failing CI because the working directory is not clean after generating api types" - exit 1 -fi -cd frontend - pids=() for i in $(seq 1 $process_count); do # Create a named pipe for this process diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index 1f235b01..e9d47559 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days diff --git a/sync-server/src/server/requests.rs b/sync-server/src/server/requests.rs index 574823f5..400ececf 100644 --- a/sync-server/src/server/requests.rs +++ b/sync-server/src/server/requests.rs @@ -11,7 +11,7 @@ use crate::app_state::database::models::VaultUpdateId; pub struct CreateDocumentVersion { pub relative_path: String, - // whether to merge with existing document at the same path if it exists + // whether to merge with existing document at the same path if it already exists pub force_merge: Option, #[ts(as = "Vec")] diff --git a/sync-server/src/server/update_document.rs b/sync-server/src/server/update_document.rs index c24b62c9..2be40cd3 100644 --- a/sync-server/src/server/update_document.rs +++ b/sync-server/src/server/update_document.rs @@ -246,8 +246,6 @@ pub async fn merge_with_stored_version( content.clone() }; - let is_different_from_request_content = merged_content != content; - // We can only update the relative path if we're the first one to do so let new_relative_path = if parent_document.relative_path == latest_version.relative_path && latest_version.relative_path != sanitized_relative_path @@ -278,6 +276,8 @@ pub async fn merge_with_stored_version( .await .map_err(server_error)?; + let is_different_from_request_content = merged_content != content; + let new_version = StoredDocumentVersion { document_id: parent_document.document_id, vault_update_id: last_update_id + 1, From 7c991c3b4d51bd05c84ff71bbbd3fb65ebb0074b Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 4 Jan 2026 14:08:33 +0000 Subject: [PATCH 21/25] Fix syncing logic --- frontend/sync-client/src/consts.ts | 1 + .../sync-client/src/persistence/database.ts | 42 +-- .../src/services/websocket-manager.ts | 45 +++- frontend/sync-client/src/sync-client.ts | 6 +- .../sync-operations/unrestricted-syncer.ts | 247 +++++++++--------- sync-server/config-e2e.yml | 32 +-- sync-server/src/app_state/database.rs | 3 +- sync-server/src/server/create_document.rs | 5 +- sync-server/src/server/update_document.rs | 18 +- .../src/utils/find_first_available_path.rs | 8 +- 10 files changed, 223 insertions(+), 184 deletions(-) diff --git a/frontend/sync-client/src/consts.ts b/frontend/sync-client/src/consts.ts index e0a2d60e..9e4fa7d2 100644 --- a/frontend/sync-client/src/consts.ts +++ b/frontend/sync-client/src/consts.ts @@ -4,3 +4,4 @@ export const MAX_LOG_MESSAGE_COUNT = 100000; export const MAX_HISTORY_ENTRY_COUNT = 5000; export const SUPPORTED_API_VERSION = 3; export const WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS = 10; +export const WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS = 10; diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 5b4e943b..a5daf876 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -26,7 +26,6 @@ export interface StoredDocumentMetadata { export interface StoredDatabase { documents: StoredDocumentMetadata[]; lastSeenUpdateId: VaultUpdateId | undefined; - hasInitialSyncCompleted: boolean; } /** @@ -46,7 +45,6 @@ export interface DocumentRecord { export class Database { private documents: DocumentRecord[]; private lastSeenUpdateIds: CoveredValues; - private hasInitialSyncCompleted: boolean; public constructor( private readonly logger: Logger, @@ -56,15 +54,13 @@ export class Database { initialState ??= {}; this.documents = - initialState.documents?.map( - ({ relativePath, ...metadata }) => ({ - relativePath, - metadata, - isDeleted: false, - updates: [], - parallelVersion: 0 - }) - ) ?? []; + initialState.documents?.map(({ relativePath, ...metadata }) => ({ + relativePath, + metadata, + isDeleted: false, + updates: [], + parallelVersion: 0 + })) ?? []; this.ensureConsistency(); this.logger.debug(`Loaded ${this.documents.length} documents`); @@ -79,11 +75,6 @@ export class Database { this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId); }); - this.hasInitialSyncCompleted = - initialState.hasInitialSyncCompleted ?? false; - this.logger.debug( - `Loaded hasInitialSyncCompleted: ${this.hasInitialSyncCompleted}` - ); } public get length(): number { @@ -199,15 +190,12 @@ export class Database { relativePath: RelativePath, promise: Promise ): DocumentRecord { - this.logger.debug( - `Creating new pending document: ${relativePath}` - ); + this.logger.debug(`Creating new pending document: ${relativePath}`); const previousEntry = this.getLatestDocumentByRelativePath(relativePath); const entry = { relativePath, - documentId: undefined, metadata: undefined, isDeleted: false, updates: [promise], @@ -250,7 +238,9 @@ export class Database { public getDocumentByDocumentId( find: DocumentId ): DocumentRecord | undefined { - return this.documents.find(({ metadata }) => metadata?.documentId === find); + return this.documents.find( + ({ metadata }) => metadata?.documentId === find + ); } public move( @@ -292,14 +282,6 @@ export class Database { candidate.isDeleted = true; } - public getHasInitialSyncCompleted(): boolean { - return this.hasInitialSyncCompleted; - } - - public setHasInitialSyncCompleted(value: boolean): void { - this.hasInitialSyncCompleted = value; - this.saveInTheBackground(); - } public getLastSeenUpdateId(): VaultUpdateId { return this.lastSeenUpdateIds.min; @@ -323,7 +305,6 @@ export class Database { this.lastSeenUpdateIds = new CoveredValues( 0 // the first updateId will be 1 which is the first integer after -1 ); - this.hasInitialSyncCompleted = false; this.saveInTheBackground(); } @@ -337,7 +318,6 @@ export class Database { }) ), lastSeenUpdateId: this.lastSeenUpdateIds.min, - hasInitialSyncCompleted: this.hasInitialSyncCompleted }); } diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 71bd083e..266047ce 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -6,7 +6,10 @@ import type { CursorPositionFromClient } from "./types/CursorPositionFromClient" import type { ClientCursors } from "./types/ClientCursors"; import { createPromise } from "../utils/create-promise"; import type { WebSocketVaultUpdate } from "./types/WebSocketVaultUpdate"; -import { WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS } from "../consts"; +import { + WEBSOCKET_DISCONNECT_TIMEOUT_IN_SECONDS, + WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS +} from "../consts"; import { removeFromArray } from "../utils/remove-from-array"; import { EventListeners } from "../utils/data-structures/event-listeners"; import { awaitAll } from "../utils/await-all"; @@ -27,6 +30,7 @@ export class WebSocketManager { private isStopped = true; private resolveDisconnectingPromise: null | (() => unknown) = null; private reconnectTimeoutId: ReturnType | undefined; + private connectionTimeoutId: ReturnType | undefined; private readonly outstandingPromises: Promise[] = []; @@ -36,7 +40,7 @@ export class WebSocketManager { private readonly logger: Logger, private readonly settings: Settings, private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket - ) {} + ) { } public get isWebSocketConnected(): boolean { return ( @@ -61,6 +65,11 @@ export class WebSocketManager { this.reconnectTimeoutId = undefined; } + if (this.connectionTimeoutId !== undefined) { + clearTimeout(this.connectionTimeoutId); + this.connectionTimeoutId = undefined; + } + this.webSocket?.close(1000, "WebSocketManager has been stopped"); // eslint-disable-next-line @typescript-eslint/init-declarations @@ -171,7 +180,22 @@ export class WebSocketManager { this.webSocket = new this.webSocketFactoryImplementation(wsUri); + // Set connection timeout to handle cases where server is down and the WebSocket connection won't open + this.connectionTimeoutId = setTimeout(() => { + this.connectionTimeoutId = undefined; + this.logger.warn( + `WebSocket connection timeout after ${WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS} seconds` + ); + // Force close to trigger onclose handler which will schedule reconnection + this.webSocket?.close(); + }, WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS * 1000); + this.webSocket.onopen = (): void => { + if (this.connectionTimeoutId !== undefined) { + clearTimeout(this.connectionTimeoutId); + this.connectionTimeoutId = undefined; + } + // Check if we've been stopped while connecting if (this.isStopped) { this.webSocket?.close( @@ -215,7 +239,18 @@ export class WebSocketManager { } }; + this.webSocket.onerror = (error): void => { + this.logger.error( + `WebSocket error occurred: ${error instanceof ErrorEvent ? error.message : "Unknown error"}` + ); + }; + this.webSocket.onclose = (event): void => { + if (this.connectionTimeoutId !== undefined) { + clearTimeout(this.connectionTimeoutId); + this.connectionTimeoutId = undefined; + } + this.logger.warn( `WebSocket closed with code ${event.code} (${event.reason == "" ? "unknown reason" : event.reason})` ); @@ -225,10 +260,14 @@ export class WebSocketManager { this.resolveDisconnectingPromise?.(); this.resolveDisconnectingPromise = null; } else { + const delay = this.settings.getSettings().webSocketRetryIntervalMs; + this.logger.info( + `Reconnecting to WebSocket in ${delay}ms...` + ); this.reconnectTimeoutId = setTimeout(() => { this.reconnectTimeoutId = undefined; this.initializeWebSocket(); - }, this.settings.getSettings().webSocketRetryIntervalMs); + }, delay); } }; } diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 9e368a7b..dfcfc3e4 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -56,7 +56,7 @@ export class SyncClient { database: Partial; }> > - ) {} + ) { } public get documentCount(): number { return this.database.length; @@ -339,7 +339,9 @@ export class SyncClient { this.hasFinishedOfflineSync = false; this.serverConfig.reset(); - await this.startSyncing(); + if (this.settings.getSettings().isSyncEnabled) { + await this.startSyncing(); + } } public getSettings(): SyncSettings { diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index f277f637..4ea1eda0 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -84,36 +84,16 @@ export class UnrestrictedSyncer { const response = await this.syncService.create({ relativePath: originalRelativePath, contentBytes, - forceMerge: !this.database.getHasInitialSyncCompleted() // don't duplicate files on first sync + forceMerge: true }); - // In case a document with the same name (but different ID) had existed remotely that we haven't known about - if (response.relativePath != originalRelativePath) { - this.logger.debug( - `Document ${originalRelativePath} has been created remotely at a different path: ${response.relativePath}, moving it locally` - ); - await this.operations.move( - document.relativePath, - response.relativePath - ); // this can throw FileNotFoundError - } - - this.database.updateDocumentMetadata( - { - documentId: response.documentId, - parentVersionId: response.vaultUpdateId, - hash: contentHash, - remoteRelativePath: response.relativePath - }, - document - ); - - this.database.addSeenUpdateId(response.vaultUpdateId); - await this.updateCache( - response.vaultUpdateId, - contentBytes, - response.relativePath - ); + this.handleMaybeMergingResponse({ + document, + response, + contentHash, + originalRelativePath, + originalContentBytes: contentBytes + }); this.history.addHistoryEntry({ status: SyncStatus.SUCCESS, @@ -134,7 +114,7 @@ export class UnrestrictedSyncer { await this.executeSync(updateDetails, async () => { if (document.metadata === undefined) { this.logger.debug( - `Document ${document.relativePath} has no metadata, so it was never synced remotely` + `Document ${document.relativePath} has no metadata, so it has never got synced remotely; no need to delete it remotely` ); return; } @@ -254,69 +234,16 @@ export class UnrestrictedSyncer { }); } - // `document` is mutable and reflects the latest state in the local database - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (document.isDeleted) { - this.logger.info( - `Document ${document.relativePath} has been deleted before we could finish updating it` - ); - this.database.addSeenUpdateId(response.vaultUpdateId); - return; - } - if ( - // `Syncer` creates fake local document metadata for all remote docs with invalid hashes. The parent IDs will likely match - // the latest versions so we still need to update the local versions to turn the fakes into real metadata. - document.metadata.parentVersionId > response.vaultUpdateId - ) { - this.logger.debug( - `Document ${document.relativePath} is already more up to date than the fetched version` - ); - this.database.addSeenUpdateId(response.vaultUpdateId); // in case the previous `vaultUpdateId` update hasn't made it through - return; - } - - if (response.isDeleted) { - return this.applyRemoteDeleteLocally(document, response); - } - - let actualPath = document.relativePath; - - if (response.relativePath != originalRelativePath) { - actualPath = response.relativePath; - // Make sure to update the remote relative path to avoid uploading - // the file as a result of this filesystem event. - document.metadata.remoteRelativePath = response.relativePath; - await this.operations.move( - document.relativePath, - response.relativePath - ); // this can throw FileNotFoundError - } + this.handleMaybeMergingResponse({ + document, + response: response!, + contentHash, + originalRelativePath, + originalContentBytes: contentBytes + }); if (!("type" in response) || response.type === "MergingUpdate") { - const responseBytes = base64ToBytes(response.contentBase64); - contentHash = hash(responseBytes); - - this.database.updateDocumentMetadata( - { - ...document.metadata, - parentVersionId: response.vaultUpdateId, - hash: contentHash, - remoteRelativePath: response.relativePath - }, - document - ); - await this.operations.write( - actualPath, - contentBytes, - responseBytes - ); - await this.updateCache( - response.vaultUpdateId, - responseBytes, - actualPath - ); - if (!force) { this.history.addHistoryEntry({ status: SyncStatus.SUCCESS, @@ -324,32 +251,15 @@ export class UnrestrictedSyncer { message: `The file we updated had been updated remotely, so we downloaded the merged version` }); } - } else { - this.database.updateDocumentMetadata( - { - ...document.metadata, - parentVersionId: response.vaultUpdateId, - hash: contentHash, - remoteRelativePath: response.relativePath - }, - document - ); - await this.updateCache( - response.vaultUpdateId, - contentBytes, - actualPath - ); } - this.database.addSeenUpdateId(response.vaultUpdateId); - const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined || response.relativePath != originalRelativePath ? { type: SyncType.MOVE, relativePath: response.relativePath, - movedFrom: originalRelativePath + movedFrom: oldPath ?? originalRelativePath } : { type: SyncType.UPDATE, @@ -363,7 +273,7 @@ export class UnrestrictedSyncer { message: `Successfully uploaded locally updated file to the server`, author: response.userId }); - } else { + } else if (!response.isDeleted) { this.history.addHistoryEntry({ status: SyncStatus.SUCCESS, details: actualUpdateDetails, @@ -371,6 +281,17 @@ export class UnrestrictedSyncer { author: response.userId, timestamp: new Date(response.updatedDate) }); + } else { + this.history.addHistoryEntry({ + status: SyncStatus.SUCCESS, + details: { + type: SyncType.DELETE, + relativePath: document.relativePath + }, + message: "File has been deleted remotely, so we deleted it locally", + author: response.userId, + timestamp: new Date(response.updatedDate) + }); } }); } @@ -539,6 +460,105 @@ export class UnrestrictedSyncer { } } + private async handleMaybeMergingResponse( + { + document, + response, + contentHash, + originalRelativePath, + originalContentBytes + }: { + document: DocumentRecord; + response: DocumentVersion | DocumentUpdateResponse, + contentHash: string, + originalRelativePath: string, + originalContentBytes: Uint8Array + } + ): Promise { + + // `document` is mutable and reflects the latest state in the local database + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (document.isDeleted) { + this.logger.info( + `Document ${document.relativePath} has been deleted before we could finish updating it` + ); + this.database.addSeenUpdateId(response.vaultUpdateId); + return; + } + + if ( + (document.metadata?.parentVersionId ?? 0) > response.vaultUpdateId + ) { + this.logger.debug( + `Document ${document.relativePath} is already more up to date than the fetched version` + ); + this.database.addSeenUpdateId(response.vaultUpdateId); // in case the previous `vaultUpdateId` update hasn't made it through + return; + } + + if (response.isDeleted) { + return this.applyRemoteDeleteLocally(document, response); + } + + let actualPath = document.relativePath; + + // this can't happen on the creation path as we can only get a merging response if a document already exists remotely on the same path + if (response.relativePath != originalRelativePath) { + actualPath = response.relativePath; + // Make sure to update the remote relative path to avoid uploading + // the file as a result of this filesystem event. + if (document.metadata !== undefined) { + document.metadata.remoteRelativePath = response.relativePath; + } + await this.operations.move( + document.relativePath, + response.relativePath + ); // this can throw FileNotFoundError + } + + if (!("type" in response) || response.type === "MergingUpdate") { + const responseBytes = base64ToBytes(response.contentBase64); + contentHash = hash(responseBytes); + + this.database.updateDocumentMetadata( + { + documentId: response.documentId, + parentVersionId: response.vaultUpdateId, + hash: contentHash, + remoteRelativePath: response.relativePath + }, + document + ); + await this.operations.write( + actualPath, + originalContentBytes, + responseBytes + ); + await this.updateCache( + response.vaultUpdateId, + responseBytes, + actualPath + ); + } else { + this.database.updateDocumentMetadata( + { + documentId: response.documentId, + parentVersionId: response.vaultUpdateId, + hash: contentHash, + remoteRelativePath: response.relativePath + }, + document + ); + await this.updateCache( + response.vaultUpdateId, + originalContentBytes, + actualPath + ); + } + + this.database.addSeenUpdateId(response.vaultUpdateId); + } + private getHistoryEntryForSkippedOversizedFile( sizeInBytes: number, relativePath: RelativePath @@ -578,16 +598,7 @@ export class UnrestrictedSyncer { document: DocumentRecord, response: DocumentVersion | DocumentUpdateResponse ): Promise { - this.history.addHistoryEntry({ - status: SyncStatus.SUCCESS, - details: { - type: SyncType.DELETE, - relativePath: document.relativePath - }, - message: "File has been deleted remotely, so we deleted it locally", - author: response.userId, - timestamp: new Date(response.updatedDate) - }); + this.database.delete(document.relativePath); this.database.updateDocumentMetadata( diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index e9d47559..1f235b01 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days diff --git a/sync-server/src/app_state/database.rs b/sync-server/src/app_state/database.rs index 75ce6df4..135d93bf 100644 --- a/sync-server/src/app_state/database.rs +++ b/sync-server/src/app_state/database.rs @@ -79,6 +79,7 @@ impl Database { }, ); } + info!("Database migrations applied"); let database = Self { config: config.clone(), @@ -301,7 +302,7 @@ impl Database { .context("Cannot fetch max update id in vault") } - pub async fn get_latest_document_by_path( + pub async fn get_latest_non_deleted_document_by_path( &self, vault: &VaultId, relative_path: &str, diff --git a/sync-server/src/server/create_document.rs b/sync-server/src/server/create_document.rs index 20f67193..e4b3c055 100644 --- a/sync-server/src/server/create_document.rs +++ b/sync-server/src/server/create_document.rs @@ -52,7 +52,7 @@ pub async fn create_document( if request.force_merge.unwrap_or_default() { let latest_version = state .database - .get_latest_document_by_path( + .get_latest_non_deleted_document_by_path( &vault_id, &sanitized_relative_path, Some(&mut transaction), @@ -65,7 +65,8 @@ pub async fn create_document( ); return merge_with_stored_version( - latest_version.clone(), + &sanitized_relative_path, + &Vec::new(), latest_version, vault_id, user, diff --git a/sync-server/src/server/update_document.rs b/sync-server/src/server/update_document.rs index 2be40cd3..7bee7b60 100644 --- a/sync-server/src/server/update_document.rs +++ b/sync-server/src/server/update_document.rs @@ -172,7 +172,8 @@ async fn update_document( } merge_with_stored_version( - parent_document, + &parent_document.relative_path, + &parent_document.content, latest_version, vault_id, user, @@ -187,7 +188,8 @@ async fn update_document( #[allow(clippy::too_many_arguments)] pub async fn merge_with_stored_version( - parent_document: StoredDocumentVersion, + parent_document_path: &str, + parent_document_content: &[u8], latest_version: StoredDocumentVersion, vault_id: VaultId, user: User, @@ -203,7 +205,7 @@ pub async fn merge_with_stored_version( { info!( "Document content is the same as the latest version for `{}`, skipping update", - parent_document.document_id + latest_version.document_id ); transaction .rollback() @@ -219,17 +221,17 @@ pub async fn merge_with_stored_version( let are_all_participants_mergable = is_file_type_mergable( sanitized_relative_path, &state.config.server.mergeable_file_extensions, - ) && !is_binary(&parent_document.content) + ) && !is_binary(parent_document_content) && !is_binary(&latest_version.content) && !is_binary(&content); let merged_content = if are_all_participants_mergable { info!( "Merging changes for document `{}` in vault `{vault_id}`", - parent_document.document_id + latest_version.document_id ); reconcile( - str::from_utf8(&parent_document.content) + str::from_utf8(parent_document_content) .expect("parent must be valid UTF-8 because it's not binary"), &str::from_utf8(&latest_version.content) .expect("latest_version must be valid UTF-8 because it's not binary") @@ -247,7 +249,7 @@ pub async fn merge_with_stored_version( }; // We can only update the relative path if we're the first one to do so - let new_relative_path = if parent_document.relative_path == latest_version.relative_path + let new_relative_path = if parent_document_path == &latest_version.relative_path && latest_version.relative_path != sanitized_relative_path { let new_path = find_first_available_path( @@ -279,7 +281,7 @@ pub async fn merge_with_stored_version( let is_different_from_request_content = merged_content != content; let new_version = StoredDocumentVersion { - document_id: parent_document.document_id, + document_id: latest_version.document_id, vault_update_id: last_update_id + 1, relative_path: new_relative_path, content: merged_content, diff --git a/sync-server/src/utils/find_first_available_path.rs b/sync-server/src/utils/find_first_available_path.rs index 7629d8f1..937eecae 100644 --- a/sync-server/src/utils/find_first_available_path.rs +++ b/sync-server/src/utils/find_first_available_path.rs @@ -9,17 +9,19 @@ pub async fn find_first_available_path( database: &crate::app_state::database::Database, transaction: &mut Transaction<'_>, ) -> Result { - info!("Finding first available path for `{sanitized_relative_path}` in vault `{vault_id}`"); for candidate in dedup_paths(sanitized_relative_path) { - debug!("Checking candidate path for deconflicting names: `{candidate}`"); if database - .get_latest_document_by_path(vault_id, &candidate, Some(transaction)) + .get_latest_non_deleted_document_by_path(vault_id, &candidate, Some(transaction)) .await? .is_none() { info!("Selected available path: `{candidate}`"); return Ok(candidate); } + + info!( + "Finding first available path for `{sanitized_relative_path}` in vault `{vault_id}` as `{candidate}` is already taken" + ); } unreachable!("dedup_paths produces infinite paths"); From e3a90833ff44669a644e354872882e24bda3c1b1 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 4 Jan 2026 14:14:05 +0000 Subject: [PATCH 22/25] Lint --- .../sync-client/src/persistence/database.ts | 8 +- .../sync-client/src/services/sync-service.ts | 8 +- .../src/services/websocket-manager.ts | 9 +- frontend/sync-client/src/sync-client.ts | 2 +- .../sync-client/src/sync-operations/syncer.ts | 3 +- .../sync-operations/unrestricted-syncer.ts | 110 +++++++++--------- frontend/test-client/src/agent/mock-client.ts | 14 +-- sync-server/config-e2e.yml | 32 ++--- sync-server/src/server/update_document.rs | 2 +- .../src/utils/find_first_available_path.rs | 2 +- 10 files changed, 92 insertions(+), 98 deletions(-) diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index a5daf876..981780e8 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -74,7 +74,6 @@ export class Database { this.documents.forEach((doc) => { this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId); }); - } public get length(): number { @@ -104,7 +103,7 @@ export class Database { i === 0 ? false : records[i - 1].parallelVersion === - current.parallelVersion + current.parallelVersion ) ) { throw new Error( @@ -282,7 +281,6 @@ export class Database { candidate.isDeleted = true; } - public getLastSeenUpdateId(): VaultUpdateId { return this.lastSeenUpdateIds.min; } @@ -317,7 +315,7 @@ export class Database { ...metadata! // `resolvedDocuments` only returns docs with metadata set }) ), - lastSeenUpdateId: this.lastSeenUpdateIds.min, + lastSeenUpdateId: this.lastSeenUpdateIds.min }); } @@ -341,7 +339,7 @@ export class Database { if (duplicates.length > 0) { throw new Error( "Document IDs are not unique, found duplicates: " + - duplicates.join("; ") + duplicates.join("; ") ); } } diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index 82303bce..ce4d125a 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -157,7 +157,8 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${ + result.documentId }}` ); @@ -209,7 +210,8 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${ + result.documentId }}` ); @@ -336,7 +338,7 @@ export class SyncService { return this.retryForever(async () => { this.logger.debug( "Getting all documents" + - (since != null ? ` since ${since}` : "") + (since != null ? ` since ${since}` : "") ); const url = new URL(this.getUrl("/documents")); diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 266047ce..27ef7084 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -40,7 +40,7 @@ export class WebSocketManager { private readonly logger: Logger, private readonly settings: Settings, private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket - ) { } + ) {} public get isWebSocketConnected(): boolean { return ( @@ -260,10 +260,9 @@ export class WebSocketManager { this.resolveDisconnectingPromise?.(); this.resolveDisconnectingPromise = null; } else { - const delay = this.settings.getSettings().webSocketRetryIntervalMs; - this.logger.info( - `Reconnecting to WebSocket in ${delay}ms...` - ); + const delay = + this.settings.getSettings().webSocketRetryIntervalMs; + this.logger.info(`Reconnecting to WebSocket in ${delay}ms...`); this.reconnectTimeoutId = setTimeout(() => { this.reconnectTimeoutId = undefined; this.initializeWebSocket(); diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index dfcfc3e4..e7ae3baa 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -56,7 +56,7 @@ export class SyncClient { database: Partial; }> > - ) { } + ) {} public get documentCount(): number { return this.database.length; diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index e4017ca5..62f104b3 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -166,7 +166,7 @@ export class Syncer { // in that case, we mustn't move it again. if ( this.database.getLatestDocumentByRelativePath(relativePath) === - undefined || + undefined || this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === true ) { @@ -484,6 +484,5 @@ export class Syncer { return this.syncLocallyDeletedFile(relativePath); }) ); - } } diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 4ea1eda0..52ffcc6f 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -87,7 +87,7 @@ export class UnrestrictedSyncer { forceMerge: true }); - this.handleMaybeMergingResponse({ + await this.handleMaybeMergingResponse({ document, response, contentHash, @@ -159,14 +159,14 @@ export class UnrestrictedSyncer { const updateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined ? { - type: SyncType.MOVE, - relativePath: document.relativePath, - movedFrom: oldPath - } + type: SyncType.MOVE, + relativePath: document.relativePath, + movedFrom: oldPath + } : { - type: SyncType.UPDATE, - relativePath: document.relativePath - }; + type: SyncType.UPDATE, + relativePath: document.relativePath + }; await this.executeSync(updateDetails, async () => { const originalRelativePath = document.relativePath; @@ -181,7 +181,7 @@ export class UnrestrictedSyncer { const contentBytes = await this.operations.read( document.relativePath ); // this can throw FileNotFoundError - let contentHash = hash(contentBytes); + const contentHash = hash(contentBytes); const areThereLocalChanges = !( document.metadata.hash === contentHash && oldPath === undefined @@ -205,22 +205,22 @@ export class UnrestrictedSyncer { response = isText && cachedVersion !== undefined ? await this.syncService.putText({ - documentId: document.metadata.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - content: diff( - new TextDecoder().decode(cachedVersion), - new TextDecoder().decode(contentBytes) - ) - }) + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + content: diff( + new TextDecoder().decode(cachedVersion), + new TextDecoder().decode(contentBytes) + ) + }) : await this.syncService.putBinary({ - documentId: document.metadata.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - contentBytes - }); + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + contentBytes + }); } else { if (!force) { this.logger.debug( @@ -234,10 +234,9 @@ export class UnrestrictedSyncer { }); } - - this.handleMaybeMergingResponse({ + await this.handleMaybeMergingResponse({ document, - response: response!, + response: response, contentHash, originalRelativePath, originalContentBytes: contentBytes @@ -255,16 +254,16 @@ export class UnrestrictedSyncer { const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined || - response.relativePath != originalRelativePath + response.relativePath != originalRelativePath ? { - type: SyncType.MOVE, - relativePath: response.relativePath, - movedFrom: oldPath ?? originalRelativePath - } + type: SyncType.MOVE, + relativePath: response.relativePath, + movedFrom: oldPath ?? originalRelativePath + } : { - type: SyncType.UPDATE, - relativePath: response.relativePath - }; + type: SyncType.UPDATE, + relativePath: response.relativePath + }; if (areThereLocalChanges) { this.history.addHistoryEntry({ @@ -288,7 +287,8 @@ export class UnrestrictedSyncer { type: SyncType.DELETE, relativePath: document.relativePath }, - message: "File has been deleted remotely, so we deleted it locally", + message: + "File has been deleted remotely, so we deleted it locally", author: response.userId, timestamp: new Date(response.updatedDate) }); @@ -460,24 +460,21 @@ export class UnrestrictedSyncer { } } - private async handleMaybeMergingResponse( - { - document, - response, - contentHash, - originalRelativePath, - originalContentBytes - }: { - document: DocumentRecord; - response: DocumentVersion | DocumentUpdateResponse, - contentHash: string, - originalRelativePath: string, - originalContentBytes: Uint8Array - } - ): Promise { - + private async handleMaybeMergingResponse({ + document, + response, + contentHash, + originalRelativePath, + originalContentBytes + }: { + document: DocumentRecord; + response: DocumentVersion | DocumentUpdateResponse; + contentHash: string; + originalRelativePath: string; + originalContentBytes: Uint8Array; + }): Promise { // `document` is mutable and reflects the latest state in the local database - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (document.isDeleted) { this.logger.info( `Document ${document.relativePath} has been deleted before we could finish updating it` @@ -572,8 +569,9 @@ export class UnrestrictedSyncer { type: SyncType.SKIPPED, relativePath }, - message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${maxFileSizeMB - } MB` + message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${ + maxFileSizeMB + } MB` }; } } @@ -598,8 +596,6 @@ export class UnrestrictedSyncer { document: DocumentRecord, response: DocumentVersion | DocumentUpdateResponse ): Promise { - - this.database.delete(document.relativePath); this.database.updateDocumentMetadata( { diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 240869bb..84ef4f18 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -102,13 +102,13 @@ export class MockClient implements FileSystemOperations { .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}` - ); - } + // all changes should be additive + { + assert( + newParts.includes(part), + `Part ${part} not found in new content: ${newContent}` + ); + } ); } diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index 1f235b01..e9d47559 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days diff --git a/sync-server/src/server/update_document.rs b/sync-server/src/server/update_document.rs index 7bee7b60..b5d9bf0a 100644 --- a/sync-server/src/server/update_document.rs +++ b/sync-server/src/server/update_document.rs @@ -249,7 +249,7 @@ pub async fn merge_with_stored_version( }; // We can only update the relative path if we're the first one to do so - let new_relative_path = if parent_document_path == &latest_version.relative_path + let new_relative_path = if parent_document_path == latest_version.relative_path && latest_version.relative_path != sanitized_relative_path { let new_path = find_first_available_path( diff --git a/sync-server/src/utils/find_first_available_path.rs b/sync-server/src/utils/find_first_available_path.rs index 937eecae..20a0a656 100644 --- a/sync-server/src/utils/find_first_available_path.rs +++ b/sync-server/src/utils/find_first_available_path.rs @@ -1,7 +1,7 @@ use crate::app_state::database::models::VaultId; use crate::{app_state::database::Transaction, utils::dedup_paths::dedup_paths}; use anyhow::Result; -use log::{debug, info}; +use log::info; pub async fn find_first_available_path( vault_id: &VaultId, From 2dfb8b71e54ce6a274f26339b717be137c259678 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Mon, 12 Jan 2026 21:24:05 +0000 Subject: [PATCH 23/25] Working setup --- README.md | 7 + .../obsidian-plugin/src/vault-link-plugin.ts | 8 +- frontend/package-lock.json | 4 +- .../sync-client/src/persistence/database.ts | 23 +- .../sync-client/src/services/sync-service.ts | 8 +- .../src/services/websocket-manager.ts | 9 +- frontend/sync-client/src/sync-client.ts | 19 +- .../sync-client/src/sync-operations/syncer.ts | 134 +++++-- .../sync-operations/unrestricted-syncer.ts | 349 +++++++++--------- .../src/utils/data-structures/locks.ts | 14 +- .../src/utils/debugging/log-to-console.ts | 7 +- frontend/test-client/src/agent/mock-agent.ts | 37 +- frontend/test-client/src/agent/mock-client.ts | 45 +-- frontend/test-client/src/cli.ts | 43 ++- sync-server/config-e2e.yml | 32 +- sync-server/src/app_state/database.rs | 38 +- 16 files changed, 459 insertions(+), 318 deletions(-) diff --git a/README.md b/README.md index 74c6ee97..8d29cd0e 100644 --- a/README.md +++ b/README.md @@ -77,3 +77,10 @@ And to clean up the logs & database files, run `scripts/clean-up.sh` ## Projects - [Sync server](./sync-server/README.md) + + + + + + +a create that has been processed by the server but got lost on the way back will create a 2nd doc if it gets edited diff --git a/frontend/obsidian-plugin/src/vault-link-plugin.ts b/frontend/obsidian-plugin/src/vault-link-plugin.ts index 3def64f8..4af350f4 100644 --- a/frontend/obsidian-plugin/src/vault-link-plugin.ts +++ b/frontend/obsidian-plugin/src/vault-link-plugin.ts @@ -135,14 +135,14 @@ export default class VaultLinkPlugin extends Plugin { nativeLineEndings: Platform.isWin ? "\r\n" : "\n", ...(IS_DEBUG_BUILD ? { - fetch: debugging.slowFetchFactory(1), - webSocket: debugging.slowWebSocketFactory(1, new Logger()) - } + fetch: debugging.slowFetchFactory(1), + webSocket: debugging.slowWebSocketFactory(1, new Logger()) + } : {}) }); if (IS_DEBUG_BUILD) { - debugging.logToConsole(client); + debugging.logToConsole(client.logger); } return client; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 83f10716..e9460f22 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2902,7 +2902,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { diff --git a/frontend/sync-client/src/persistence/database.ts b/frontend/sync-client/src/persistence/database.ts index 981780e8..5118833f 100644 --- a/frontend/sync-client/src/persistence/database.ts +++ b/frontend/sync-client/src/persistence/database.ts @@ -103,7 +103,7 @@ export class Database { i === 0 ? false : records[i - 1].parallelVersion === - current.parallelVersion + current.parallelVersion ) ) { throw new Error( @@ -170,7 +170,7 @@ export class Database { if (entry === undefined) { throw new Error( - `Document not found by relative path: ${relativePath}, ${JSON.stringify( + `Document not found by relative path in getResolvedDocumentByRelativePath: ${relativePath}, ${JSON.stringify( this.documents, null, 2 @@ -262,7 +262,7 @@ export class Database { } oldDocument.relativePath = newRelativePath; - // We're in a strange state where the target of the move has just got deleted, + // We might be 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 = @@ -275,7 +275,11 @@ export class Database { const candidate = this.getLatestDocumentByRelativePath(relativePath); if (candidate === undefined) { throw new Error( - `Document not found by relative path: ${relativePath}` + `Document not found by relative path in delete: ${relativePath}, ${JSON.stringify( + this.documents, + null, + 2 + )}` ); } candidate.isDeleted = true; @@ -334,12 +338,19 @@ export class Database { const duplicates = Array.from(idToPath.entries()) .filter(([_, paths]) => paths.length > 1) - .map(([id, paths]) => `${id} (${paths.join(", ")})`); + .map(([id, paths]) => { + let details = ""; + for (const path of paths) { + const doc = this.getLatestDocumentByRelativePath(path); + details += `\n- ${JSON.stringify(doc, null, 2)}`; + } + return `${id} (${paths.join(", ")}): ${details}`; + }); if (duplicates.length > 0) { throw new Error( "Document IDs are not unique, found duplicates: " + - duplicates.join("; ") + duplicates.join("; ") ); } } diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index ce4d125a..82303bce 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -157,8 +157,7 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${result.documentId }}` ); @@ -210,8 +209,7 @@ export class SyncService { (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 + `Updated document ${JSON.stringify(result)} with id ${result.documentId }}` ); @@ -338,7 +336,7 @@ export class SyncService { return this.retryForever(async () => { this.logger.debug( "Getting all documents" + - (since != null ? ` since ${since}` : "") + (since != null ? ` since ${since}` : "") ); const url = new URL(this.getUrl("/documents")); diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 27ef7084..e99b8662 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -164,7 +164,10 @@ export class WebSocketManager { this.webSocket.onclose = null; this.webSocket.onmessage = null; this.webSocket.onerror = null; - this.webSocket.close(); + this.webSocket.close( + 1000, + "Closing previous WebSocket connection" + ); } catch (e) { this.logger.error( `Failed to close previous WebSocket connection: ${e}` @@ -187,7 +190,7 @@ export class WebSocketManager { `WebSocket connection timeout after ${WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS} seconds` ); // Force close to trigger onclose handler which will schedule reconnection - this.webSocket?.close(); + this.webSocket?.close(1000, "Connection timeout"); }, WEBSOCKET_CONNECTION_TIMEOUT_IN_SECONDS * 1000); this.webSocket.onopen = (): void => { @@ -240,7 +243,7 @@ export class WebSocketManager { }; this.webSocket.onerror = (error): void => { - this.logger.error( + this.logger.warn( `WebSocket error occurred: ${error instanceof ErrorEvent ? error.message : "Unknown error"}` ); }; diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index e7ae3baa..04a69fc9 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -29,7 +29,6 @@ import { ServerConfig } from "./services/server-config"; import type { EventListeners } from "./utils/data-structures/event-listeners"; export class SyncClient { - private hasStartedOfflineSync = false; private hasFinishedOfflineSync = false; private hasStarted = false; private hasBeenDestroyed = false; @@ -41,6 +40,7 @@ export class SyncClient { private readonly history: SyncHistory, private readonly settings: Settings, private readonly database: Database, + private readonly unrestrictedSyncer: UnrestrictedSyncer, private readonly syncer: Syncer, private readonly webSocketManager: WebSocketManager, public readonly logger: Logger, @@ -56,7 +56,7 @@ export class SyncClient { database: Partial; }> > - ) {} + ) { } public get documentCount(): number { return this.database.length; @@ -221,6 +221,7 @@ export class SyncClient { history, settings, database, + unrestrictedSyncer, syncer, webSocketManager, logger, @@ -335,7 +336,6 @@ export class SyncClient { this.database.reset(); await this.database.save(); // ensure the new database reads as empty this.resetInMemoryState(); - this.hasStartedOfflineSync = false; this.hasFinishedOfflineSync = false; this.serverConfig.reset(); @@ -369,7 +369,9 @@ export class SyncClient { this.checkIfDestroyed("syncLocallyCreatedFile"); this.fileChangeNotifier.notifyOfFileChange(relativePath); - return this.syncer.syncLocallyCreatedFile(relativePath); + return this.syncer.syncLocallyCreatedFile(relativePath, { + forceMerge: false + }); } public async syncLocallyDeletedFile( @@ -475,17 +477,15 @@ export class SyncClient { // warm the cache await this.serverConfig.getConfig(); - this.webSocketManager.start(); - if (!this.hasStartedOfflineSync) { - this.hasStartedOfflineSync = true; - await this.syncer.scheduleSyncForOfflineChanges(); - } + await this.syncer.scheduleSyncForOfflineChanges(); + this.webSocketManager.start(); this.hasFinishedOfflineSync = true; } private async pause(): Promise { + this.hasFinishedOfflineSync = false; this.fetchController.startReset(); await this.webSocketManager.stop(); await this.waitUntilFinished(); @@ -497,6 +497,7 @@ export class SyncClient { // don't reset the logger this.cursorTracker.reset(); this.syncer.reset(); + this.unrestrictedSyncer.reset(); this.fileOperations.reset(); } diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 62f104b3..068c348a 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -42,7 +42,7 @@ export class Syncer { private readonly settings: Settings, private readonly webSocketManager: WebSocketManager, private readonly operations: FileOperations, - private readonly internalSyncer: UnrestrictedSyncer + private readonly unrestrictedSyncer: UnrestrictedSyncer ) { this.syncQueue = new PQueue({ concurrency: settings.getSettings().syncConcurrency @@ -81,12 +81,15 @@ export class Syncer { } public async syncLocallyCreatedFile( - relativePath: RelativePath + relativePath: RelativePath, + { forceMerge }: { forceMerge: boolean } ): Promise { if ( this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === false ) { + // This is likely a consequence of us creating a file because of a remote update + // which triggered a local create, so we don't need to do anything here. this.logger.debug( `Document ${relativePath} already exists in the database, skipping` ); @@ -94,6 +97,7 @@ export class Syncer { } const [promise, resolve, reject] = createPromise(); + this.logger.warn(`creating ${relativePath} locally`); const document = this.database.createNewPendingDocument( relativePath, @@ -102,8 +106,13 @@ export class Syncer { try { await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncLocallyCreatedFile(document) - ); + this.unrestrictedSyncer.unrestrictedSyncLocallyCreatedOrUpdatedFile( + { document, forceMerge } + ) + ) + + this.logger.warn(`done creating ${relativePath} locally`); + resolve(); } catch (e) { @@ -123,7 +132,7 @@ export class Syncer { // This is must be a consequence of us deleting a file because of a remote update // which triggered a local delete, so we don't need to do anything here. this.logger.debug( - `Document ${relativePath} has already been markes as deleted, skipping` + `Document ${relativePath} has already been marked as deleted, skipping` ); return; } @@ -141,7 +150,7 @@ export class Syncer { try { await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncLocallyDeletedFile(document) + this.unrestrictedSyncer.unrestrictedSyncLocallyDeletedFile(document) ); resolve(); @@ -166,7 +175,7 @@ export class Syncer { // in that case, we mustn't move it again. if ( this.database.getLatestDocumentByRelativePath(relativePath) === - undefined || + undefined || this.database.getLatestDocumentByRelativePath(relativePath) ?.isDeleted === true ) { @@ -183,6 +192,8 @@ export class Syncer { let document = this.database.getLatestDocumentByRelativePath(relativePath); + this.logger.warn(`sync doc ${JSON.stringify(document)} for path ${relativePath} (old path: ${oldPath}), len docs: ${document?.updates.length}`); + if ( oldPath !== undefined && document?.metadata?.remoteRelativePath === relativePath @@ -193,6 +204,7 @@ export class Syncer { return; } + // must have been removed after a successful delete if (document === undefined) { this.logger.debug( `Cannot find document ${relativePath} in the database, skipping` @@ -213,12 +225,13 @@ export class Syncer { relativePath, promise ); + this.logger.warn(`updating ${document.relativePath} locally`); try { await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncLocallyUpdatedFile({ + this.unrestrictedSyncer.unrestrictedSyncLocallyCreatedOrUpdatedFile({ oldPath, - document + document: document! }) ); @@ -252,8 +265,6 @@ export class Syncer { `Not all local changes have been applied remotely: ${e}` ); throw e; - } finally { - this.runningScheduleSyncForOfflineChanges = undefined; } } @@ -266,6 +277,8 @@ export class Syncer { message: WebSocketVaultUpdate ): Promise { try { + await this.scheduleSyncForOfflineChanges(); + const handlerPromise = awaitAll( message.documents.map(async (document) => this.internalSyncRemotelyUpdatedFile(document) @@ -312,25 +325,45 @@ export class Syncer { remoteVersion.documentId ); + this.logger.warn(`${remoteVersion.documentId} got remote update ${JSON.stringify(remoteVersion)}`); + if (document === undefined) { - // Let's avoid the same documents getting created in parallel multiple times. - // There might be multiple tasks waiting for the lock + this.logger.warn(`${remoteVersion.documentId} but document doesn't exist`) + + return this.remoteDocumentsLock.withLock( + // Avoid the same documents getting created in parallel multiple times through fetching multiple updates of the same + // new remote document concurrently. + // There might be multiple tasks waiting for the lock remoteVersion.documentId, async () => { + + // We have to wait for any ongoing creates sent for this file to finish, + // This is to avoid fetching one's own creates before the corresponding local create has finished syncing. This is a concern because + // documents being created don't yet have a document id in the local database and we could be notified of the remote create + // before the local create has finished syncing, so we can't just ignore the update based on the local DB content as we + // can't find the corresponding document yet. + if (document?.metadata === undefined) { + await this.unrestrictedSyncer.fileCreationLock.waitForLockWithoutAcquiringLock(remoteVersion.relativePath); + } + document = this.database.getDocumentByDocumentId( remoteVersion.documentId ); - // We're either the first one to get the lock, so we have to create the document in `unrestrictedSyncRemotelyUpdatedFile` + this.logger.warn(`${remoteVersion.documentId} rechecking, document is now ${JSON.stringify(document)}`) + + // We're the first one to get the lock, so we have to create the document in `unrestrictedSyncRemotelyUpdatedFile` if (document === undefined) { + this.logger.warn(`${remoteVersion.documentId} document is undefined, creating new document`) await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncRemotelyUpdatedFile( + this.unrestrictedSyncer.unrestrictedSyncRemotelyUpdatedFile( remoteVersion ) ); } else { - const [promise, resolve, reject] = createPromise(); + const [promise, resolve, reject] = + createPromise(); document = await this.database.getResolvedDocumentByRelativePath( @@ -340,7 +373,7 @@ export class Syncer { try { await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncRemotelyUpdatedFile( + this.unrestrictedSyncer.unrestrictedSyncRemotelyUpdatedFile( remoteVersion, document ) @@ -350,13 +383,19 @@ export class Syncer { } catch (e) { reject(e); } finally { - this.database.removeDocumentPromise(promise); + this.database.removeDocumentPromise( + promise + ); } } - this.database.addSeenUpdateId(remoteVersion.vaultUpdateId); + this.database.addSeenUpdateId( + remoteVersion.vaultUpdateId + ); } - ); + ) + } else { + this.logger.warn(`${remoteVersion.documentId} and document exists (path: ${JSON.stringify(document)})`); } // We're either the first one to get the lock, so we have to create the document in `unrestrictedSyncRemotelyUpdatedFile` @@ -369,7 +408,7 @@ export class Syncer { try { await this.syncQueue.add(async () => - this.internalSyncer.unrestrictedSyncRemotelyUpdatedFile( + this.unrestrictedSyncer.unrestrictedSyncRemotelyUpdatedFile( remoteVersion, document ) @@ -402,7 +441,8 @@ export class Syncer { } } - await awaitAll( + type Instruction = { "type": "update" | "create", relativePath: string, oldPath?: string }; + const instructions: (Instruction | undefined)[] = await awaitAll( allLocalFiles.map(async (relativePath) => { if ( this.database.getLatestDocumentByRelativePath(relativePath) @@ -412,9 +452,7 @@ export class Syncer { `Document ${relativePath} might have been updated locally, scheduling sync to validate and update it` ); - return this.syncLocallyUpdatedFile({ - relativePath - }); + return { type: "update", relativePath } as Instruction; } // Perhaps the file has been moved; let's check by looking at the deleted files @@ -457,21 +495,26 @@ export class Syncer { `Document '${originalFile.relativePath}' was not found under its current path in the database but was found under a different path (${relativePath}), scheduling sync to move it` ); - // We're outside of the pqueue, so we need to call the public wrapper - return this.syncLocallyUpdatedFile({ + return { + type: "update", oldPath: originalFile.relativePath, relativePath - }); + } as Instruction; + } this.logger.debug( `Document ${relativePath} not found in database, scheduling sync to create it` ); - // We're outside of the pqueue, so we need to call the public wrapper - return this.syncLocallyCreatedFile(relativePath); + + return { + type: "create", + relativePath + } as Instruction; }) ); + // this has to happen strictly after the previous awaitAll, as that one // might have removed some of the documents from the list await awaitAll( @@ -484,5 +527,36 @@ export class Syncer { return this.syncLocallyDeletedFile(relativePath); }) ); + + + await awaitAll(instructions.map(async (instruction) => { + if (instruction === undefined) { + return; + } + + if (instruction.type === "update") { + // We're outside of the pqueue, so we need to call the public wrapper + return await this.syncLocallyUpdatedFile({ + oldPath: instruction.oldPath, + relativePath: instruction.relativePath + }); + } + })); + + // we have to ensure the deletes & updates have finished before starting creates, + // otherwise the server might return an existing document (that we're about to delete) + // instead of actually creating a new one + await awaitAll(instructions.map(async (instruction) => { + if (instruction === undefined) { + return; + } + + if (instruction.type === "create") { + // We're outside of the pqueue, so we need to call the public wrapper + return await this.syncLocallyCreatedFile(instruction.relativePath, { forceMerge: true }); + } + })); + + } } diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index 52ffcc6f..272668c4 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -33,9 +33,12 @@ import type { FixedSizeDocumentCache } from "../utils/data-structures/fix-sized- import { isFileTypeMergable } from "../utils/is-file-type-mergable"; import { isBinary } from "../utils/is-binary"; import type { ServerConfig } from "../services/server-config"; +import { Locks } from "../utils/data-structures/locks"; export class UnrestrictedSyncer { private ignorePatterns: RegExp[]; + public readonly fileCreationLock: Locks = new Locks(); + public constructor( private readonly logger: Logger, @@ -60,118 +63,50 @@ export class UnrestrictedSyncer { }); } - public async unrestrictedSyncLocallyCreatedFile( - document: DocumentRecord - ): Promise { - const updateDetails: SyncCreateDetails = { - type: SyncType.CREATE, - relativePath: document.relativePath - }; - - return this.executeSync(updateDetails, async () => { - const originalRelativePath = document.relativePath; - if (document.isDeleted) { - this.logger.debug( - `Document ${originalRelativePath} has been already deleted, no need to create it` - ); - return; - } - - const contentBytes = - await this.operations.read(originalRelativePath); // this can throw FileNotFoundError - const contentHash = hash(contentBytes); - - const response = await this.syncService.create({ - relativePath: originalRelativePath, - contentBytes, - forceMerge: true - }); - - await this.handleMaybeMergingResponse({ - document, - response, - contentHash, - originalRelativePath, - originalContentBytes: contentBytes - }); - - this.history.addHistoryEntry({ - status: SyncStatus.SUCCESS, - details: updateDetails, - message: `Successfully uploaded locally created file` - }); - }); - } - - public async unrestrictedSyncLocallyDeletedFile( - document: DocumentRecord - ): Promise { - const updateDetails: SyncDeleteDetails = { - type: SyncType.DELETE, - relativePath: document.relativePath - }; - - await this.executeSync(updateDetails, async () => { - if (document.metadata === undefined) { - this.logger.debug( - `Document ${document.relativePath} has no metadata, so it has never got synced remotely; no need to delete it remotely` - ); - return; - } - - const response = await this.syncService.delete({ - documentId: document.metadata.documentId, - relativePath: document.relativePath - }); - - this.database.updateDocumentMetadata( - { - ...document.metadata, - parentVersionId: response.vaultUpdateId, - hash: EMPTY_HASH, - remoteRelativePath: document.relativePath - }, - document - ); - - this.database.addSeenUpdateId(response.vaultUpdateId); - - this.history.addHistoryEntry({ - status: SyncStatus.SUCCESS, - details: updateDetails, - message: `Successfully deleted locally deleted file on the server`, - author: response.userId - }); - }); - } - - public async unrestrictedSyncLocallyUpdatedFile({ + public async unrestrictedSyncLocallyCreatedOrUpdatedFile({ oldPath, document, + forceMerge, // We use the same code path for both local and remote updates. We need to force the update // if there are no local changes but we know that the remote version is newer. force = false }: { oldPath?: RelativePath; force?: boolean; + forceMerge?: boolean document: DocumentRecord; }): Promise { - const updateDetails: SyncUpdateDetails | SyncMovedDetails = - oldPath !== undefined - ? { - type: SyncType.MOVE, - relativePath: document.relativePath, - movedFrom: oldPath - } - : { - type: SyncType.UPDATE, - relativePath: document.relativePath - }; + + // this.history.addHistoryEntry({ + // status: SyncStatus.SUCCESS, + // details: updateDetails, + // message: `Successfully uploaded locally created file` + // }); + + let updateDetails: SyncCreateDetails | SyncUpdateDetails | SyncMovedDetails; + if (document.metadata === undefined) { + updateDetails = { + type: SyncType.CREATE, + relativePath: document.relativePath + }; + } + else if (oldPath !== undefined) { + updateDetails = { + type: SyncType.MOVE, + relativePath: document.relativePath, + movedFrom: oldPath + }; + } else { + updateDetails = { + type: SyncType.UPDATE, + relativePath: document.relativePath + }; + } await this.executeSync(updateDetails, async () => { const originalRelativePath = document.relativePath; - if (document.isDeleted || document.metadata === undefined) { + if (document.isDeleted) { this.logger.debug( `Document ${document.relativePath} has been already deleted, no need to update it` ); @@ -183,64 +118,88 @@ export class UnrestrictedSyncer { ); // this can throw FileNotFoundError const contentHash = hash(contentBytes); - const areThereLocalChanges = !( - document.metadata.hash === contentHash && oldPath === undefined - ); + this.logger.warn(`updating ${document.relativePath} locally, inner`); let response: DocumentVersion | DocumentUpdateResponse | undefined = undefined; - if (areThereLocalChanges) { - const isText = - !isBinary(contentBytes) && - isFileTypeMergable( - document.relativePath, - (await this.serverConfig.getConfig()) - .mergeableFileExtensions - ); - const cachedVersion = this.contentCache.get( - document.metadata.parentVersionId - ); + if (document.metadata === undefined) { + response = await this.fileCreationLock.withLock(document.relativePath, async () => { + const response = await this.syncService.create({ + relativePath: originalRelativePath, + contentBytes, + forceMerge + }); - response = - isText && cachedVersion !== undefined - ? await this.syncService.putText({ - documentId: document.metadata.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - content: diff( - new TextDecoder().decode(cachedVersion), - new TextDecoder().decode(contentBytes) - ) - }) - : await this.syncService.putBinary({ - documentId: document.metadata.documentId, - parentVersionId: - document.metadata.parentVersionId, - relativePath: document.relativePath, - contentBytes - }); + await this.handleMaybeMergingResponse({ + document, + response, + contentHash, + originalRelativePath, + originalContentBytes: contentBytes + }); + + return response; + }); } else { - if (!force) { - this.logger.debug( - `File hash of ${document.relativePath} matches with last synced version and the path hasn't changed; no need to sync` + const areThereLocalChanges = + document.metadata.hash !== contentHash || oldPath !== undefined; + + if (areThereLocalChanges) { + const isText = + !isBinary(contentBytes) && + isFileTypeMergable( + document.relativePath, + (await this.serverConfig.getConfig()) + .mergeableFileExtensions + ); + const cachedVersion = this.contentCache.get( + document.metadata.parentVersionId ); - return; + + response = + isText && cachedVersion !== undefined + ? await this.syncService.putText({ + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + content: diff( + new TextDecoder().decode(cachedVersion), + new TextDecoder().decode(contentBytes) + ) + }) + : await this.syncService.putBinary({ + documentId: document.metadata.documentId, + parentVersionId: + document.metadata.parentVersionId, + relativePath: document.relativePath, + contentBytes + }); + } else { + if (!force) { + this.logger.debug( + `File hash of ${document.relativePath} matches with last synced version and the path hasn't changed; no need to sync` + ); + return; + } + + // we use this code path (force == true) to sync remotely updated files which have no local changes + response = await this.syncService.get({ + documentId: document.metadata.documentId + }); } - response = await this.syncService.get({ - documentId: document.metadata.documentId + await this.handleMaybeMergingResponse({ + document, + response, + contentHash, + originalRelativePath, + originalContentBytes: contentBytes }); } - await this.handleMaybeMergingResponse({ - document, - response: response, - contentHash, - originalRelativePath, - originalContentBytes: contentBytes - }); + if (!("type" in response) || response.type === "MergingUpdate") { if (!force) { @@ -249,30 +208,33 @@ export class UnrestrictedSyncer { details: updateDetails, message: `The file we updated had been updated remotely, so we downloaded the merged version` }); + return; } } const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails = oldPath !== undefined || - response.relativePath != originalRelativePath + response.relativePath != originalRelativePath ? { - type: SyncType.MOVE, - relativePath: response.relativePath, - movedFrom: oldPath ?? originalRelativePath - } + type: SyncType.MOVE, + relativePath: response.relativePath, + movedFrom: originalRelativePath + } : { - type: SyncType.UPDATE, - relativePath: response.relativePath - }; + type: SyncType.UPDATE, + relativePath: response.relativePath + }; - if (areThereLocalChanges) { - this.history.addHistoryEntry({ - status: SyncStatus.SUCCESS, - details: actualUpdateDetails, - message: `Successfully uploaded locally updated file to the server`, - author: response.userId - }); - } else if (!response.isDeleted) { + // if (areThereLocalChanges) { + // this.history.addHistoryEntry({ + // status: SyncStatus.SUCCESS, + // details: actualUpdateDetails, + // message: `Successfully uploaded locally updated file to the server`, + // author: response.userId + // }); + // } else + + if (!response.isDeleted) { this.history.addHistoryEntry({ status: SyncStatus.SUCCESS, details: actualUpdateDetails, @@ -296,6 +258,49 @@ export class UnrestrictedSyncer { }); } + + public async unrestrictedSyncLocallyDeletedFile( + document: DocumentRecord + ): Promise { + const updateDetails: SyncDeleteDetails = { + type: SyncType.DELETE, + relativePath: document.relativePath + }; + + await this.executeSync(updateDetails, async () => { + if (document.metadata === undefined) { + this.logger.debug( + `Document ${document.relativePath} has never been synced, no need to delete it remotely` + ); + return; + } + + const response = await this.syncService.delete({ + documentId: document.metadata.documentId, + relativePath: document.relativePath + }); + + this.database.updateDocumentMetadata( + { + documentId: response.documentId, + parentVersionId: response.vaultUpdateId, + hash: EMPTY_HASH, + remoteRelativePath: document.relativePath + }, + document + ); + + this.database.addSeenUpdateId(response.vaultUpdateId); + + this.history.addHistoryEntry({ + status: SyncStatus.SUCCESS, + details: updateDetails, + message: `Successfully deleted locally deleted file on the server`, + author: response.userId + }); + }); + } + public async unrestrictedSyncRemotelyUpdatedFile( remoteVersion: DocumentVersionWithoutContent, document?: DocumentRecord @@ -305,6 +310,7 @@ export class UnrestrictedSyncer { relativePath: remoteVersion.relativePath }; + await this.executeSync(updateDetails, async () => { if (document?.metadata !== undefined) { // If the file exists locally, let's pretend the user has updated it @@ -320,7 +326,7 @@ export class UnrestrictedSyncer { return; } - return this.unrestrictedSyncLocallyUpdatedFile({ + return this.unrestrictedSyncLocallyCreatedOrUpdatedFile({ document, force: true }); @@ -403,10 +409,21 @@ export class UnrestrictedSyncer { }); } - public async executeSync( + public reset(): void { + this.fileCreationLock.reset(); + } + + private async executeSync( details: SyncDetails, fn: () => Promise ): Promise { + if (!this.settings.getSettings().isSyncEnabled) { + this.logger.info( + `Skipping sync operation for file '${details.relativePath}' because sync is disabled` + ); + return; + } + for (const pattern of this.ignorePatterns) { if (pattern.test(details.relativePath)) { this.logger.debug( @@ -460,6 +477,8 @@ export class UnrestrictedSyncer { } } + + private async handleMaybeMergingResponse({ document, response, @@ -474,7 +493,6 @@ export class UnrestrictedSyncer { originalContentBytes: Uint8Array; }): Promise { // `document` is mutable and reflects the latest state in the local database - if (document.isDeleted) { this.logger.info( `Document ${document.relativePath} has been deleted before we could finish updating it` @@ -569,9 +587,8 @@ export class UnrestrictedSyncer { type: SyncType.SKIPPED, relativePath }, - message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${ - maxFileSizeMB - } MB` + message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${maxFileSizeMB + } MB` }; } } diff --git a/frontend/sync-client/src/utils/data-structures/locks.ts b/frontend/sync-client/src/utils/data-structures/locks.ts index 743e8c6a..1e550c5a 100644 --- a/frontend/sync-client/src/utils/data-structures/locks.ts +++ b/frontend/sync-client/src/utils/data-structures/locks.ts @@ -18,7 +18,7 @@ export class Locks { [() => unknown, (err: unknown) => 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. @@ -125,6 +125,18 @@ export class Locks { }); } + /** + * Waits until a lock is released without acquiring it. + * Operations are queued in FIFO order. + * + * @param key The key to wait for + * @returns Promise that resolves when lock is released + */ + public async waitForLockWithoutAcquiringLock(key: T): Promise { + await this.waitForLock(key); + this.unlock(key); + } + /** * Releases a lock and grants access to the next waiting operation in FIFO order. * Removes the key from locked set if no waiters. 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 c47f18f6..9fdca13b 100644 --- a/frontend/sync-client/src/utils/debugging/log-to-console.ts +++ b/frontend/sync-client/src/utils/debugging/log-to-console.ts @@ -1,9 +1,8 @@ -import type { SyncClient } from "../../sync-client"; -import type { LogLine } from "../../tracing/logger"; +import type { Logger, LogLine } from "../../tracing/logger"; import { LogLevel } from "../../tracing/logger"; -export function logToConsole(client: SyncClient): void { - client.logger.onLogEmitted.add((logLine: LogLine) => { +export function logToConsole(logger: Logger): void { + logger.onLogEmitted.add((logLine: LogLine) => { const formatted = `${logLine.timestamp.toISOString()} ${logLine.level} ${logLine.message}`; switch (logLine.level) { diff --git a/frontend/test-client/src/agent/mock-agent.ts b/frontend/test-client/src/agent/mock-agent.ts index 1640c2ec..5b0d3a8c 100644 --- a/frontend/test-client/src/agent/mock-agent.ts +++ b/frontend/test-client/src/agent/mock-agent.ts @@ -63,10 +63,15 @@ export class MockAgent extends MockClient { case LogLevel.ERROR: console.error(formatted); - if (!this.useSlowFileEvents) { + if (!this.useSlowFileEvents && !formatted.includes("retrying in")) { // 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)); + sleep(100).then(() => { + console.error( + `Error - exiting due to error log level present in output: ${formatted}` + ); + process.exit(1); + }); } break; @@ -199,14 +204,14 @@ export class MockAgent extends MockClient { ); this.client.logger.info( "Local files: " + - Array.from(otherAgent.localFiles.keys()).join(", ") + 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(", ") + Array.from(otherAgent.localFiles.keys()).join(", ") ); throw e; @@ -230,20 +235,20 @@ export class MockAgent extends MockClient { }); if (this.doDeletes) { - assert( - found.length <= 1, - `[${this.name}] Content ${content} found in ${found.join(", ")}` - ); + // 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( @@ -279,7 +284,7 @@ export class MockAgent extends MockClient { `Decided to create file ${file} with content ${content}` ); - return this.create(file, new TextEncoder().encode(` ${content} `)); + return this.create(file, new TextEncoder().encode(` ${content} `), { ignoreSlowFileEvents: true }); } private async disableSyncAction(): Promise { @@ -320,7 +325,7 @@ export class MockAgent extends MockClient { 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, { ignoreSlowFileEvents: true }); } private async updateFileAction(files: RelativePath[]): Promise { @@ -346,13 +351,13 @@ export class MockAgent extends MockClient { await this.atomicUpdateText(file, (old) => ({ text: old.text + ` ${content} `, cursors: [] - })); + }), { ignoreSlowFileEvents: true }); } private async deleteFileAction(files: RelativePath[]): Promise { const file = choose(files); this.client.logger.info(`Decided to delete file ${file}`); - return this.delete(file); + return this.delete(file, { ignoreSlowFileEvents: true }); } private getContent(): string { diff --git a/frontend/test-client/src/agent/mock-client.ts b/frontend/test-client/src/agent/mock-client.ts index 84ef4f18..f7b6e384 100644 --- a/frontend/test-client/src/agent/mock-client.ts +++ b/frontend/test-client/src/agent/mock-client.ts @@ -64,7 +64,8 @@ export class MockClient implements FileSystemOperations { public async create( path: RelativePath, - newContent: Uint8Array + newContent: Uint8Array, + { ignoreSlowFileEvents }: { ignoreSlowFileEvents: boolean } = { ignoreSlowFileEvents: false } ): Promise { if (this.localFiles.has(path)) { throw new Error(`File ${path} already exists`); @@ -74,9 +75,9 @@ export class MockClient implements FileSystemOperations { ); this.localFiles.set(path, newContent); - this.executeFileOperation(async () => + this.executeFileOperation((async () => this.client.syncLocallyCreatedFile(path) - ); + ), ignoreSlowFileEvents); } public async createDirectory(_path: RelativePath): Promise { @@ -85,7 +86,8 @@ export class MockClient implements FileSystemOperations { public async atomicUpdateText( path: RelativePath, - updater: (currentContent: TextWithCursors) => TextWithCursors + updater: (currentContent: TextWithCursors) => TextWithCursors, + { ignoreSlowFileEvents }: { ignoreSlowFileEvents: boolean } = { ignoreSlowFileEvents: false } ): Promise { const file = this.localFiles.get(path); if (!file) { @@ -102,13 +104,13 @@ export class MockClient implements FileSystemOperations { .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}` - ); - } + // all changes should be additive + { + assert( + newParts.includes(part), + `Part ${part} not found in new content: ${newContent}` + ); + } ); } @@ -116,11 +118,11 @@ export class MockClient implements FileSystemOperations { `Updated file ${path} with:\n current content: ${currentContent}\n new content: ${newContent}` ); - this.executeFileOperation(async () => + this.executeFileOperation((async () => this.client.syncLocallyUpdatedFile({ relativePath: path }) - ); + ), ignoreSlowFileEvents); return newContent; } @@ -144,20 +146,21 @@ export class MockClient implements FileSystemOperations { }); } - public async delete(path: RelativePath): Promise { + public async delete(path: RelativePath, { ignoreSlowFileEvents }: { ignoreSlowFileEvents: boolean } = { ignoreSlowFileEvents: false }): 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.executeFileOperation((async () => this.client.syncLocallyDeletedFile(path) - ); + ), ignoreSlowFileEvents); } public async rename( oldPath: RelativePath, - newPath: RelativePath + newPath: RelativePath, + { ignoreSlowFileEvents }: { ignoreSlowFileEvents: boolean } = { ignoreSlowFileEvents: false } ): Promise { const file = this.localFiles.get(oldPath); if (!file) { @@ -172,16 +175,16 @@ export class MockClient implements FileSystemOperations { `Renamed file: ${oldPath} -> ${newPath} with:\n content ${new TextDecoder().decode(file)}` ); - this.executeFileOperation(async () => + this.executeFileOperation((async () => this.client.syncLocallyUpdatedFile({ oldPath, relativePath: newPath }) - ); + ), ignoreSlowFileEvents); } - private executeFileOperation(callback: () => unknown): void { - if (this.useSlowFileEvents) { + private executeFileOperation(callback: () => unknown, ignoreSlowFileEvents: boolean = false): void { + if (this.useSlowFileEvents && !ignoreSlowFileEvents) { // we aren't the best client and it takes some time to notice changes setTimeout(callback, Math.random() * 100); } else { diff --git a/frontend/test-client/src/cli.ts b/frontend/test-client/src/cli.ts index 3af547e7..e7303330 100644 --- a/frontend/test-client/src/cli.ts +++ b/frontend/test-client/src/cli.ts @@ -1,5 +1,5 @@ import type { SyncSettings } from "sync-client"; -import { utils } from "sync-client"; +import { utils, debugging, Logger } from "sync-client"; import { MockAgent } from "./agent/mock-agent"; import { sleep } from "./utils/sleep"; import { v4 as uuidv4 } from "uuid"; @@ -13,6 +13,9 @@ let slowFileEvents = false; // Whether to do resets in the test runs let doResets = false; +const logger = new Logger(); +debugging.logToConsole(logger); + async function runTest({ agentCount, concurrency, @@ -33,11 +36,13 @@ async function runTest({ slowFileEvents = useSlowFileEvents; doResets = useResets; + + const settings = `with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}, doResets ${useResets}, jitterScaleInSeconds ${jitterScaleInSeconds}, useSlowFileEvents ${useSlowFileEvents}`; - console.info(`Running test ${settings}`); + logger.info(`Running test ${settings}`); const vaultName = uuidv4(); - console.info(`Using vault name: ${vaultName}`); + logger.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 @@ -64,17 +69,17 @@ async function runTest({ await utils.awaitAll(clients.map(async (client) => client.init())); for (let i = 0; i < iterations; i++) { - console.info(`Iteration ${i + 1}/${iterations}`); + logger.info(`Iteration ${i + 1}/${iterations}`); await utils.awaitAll(clients.map(async (client) => client.act())); await sleep(Math.random() * 200); } - console.info("Stopping agents"); + logger.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}`); + logger.info(`Finishing up ${client.name}`); await client.finish(); } catch (err) { if (!slowFileEvents) { @@ -86,7 +91,7 @@ async function runTest({ // 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}`); + logger.info(`Destroying ${client.name}`); await client.destroy(); } catch (err) { if (!slowFileEvents) { @@ -95,27 +100,27 @@ async function runTest({ } } - console.info("Agents finished successfully"); + logger.info("Agents finished successfully"); clients.slice(0, -1).forEach((client, i) => { - console.info( + logger.info( `Checking consistency between ${client.name} and ${clients[i + 1].name}` ); client.assertFileSystemsAreConsistent(clients[i]); - console.info(`Consistency check for ${client.name} passed`); + logger.info(`Consistency check for ${client.name} passed`); }); - console.info("File systems found to be consistent"); + logger.info("File systems found to be consistent"); clients.forEach((client) => { - console.info(`Checking content for ${client.name}`); + logger.info(`Checking content for ${client.name}`); client.assertAllContentIsPresentOnce(); - console.info(`Content check for ${client.name} passed`); + logger.info(`Content check for ${client.name} passed`); }); - console.info(`Test passed ${settings}`); + logger.info(`Test passed ${settings}`); } catch (err) { - console.error(`Test failed ${settings}`); + logger.error(`Test failed ${settings}`); throw err; } } @@ -163,7 +168,7 @@ process.on("uncaughtException", (error) => { return; } - console.error("Uncaught exception:", error); + logger.error(`Error - uncaught exception: ${error}`); process.exit(1); }); @@ -191,7 +196,7 @@ process.on("unhandledRejection", (error, _promise) => { return; } - console.error("Unhandled rejection:", error); + logger.error(`Error - unhandled rejection: ${error}`); process.exit(1); }); @@ -199,7 +204,7 @@ runTests() .then(() => { process.exit(0); }) - .catch((err: unknown) => { - console.error(err); + .catch((error: unknown) => { + logger.error(`Error - tests failed with ${error}`); process.exit(1); }); diff --git a/sync-server/config-e2e.yml b/sync-server/config-e2e.yml index e9d47559..1f235b01 100644 --- a/sync-server/config-e2e.yml +++ b/sync-server/config-e2e.yml @@ -9,24 +9,24 @@ server: max_clients_per_vault: 256 response_timeout: 30m mergeable_file_extensions: - - md - - txt + - md + - txt users: user_configs: - - name: admin - token: test-token-change-me - vault_access: - type: allow_access_to_all - - name: other-admin - token: test-token-change-me2 - vault_access: - type: allow_access_to_all - - name: test - token: other-test-token - vault_access: - type: allow_list - allowed: - - default + - name: admin + token: test-token-change-me + vault_access: + type: allow_access_to_all + - name: other-admin + token: test-token-change-me2 + vault_access: + type: allow_access_to_all + - name: test + token: other-test-token + vault_access: + type: allow_list + allowed: + - default logging: log_directory: logs log_rotation: 7days diff --git a/sync-server/src/app_state/database.rs b/sync-server/src/app_state/database.rs index 135d93bf..95dbf5ec 100644 --- a/sync-server/src/app_state/database.rs +++ b/sync-server/src/app_state/database.rs @@ -104,8 +104,8 @@ impl Database { let connection_options = SqliteConnectOptions::new() .filename(file_name.clone()) .create_if_missing(true) - .auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Full) - .busy_timeout(Duration::from_secs(3600)) + .auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental) + .busy_timeout(Duration::from_secs(30)) .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) .log_slow_statements(log::LevelFilter::Warn, Duration::from_secs(30)); @@ -130,26 +130,30 @@ impl Database { } async fn get_connection_pool(&self, vault: &VaultId) -> Result> { - let mut pools = self.connection_pools.lock().await; - - if !pools.contains_key(vault) { - let pool = Self::create_vault_database(&self.config, vault).await?; - pools.insert( - vault.clone(), - PoolWithTimestamp { - pool, - last_accessed: Instant::now(), - }, - ); + // First, check if the pool exists without holding the lock during creation + { + let mut pools = self.connection_pools.lock().await; + if let Some(pool_with_timestamp) = pools.get_mut(vault) { + pool_with_timestamp.last_accessed = Instant::now(); + return Ok(pool_with_timestamp.pool.clone()); + } } + // Create the pool outside of the lock to avoid blocking other vaults + // Note: This may result in multiple pools being created for the same vault + // under high concurrency, but only one will be kept + let new_pool = Self::create_vault_database(&self.config, vault).await?; + + // Re-acquire lock and insert (or use existing if another task created it) + let mut pools = self.connection_pools.lock().await; let pool_with_timestamp = pools - .get_mut(vault) - .expect("Pool was just inserted or already exists"); + .entry(vault.clone()) + .or_insert_with(|| PoolWithTimestamp { + pool: new_pool.clone(), + last_accessed: Instant::now(), + }); - // Update last accessed time pool_with_timestamp.last_accessed = Instant::now(); - Ok(pool_with_timestamp.pool.clone()) } From 0e1849061bbbd35afbb92a9ca8e18b14996d8297 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Mon, 12 Jan 2026 21:24:18 +0000 Subject: [PATCH 24/25] Improve DB contention --- sync-server/src/app_state/database.rs | 30 +++++++++++++-------------- sync-server/src/consts.rs | 1 + 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/sync-server/src/app_state/database.rs b/sync-server/src/app_state/database.rs index 95dbf5ec..7308ec1a 100644 --- a/sync-server/src/app_state/database.rs +++ b/sync-server/src/app_state/database.rs @@ -10,7 +10,7 @@ use sqlx::{ConnectOptions, sqlite::SqliteConnectOptions, types::chrono::Utc}; pub mod models; use sqlx::{Pool, Sqlite, sqlite::SqlitePoolOptions}; -use tokio::sync::Mutex; +use tokio::sync::RwLock; use tokio::time::Instant; use uuid::fmt::Hyphenated; @@ -39,7 +39,7 @@ impl std::fmt::Debug for PoolWithTimestamp { pub struct Database { config: DatabaseConfig, broadcasts: Broadcasts, - connection_pools: Arc>>, + connection_pools: Arc>>, } pub type Transaction<'a> = sqlx::Transaction<'a, Sqlite>; @@ -83,7 +83,7 @@ impl Database { let database = Self { config: config.clone(), - connection_pools: Arc::new(Mutex::new(connection_pools)), + connection_pools: Arc::new(RwLock::new(connection_pools)), broadcasts: broadcasts.clone(), }; @@ -130,11 +130,12 @@ impl Database { } async fn get_connection_pool(&self, vault: &VaultId) -> Result> { - // First, check if the pool exists without holding the lock during creation + // Fast path: check if pool exists with a read lock (no blocking other readers) { - let mut pools = self.connection_pools.lock().await; - if let Some(pool_with_timestamp) = pools.get_mut(vault) { - pool_with_timestamp.last_accessed = Instant::now(); + let pools = self.connection_pools.read().await; + if let Some(pool_with_timestamp) = pools.get(vault) { + // Skip updating last_accessed here - it's only used for idle cleanup + // and will be updated when the pool is created or reused after recreation return Ok(pool_with_timestamp.pool.clone()); } } @@ -144,8 +145,8 @@ impl Database { // under high concurrency, but only one will be kept let new_pool = Self::create_vault_database(&self.config, vault).await?; - // Re-acquire lock and insert (or use existing if another task created it) - let mut pools = self.connection_pools.lock().await; + // Re-acquire lock (write) and insert (or use existing if another task created it) + let mut pools = self.connection_pools.write().await; let pool_with_timestamp = pools .entry(vault.clone()) .or_insert_with(|| PoolWithTimestamp { @@ -480,22 +481,19 @@ impl Database { Ok(()) } - /// Cleanup idle connection pools that haven't been accessed in more than 5 minutes async fn cleanup_idle_pools(&self) { - let mut pools = self.connection_pools.lock().await; - let now = Instant::now(); - let idle_timeout = Duration::from_secs(5 * 60); // 5 minutes + use crate::consts::IDLE_POOL_TIMEOUT; - // Collect vaults to remove + let mut pools = self.connection_pools.write().await; + let now = Instant::now(); let vaults_to_remove: Vec = pools .iter() .filter(|(_, pool_with_timestamp)| { - now.duration_since(pool_with_timestamp.last_accessed) > idle_timeout + now.duration_since(pool_with_timestamp.last_accessed) > IDLE_POOL_TIMEOUT }) .map(|(vault_id, _)| vault_id.clone()) .collect(); - // Close and remove idle pools for vault_id in &vaults_to_remove { if let Some(pool_with_timestamp) = pools.remove(vault_id) { info!("Closing idle database connection pool for vault `{vault_id}`"); diff --git a/sync-server/src/consts.rs b/sync-server/src/consts.rs index 9e9890c0..ca1d7fbf 100644 --- a/sync-server/src/consts.rs +++ b/sync-server/src/consts.rs @@ -7,6 +7,7 @@ pub const DEFAULT_CONFIG_PATH: &str = "config.yml"; pub const DEFAULT_DATABASES_DIRECTORY_PATH: &str = "databases"; pub const DEFAULT_MAX_CONNECTIONS_PER_VAULT: u32 = 12; pub const DEFAULT_CURSOR_TIMEOUT: Duration = Duration::from_secs(60); +pub const IDLE_POOL_TIMEOUT: Duration = Duration::from_secs(5 * 60); pub const DEFAULT_HOST: &str = "127.0.0.1"; pub const DEFAULT_PORT: u16 = 3000; From 6ea7d53a491d6c3b5e43acbcae16cb22def01506 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Mon, 12 Jan 2026 22:42:09 +0000 Subject: [PATCH 25/25] Migrate to using taskfiles --- .github/workflows/check.yml | 7 +- .github/workflows/deploy-docs.yml | 7 +- .github/workflows/e2e.yml | 17 ++-- .github/workflows/publish-plugin.yml | 18 ++-- CLAUDE.md | 132 ++++++++++++++++--------- Taskfile.yml | 96 ++++++++++++++++++ scripts/build-docs.sh | 14 --- scripts/build-sync-server-binaries.sh | 44 --------- scripts/bump-version.sh | 50 ---------- scripts/check.sh | 63 ------------ scripts/clean-up.sh | 4 - scripts/e2e.sh | 98 ------------------- scripts/update-api-types.sh | 18 ---- scripts/utils/check-node.sh | 11 --- scripts/utils/wait-for-server.sh | 24 ----- taskfiles/database.yml | 25 +++++ taskfiles/docs.yml | 35 +++++++ taskfiles/e2e.yml | 134 ++++++++++++++++++++++++++ taskfiles/frontend.yml | 41 ++++++++ taskfiles/release.yml | 44 +++++++++ taskfiles/rust.yml | 73 ++++++++++++++ 21 files changed, 564 insertions(+), 391 deletions(-) create mode 100644 Taskfile.yml delete mode 100755 scripts/build-docs.sh delete mode 100755 scripts/build-sync-server-binaries.sh delete mode 100755 scripts/bump-version.sh delete mode 100755 scripts/check.sh delete mode 100755 scripts/clean-up.sh delete mode 100755 scripts/e2e.sh delete mode 100755 scripts/update-api-types.sh delete mode 100755 scripts/utils/check-node.sh delete mode 100755 scripts/utils/wait-for-server.sh create mode 100644 taskfiles/database.yml create mode 100644 taskfiles/docs.yml create mode 100644 taskfiles/e2e.yml create mode 100644 taskfiles/frontend.yml create mode 100644 taskfiles/release.yml create mode 100644 taskfiles/rust.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index fc1b1c99..b6ff4921 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -32,5 +32,10 @@ jobs: toolchain: "1.92.0" components: clippy, rustfmt + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + - name: Lint & test - run: scripts/check.sh + run: task check diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index bb25e463..d39f0c00 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -34,11 +34,16 @@ jobs: node-version: "25.x" check-latest: true + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + - name: Setup Pages uses: actions/configure-pages@v4 - name: Build docs - run: scripts/build-docs.sh + run: task docs:check - name: Upload artifact uses: actions/upload-pages-artifact@v3 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 98dbfc1f..917dab69 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -37,12 +37,13 @@ jobs: toolchain: "1.92.0" components: clippy, rustfmt - - name: Setup rust - run: | - which sqlx || cargo install sqlx-cli - cd sync-server - sqlx database create --database-url sqlite://db.sqlite3 - sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + + - name: Setup database + run: task db:setup - name: E2E tests run: | @@ -51,7 +52,7 @@ jobs: SERVER_PID=$! cd .. - scripts/e2e.sh 8 + task e2e -- 8 EXIT_CODE=$? kill $SERVER_PID 2>/dev/null || true @@ -69,4 +70,4 @@ jobs: - name: Cleanup if: always() - run: scripts/clean-up.sh + run: task clean diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml index 452bc601..421dee5b 100644 --- a/.github/workflows/publish-plugin.yml +++ b/.github/workflows/publish-plugin.yml @@ -22,25 +22,27 @@ jobs: node-version: "25.x" check-latest: true - - name: Build plugin - run: | - cd frontend - npm ci - npm run build - - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: toolchain: "1.92.0" components: clippy, rustfmt + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + - name: Install cross-compilation tools run: | apt update apt install -y gcc-aarch64-linux-gnu musl-tools gcc-mingw-w64-x86-64 - - name: Build Linux and Windows binaries - run: ./scripts/build-sync-server-binaries.sh + - name: Build frontend + run: task frontend:build + + - name: Build binaries + run: task release:build-binaries - name: Create release env: diff --git a/CLAUDE.md b/CLAUDE.md index 323681d9..ef5ea994 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,8 +49,24 @@ The sync-client builds two separate bundles: ## Development Commands +This project uses [Taskfile](https://taskfile.dev/) for task automation. Run `task --list` to see all available tasks. + ### Initial Setup +**Taskfile:** + +```bash +# Install Task (https://taskfile.dev/installation/) +# macOS +brew install go-task + +# Linux +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin + +# Or via npm +npm install -g @go-task/cli +``` + **Node.js (requires version 25):** ```bash @@ -71,60 +87,89 @@ cargo install sqlx-cli cargo-machete cargo-edit cargo-insta **Frontend:** ```bash -cd frontend -npm install +task frontend:install ``` -### Server Development +### Common Tasks (Taskfile) + +```bash +task check # Full CI check (lint, test, format). Run before pushing. +task check:fix # Same as above but auto-fixes issues +task e2e -- 8 # E2E tests with 8 concurrent clients +task clean # Clean logs and database files +task update-api-types # Update TypeScript bindings from Rust types +task release:bump -- patch # Bump version (patch|minor|major) +``` + +### Server Tasks + +```bash +task rust:run # Start development server +task rust:test # Run all Rust tests +task rust:clippy # Lint Rust code +task rust:clippy-fix # Auto-fix clippy warnings +task rust:fmt # Format Rust code +task rust:fmt-check # Check Rust formatting +task rust:machete # Detect unused dependencies +``` + +### Frontend Tasks + +```bash +task frontend:dev # Start development mode +task frontend:build # Build all workspaces +task frontend:test # Run all frontend tests +task frontend:lint # Lint and format TypeScript +``` + +### Database Tasks + +```bash +task db:setup # Create and migrate database +task db:reset # Reset database (delete and recreate) +task db:prepare # Prepare SQLx offline data +task db:add-migration NAME= # Add new migration +``` + +### Documentation Tasks + +```bash +task docs:check # Build and check documentation +task docs:dev # Start documentation dev server +``` + +### Direct Commands (Alternative) + +If you prefer not to use Taskfile, these commands work directly: + +**Server:** ```bash cd sync-server cargo run config-e2e.yml # Start development server cargo test --verbose # Run all Rust tests -cargo test # Run specific test -cargo clippy --all-targets --all-features # Lint Rust code -cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged # Auto-fix clippy warnings -cargo fmt --all -- --check # Check Rust formatting -cargo fmt --all # Auto-format Rust code -cargo machete --with-metadata # Detect unused dependencies +cargo clippy --all-targets --all-features # Lint +cargo fmt --all # Format ``` -### Frontend Development +**Frontend:** ```bash cd frontend -npm run dev # Start development mode (watches sync-client and obsidian-plugin) +npm run dev # Development mode npm run build # Build all workspaces -npm run build -w sync-client # Build specific workspace -npm run test # Run all tests across all workspaces -npm run test -w sync-client # Run tests for specific workspace -npm run lint # Lint and format TypeScript code with ESLint + Prettier +npm run test # Run tests +npm run lint # Lint and format ``` -### Database Operations +**Database:** ```bash cd sync-server -# Create/reset database for development -rm -rf db.sqlite* sqlx database create --database-url sqlite://db.sqlite3 sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 -cargo sqlx prepare --workspace - -# Add new migration -sqlx migrate add --source src/app_state/database/migrations -sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 ``` -### Project Scripts - -- `scripts/check.sh`: Full CI check (builds, lints, tests both server and frontend). **Run before pushing.** -- `scripts/check.sh --fix`: Same as above but auto-fixes linting and formatting issues -- `scripts/e2e.sh`: End-to-end testing (e.g., `scripts/e2e.sh 8` for 8 concurrent clients) -- `scripts/clean-up.sh`: Clean logs and database files -- `scripts/bump-version.sh patch`: Publish new version (options: patch, minor, major) -- `scripts/update-api-types.sh`: Update TypeScript bindings from Rust types (uses ts-rs) - ## Code Structure ### Workspace Configuration @@ -141,7 +186,7 @@ The frontend uses npm workspaces with four packages: Rust structs generate TypeScript types via ts-rs crate: 1. Rust structs annotated with `#[derive(TS)]` export to `sync-server/bindings/` -2. Run `scripts/update-api-types.sh` to copy bindings to `frontend/sync-client/src/services/types/` +2. Run `task update-api-types` to copy bindings to `frontend/sync-client/src/services/types/` 3. Frontend imports these types for type-safe API communication ### Important Implementation Details @@ -156,25 +201,18 @@ Rust structs generate TypeScript types via ts-rs crate: ### Running Tests -**Server:** - ```bash -cargo test --verbose # All tests -cargo test # Specific test +task rust:test # All Rust tests +task frontend:test # All frontend tests +task e2e -- 8 # E2E with 8 concurrent clients +task clean # Clean up after tests ``` -**Frontend:** +Or use direct commands: ```bash -npm run test # All workspaces -npm run test -w sync-client # Specific workspace -``` - -**E2E:** - -```bash -scripts/e2e.sh 8 # 8 concurrent clients -scripts/clean-up.sh # Clean up after tests +cd sync-server && cargo test --verbose # Rust tests +cd frontend && npm run test # Frontend tests ``` ### Test Structure diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 00000000..0d7a1f1e --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,96 @@ +version: "3" + +vars: + NODE_VERSION: "25" + +includes: + rust: + taskfile: ./taskfiles/rust.yml + dir: ./sync-server + frontend: + taskfile: ./taskfiles/frontend.yml + dir: ./frontend + db: + taskfile: ./taskfiles/database.yml + dir: ./sync-server + e2e: + taskfile: ./taskfiles/e2e.yml + docs: + taskfile: ./taskfiles/docs.yml + dir: ./docs + release: + taskfile: ./taskfiles/release.yml + +tasks: + check: + desc: Run all checks (lint, test, format) + cmds: + - task: check-node + - task: db:setup + - task: rust:test + - task: rust:lint + - task: update-api-types + - task: frontend:install + - task: frontend:build + - task: frontend:test + - task: frontend:lint + - task: format + - task: check-clean + + check:fix: + desc: Run all checks with auto-fix enabled + cmds: + - task: check-node + - task: db:setup + - task: rust:test + - task: rust:lint-fix + - task: update-api-types + - task: frontend:install + - task: frontend:build + - task: frontend:test + - task: frontend:lint + - task: format + + check-node: + internal: true + silent: true + preconditions: + - sh: node -v | grep -q "^v{{.NODE_VERSION}}" + msg: "Node.js {{.NODE_VERSION}} required (found: $(node -v))" + cmds: + - echo "Node.js {{.NODE_VERSION}} confirmed" + + check-clean: + internal: true + preconditions: + - sh: test -z "$(git status --porcelain)" + msg: | + Working directory not clean after linting: + $(git status --porcelain) + + format: + desc: Format all files with Prettier + dir: "{{.ROOT_DIR}}" + cmds: + - npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" + + update-api-types: + desc: Update TypeScript bindings from Rust types + cmds: + - rm -rf sync-server/bindings + - task: rust:export-bindings + - cp -r sync-server/bindings/* frontend/sync-client/src/services/types/ + - cd frontend && npm run lint + - task: format + + clean: + desc: Clean up logs and databases + cmds: + - rm -rf sync-server/databases logs + + e2e: + desc: Run E2E tests (usage - task e2e -- 8) + cmds: + - task: e2e:run + vars: + PROCESS_COUNT: "{{.CLI_ARGS}}" diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh deleted file mode 100755 index c87144a9..00000000 --- a/scripts/build-docs.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -set -e - -./scripts/utils/check-node.sh - -cd docs - -npm ci -npm run format:check -npm run spell:check -npm run build - -cd - diff --git a/scripts/build-sync-server-binaries.sh b/scripts/build-sync-server-binaries.sh deleted file mode 100755 index 8d690935..00000000 --- a/scripts/build-sync-server-binaries.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")/../sync-server" - -# Setup database -sqlx database create --database-url sqlite://db.sqlite3 2>/dev/null || true -sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 - -targets=${@:-"x86_64-unknown-linux-gnu x86_64-unknown-linux-musl aarch64-unknown-linux-gnu x86_64-pc-windows-gnu"} - -mkdir -p artifacts -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) - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc ;; - x86_64-unknown-linux-musl) - export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc ;; - 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/scripts/bump-version.sh b/scripts/bump-version.sh deleted file mode 100755 index bea3d982..00000000 --- a/scripts/bump-version.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -set -e - -if [[ -z $1 ]]; then - echo "Usage: $0 {patch|minor|major}" - exit 1 -fi - -if [[ $1 =~ ^(patch|minor|major)$ ]]; then - echo "Creating a new '$1' version" -else - echo "Invalid argument: $1" - echo "Usage: $0 {patch|minor|major}" - exit 1 -fi - -cargo install cargo-edit --force - -if [[ -n $(git status --porcelain) ]]; then - echo "Your working directory is not clean. Please commit or stash your changes before proceeding." - exit 1 -else - echo "Your working directory is clean." -fi - -echo "Bumping sync-server versions" -cd sync-server -cargo set-version --bump $1 - -echo "Bumping frontend versions" -cd ../frontend -npm version $1 --workspaces -cd .. - -cp frontend/obsidian-plugin/manifest.json manifest.json # for BRAT, otherwise it wouldn't update - -# Format all files across the project (frontend and backend) -npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" - -# Commit and tag -git add . -TAG=$(node -p "require('./frontend/obsidian-plugin/package.json').version") -git commit -m "Bump versions to $TAG" - -git push -echo "Tagging $TAG" -git tag -a $TAG -m "Release $TAG" -git push origin $TAG -echo "Done" diff --git a/scripts/check.sh b/scripts/check.sh deleted file mode 100755 index 2ee0dd62..00000000 --- a/scripts/check.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -set -e - -FIX_MODE=false -if [[ "$1" == "--fix" ]]; then - FIX_MODE=true - echo "Running in fix mode - will automatically fix linting and formatting issues" -fi - -./scripts/utils/check-node.sh - -echo "Running checks in sync-server" - -cd sync-server -which sqlx || cargo install sqlx-cli -sqlx database create --database-url sqlite://db.sqlite3 -sqlx migrate run --source src/app_state/database/migrations --database-url sqlite://db.sqlite3 - -cargo test --verbose - -if [[ "$FIX_MODE" == true ]]; then - cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged - cargo fmt --all -else - cargo clippy --all-targets --all-features - cargo fmt --all -- --check -fi - -which cargo-machete || cargo install cargo-machete -cargo machete --with-metadata - -cd .. -scripts/update-api-types.sh # this will dirty up the git state if not up-to-date - -echo "Running checks in frontend" -cd frontend - -if [[ "$FIX_MODE" == true ]]; then - npm install -else - npm ci -fi - -cd .. - -cd frontend -npm run build -npm run test -npm run lint -cd .. - -# Format all files across the project (frontend and backend) -# Prettier respects .gitignore by default -npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" - -if [[ "$FIX_MODE" == false ]] && [[ $(git status --porcelain) ]]; then - git status --porcelain - echo "Failing CI because the working directory is not clean after linting" - exit 1 -fi - -echo "Success" diff --git a/scripts/clean-up.sh b/scripts/clean-up.sh deleted file mode 100755 index 4dfbf4a0..00000000 --- a/scripts/clean-up.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -rm -rf sync-server/databases -rm -rf logs diff --git a/scripts/e2e.sh b/scripts/e2e.sh deleted file mode 100755 index d0e23260..00000000 --- a/scripts/e2e.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -set -e -set -o pipefail - -NO_COLOR=1 -FORCE_COLOR=0 - -./scripts/utils/check-node.sh - -# Check if the argument is provided -if [ $# -eq 0 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Get the number of processes from the first argument -process_count=$1 - -mkdir -p logs - -cd frontend -npm ci -npm run build - -../scripts/utils/wait-for-server.sh - -pids=() -for i in $(seq 1 $process_count); do - # Create a named pipe for this process - pipe="/tmp/vaultlink_pipe_$$_$i" - mkfifo "$pipe" - - # Start the node process writing to the pipe - node test-client/dist/cli.js > "$pipe" 2>&1 & - pid=$! - pids+=($pid) - echo "Started process $i with PID: $pid" - - # Read from pipe, prefix with PID - (sed "s/^/[PID $pid] /" < "$pipe" > "../logs/log_${i}.log"; rm "$pipe") & -done - -cd .. - -print_failed_log() { - for i in $(seq 1 $process_count); do - if [ -n "${pids[$i-1]}" ] && ! kill -0 ${pids[$i-1]} 2>/dev/null; then - # Get the exit code of the process - wait ${pids[$i-1]} - exit_code=$? - - # Only consider non-zero exit codes as failures - if [ $exit_code -ne 0 ]; then - echo "----- Log for process ${pids[$i-1]} (log_${i}.log) -----" - cat "$(pwd)/logs/log_${i}.log" - echo "Process ${pids[$i-1]} failed with exit code $exit_code. Log file: $(pwd)/logs/log_${i}.log" - return 0 - else - echo "Process ${pids[$i-1]} completed successfully with exit code 0" - # Mark this PID as processed by setting it to empty - pids[$i-1]="" - fi - fi - done - return 1 -} - -echo "Monitoring $process_count processes" - -# Monitor processes -while true; do - if print_failed_log; then - # Kill remaining processes - for pid in "${pids[@]}"; do - if [ -n "$pid" ]; then - kill $pid 2>/dev/null || true - fi - done - exit 1 - fi - - # Check if all processes have completed - all_done=true - for pid in "${pids[@]}"; do - if [ -n "$pid" ] && kill -0 $pid 2>/dev/null; then - all_done=false - break - fi - done - - if $all_done; then - echo "All processes completed successfully" - exit 0 - fi - - sleep 0.2 -done diff --git a/scripts/update-api-types.sh b/scripts/update-api-types.sh deleted file mode 100755 index 36ca100d..00000000 --- a/scripts/update-api-types.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -rm -rf sync-server/bindings - -cd sync-server -cargo test export_bindings -cd - - -cp -r sync-server/bindings/* frontend/sync-client/src/services/types/ - -cd frontend -npm run lint -cd .. - -# Format all files across the project (frontend and backend) -npx -C frontend prettier --write "**/*.{ts,js,json,md,yml,yaml}" diff --git a/scripts/utils/check-node.sh b/scripts/utils/check-node.sh deleted file mode 100755 index d93f2f27..00000000 --- a/scripts/utils/check-node.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e - -TARGET_NODE_VERSION=25 - -node_version=$(node -v | sed 's/^v\([0-9]*\).*/\1/') -if [ "$node_version" != "$TARGET_NODE_VERSION" ]; then - echo "Error: This script requires Node.js version $TARGET_NODE_VERSION, found: $node_version" - exit 1 -fi diff --git a/scripts/utils/wait-for-server.sh b/scripts/utils/wait-for-server.sh deleted file mode 100755 index 7824c405..00000000 --- a/scripts/utils/wait-for-server.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -e - -SERVER_URL="http://localhost:3000" -MAX_RETRIES=30 -RETRY_INTERVAL_IN_SECONDS=5 - -echo "Waiting for $SERVER_URL to become available..." -count=0 -while [ $count -lt $MAX_RETRIES ]; do - if curl -s -f -o /dev/null $SERVER_URL; then - echo "$SERVER_URL is now available!" - break - fi - echo "Attempt $(($count+1))/$MAX_RETRIES: $SERVER_URL not available yet, retrying in ${RETRY_INTERVAL_IN_SECONDS}s..." - sleep $RETRY_INTERVAL_IN_SECONDS - count=$(($count+1)) -done - -if [ $count -eq $MAX_RETRIES ]; then - echo "Error: $SERVER_URL did not become available after $MAX_RETRIES attempts." - exit 1 -fi diff --git a/taskfiles/database.yml b/taskfiles/database.yml new file mode 100644 index 00000000..3ed4f6d1 --- /dev/null +++ b/taskfiles/database.yml @@ -0,0 +1,25 @@ +version: "3" + +vars: + DATABASE_URL: "sqlite://db.sqlite3" + MIGRATIONS_PATH: "src/app_state/database/migrations" + +tasks: + setup: + desc: Create and migrate database + sources: + - "{{.MIGRATIONS_PATH}}/**/*.sql" + generates: + - "db.sqlite3" + cmds: + - which sqlx || cargo install sqlx-cli + - sqlx database create --database-url {{.DATABASE_URL}} 2>/dev/null || true + - sqlx migrate run --source {{.MIGRATIONS_PATH}} --database-url {{.DATABASE_URL}} + - cargo sqlx prepare --workspace + + add-migration: + desc: Add a new migration + requires: + vars: [NAME] + cmds: + - sqlx migrate add --source {{.MIGRATIONS_PATH}} {{.NAME}} diff --git a/taskfiles/docs.yml b/taskfiles/docs.yml new file mode 100644 index 00000000..06351b5e --- /dev/null +++ b/taskfiles/docs.yml @@ -0,0 +1,35 @@ +version: "3" + +tasks: + install: + desc: Install docs dependencies + run: once + cmds: + - npm ci + + build: + desc: Build documentation + deps: [install] + cmds: + - npm run build + + lint: + desc: Check formatting and spelling + deps: [install] + cmds: + - npm run format:check + - npm run spell:check + + check: + desc: Run all documentation checks + cmds: + - task: :check-node + - task: install + - task: lint + - task: build + + dev: + desc: Start documentation dev server + deps: [install] + cmds: + - npm run dev diff --git a/taskfiles/e2e.yml b/taskfiles/e2e.yml new file mode 100644 index 00000000..5e38e45a --- /dev/null +++ b/taskfiles/e2e.yml @@ -0,0 +1,134 @@ +version: "3" + +vars: + SERVER_URL: "http://localhost:3000" + MAX_RETRIES: 30 + RETRY_INTERVAL: 5 + LOG_DIR: "{{.ROOT_DIR}}/logs" + +tasks: + run: + desc: Run E2E tests with specified number of processes + summary: | + Runs multiple concurrent test clients against the sync server. + Each client performs random file operations to test synchronization. + + Usage: task e2e -- + Example: task e2e -- 8 + deps: [prepare] + requires: + vars: [PROCESS_COUNT] + preconditions: + - sh: test "{{.PROCESS_COUNT}}" -ge 1 2>/dev/null + msg: "PROCESS_COUNT must be a positive integer (got: {{.PROCESS_COUNT}})" + dir: "{{.ROOT_DIR}}" + env: + NO_COLOR: "1" + FORCE_COLOR: "0" + cmds: + - task: wait-for-server + - task: setup-logs + - defer: { task: cleanup-pipes } + - task: spawn-clients + + prepare: + desc: Build frontend for E2E tests + internal: true + dir: "{{.ROOT_DIR}}" + cmds: + - task: :check-node + - task: :frontend:build + + wait-for-server: + desc: Wait for server to become available + internal: true + silent: true + cmds: + - for: { var: ATTEMPTS, split: "\n" } + cmd: | + if curl -s -f -o /dev/null {{.SERVER_URL}}; then + echo "Server available at {{.SERVER_URL}}" + exit 0 + fi + echo "Attempt {{.ITEM}}/{{.MAX_RETRIES}}: waiting {{.RETRY_INTERVAL}}s..." + sleep {{.RETRY_INTERVAL}} + if [ "{{.ITEM}}" = "{{.MAX_RETRIES}}" ]; then + echo "Error: Server not available after {{.MAX_RETRIES}} attempts" + exit 1 + fi + vars: + ATTEMPTS: + sh: seq 1 {{.MAX_RETRIES}} + + setup-logs: + internal: true + status: + - test -d {{.LOG_DIR}} + cmds: + - mkdir -p {{.LOG_DIR}} + + cleanup-pipes: + internal: true + cmds: + - rm -f /tmp/vaultlink_pipe_* 2>/dev/null || true + + spawn-clients: + internal: true + dir: "{{.ROOT_DIR}}/frontend" + set: [errexit, pipefail] + cmds: + - | + pids=() + + # Start all client processes + for i in $(seq 1 {{.PROCESS_COUNT}}); do + pipe="/tmp/vaultlink_pipe_$$_$i" + mkfifo "$pipe" + node test-client/dist/cli.js > "$pipe" 2>&1 & + pid=$! + pids+=($pid) + echo "Started client $i (PID: $pid)" + (sed "s/^/[PID $pid] /" < "$pipe" > "{{.LOG_DIR}}/log_${i}.log"; rm "$pipe") & + done + + echo "Monitoring {{.PROCESS_COUNT}} client processes..." + + # Monitor loop + while true; do + # Check for failures + for i in $(seq 1 {{.PROCESS_COUNT}}); do + idx=$((i-1)) + pid=${pids[$idx]} + [ -z "$pid" ] && continue + + if ! kill -0 $pid 2>/dev/null; then + wait $pid + code=$? + if [ $code -ne 0 ]; then + echo "Client $i (PID $pid) failed with exit code $code" + echo "===== Log: {{.LOG_DIR}}/log_${i}.log =====" + cat "{{.LOG_DIR}}/log_${i}.log" + # Kill remaining processes + for p in "${pids[@]}"; do + [ -n "$p" ] && kill $p 2>/dev/null || true + done + exit 1 + fi + echo "Client $i (PID $pid) completed successfully" + pids[$idx]="" + fi + done + + # Check if all done + all_done=true + for pid in "${pids[@]}"; do + [ -n "$pid" ] && kill -0 $pid 2>/dev/null && all_done=false && break + done + + if $all_done; then + echo "All {{.PROCESS_COUNT}} clients completed successfully" + exit 0 + fi + + sleep 0.2 + done diff --git a/taskfiles/frontend.yml b/taskfiles/frontend.yml new file mode 100644 index 00000000..7c0e5280 --- /dev/null +++ b/taskfiles/frontend.yml @@ -0,0 +1,41 @@ +version: "3" + +tasks: + install: + desc: Install frontend dependencies + run: once + cmds: + - npm ci + + build: + desc: Build all frontend workspaces + deps: [install] + cmds: + - npm run build + + test: + desc: Run all frontend tests + cmds: + - npm run test + + lint: + desc: Lint and format TypeScript code + cmds: + - npm run lint + + dev: + desc: Start development mode + cmds: + - npm run dev + + workspace: + desc: Run npm script in specific workspace + summary: | + Run any npm script in a specific workspace. + + Usage: task frontend:workspace WORKSPACE= SCRIPT=