From 1576b0da8bdde75d510c38b8faa46c3935f5b6a0 Mon Sep 17 00:00:00 2001 From: flamboh Date: Thu, 24 Sep 2026 02:25:02 -0700 Subject: [PATCH 1/7] feat(web)!: migrate to SvelteKit 3 and Vite 8 with build-time DB driver - Upgrade to @sveltejs/kit 3.0.0-next.27, adapter-cloudflare 8.0.0-next.7, Vite 8, vite-plugin-svelte 7, TypeScript 6, and Vitest 5. - Move kit config into vite.config.ts, replace $lib with #lib subpath imports, extend $app/tsconfig, and declare env vars in src/env.ts. - Select the database driver at build time through the #db subpath import: the atlantis-d1 condition resolves the D1 driver (cloudflare:workers), the default resolves the SQLite driver. Routes no longer thread platform. - Pair D1 with adapter-cloudflare and SQLite with no adapter, so plain dev never starts workerd and SQLite builds never overwrite the worker output. - Patch runed useSearchParams to use the kit 3 goto options. - Build a SQLite bundle inside the Playwright web server and run Node 24 in the nix shell to meet the kit 3 Node minimum. --- .cursor/rules/svelte.mdc | 7 +- .env.example | 4 +- .github/workflows/ci.yaml | 2 - .node-version | 2 +- AGENTS.md | 2 +- apps/web/eslint.config.js | 4 +- apps/web/package.json | 27 +- apps/web/playwright.config.js | 4 +- apps/web/src/app.d.ts | 11 - apps/web/src/cloudflare-workers.d.ts | 5 + apps/web/src/env.ts | 18 + .../components/charts/BreakdownChart.svelte | 28 +- .../lib/components/charts/ChartCard.svelte | 4 +- .../components/charts/ChartContainer.svelte | 14 +- .../components/charts/CoverageStrip.svelte | 16 +- .../charts/FlowCharacteristicsChart.svelte | 4 +- .../components/charts/MetricLinePanel.svelte | 14 +- .../charts/PortCardinalityChart.svelte | 6 +- .../components/charts/SpectrumChart.svelte | 4 +- .../charts/StructureFunctionChart.svelte | 4 +- .../charts/breakdown-chart-config.ts | 4 +- .../src/lib/components/charts/chart-utils.ts | 6 +- .../lib/components/charts/coverage-strip.ts | 6 +- .../lib/components/charts/crosshair-plugin.ts | 2 +- .../flow-characteristics-data.svelte.ts | 8 +- .../components/charts/flow-characteristics.ts | 2 +- .../src/lib/components/charts/ip-time-axis.ts | 4 +- .../components/common/SegmentedControl.svelte | 6 +- .../components/datasets/DatasetTabs.svelte | 4 +- .../components/filters/DirectionFilter.svelte | 4 +- .../filters/MaadIpVersionFilter.svelte | 4 +- .../filters/MaadMeasureFilter.svelte | 4 +- .../components/filters/MetricSelector.svelte | 8 +- .../components/filters/PrimaryFilters.svelte | 24 +- .../components/filters/RouterFilter.svelte | 6 +- .../netflow/DatasetDashboardPage.svelte | 34 +- .../netflow/NetflowDashboard.svelte | 16 +- .../netflow/NetflowFileAnalysisPane.svelte | 8 +- .../netflow/NetflowFileDetailPage.svelte | 32 +- .../netflow/NetflowFileHeader.svelte | 8 +- .../netflow/NetflowFileLoadingSkeleton.svelte | 4 +- .../netflow/NetflowFileMessageCard.svelte | 4 +- .../NetflowFileRouterAnalysisSection.svelte | 2 +- .../netflow/NetflowFileRouterCard.svelte | 2 +- .../netflow/NetflowFileRouterSummary.svelte | 4 +- .../src/lib/components/netflow/constants.ts | 4 +- .../netflow/file-detail-loader.svelte.ts | 2 +- apps/web/src/lib/components/netflow/types.ts | 2 +- .../lib/components/ui/button/button.svelte | 2 +- .../lib/components/ui/card/card-action.svelte | 2 +- .../components/ui/card/card-content.svelte | 2 +- .../ui/card/card-description.svelte | 2 +- .../lib/components/ui/card/card-footer.svelte | 2 +- .../lib/components/ui/card/card-header.svelte | 2 +- .../lib/components/ui/card/card-title.svelte | 2 +- .../src/lib/components/ui/card/card.svelte | 2 +- .../components/ui/checkbox/checkbox.svelte | 2 +- .../ui/select/select-content.svelte | 4 +- .../ui/select/select-group-heading.svelte | 2 +- .../components/ui/select/select-group.svelte | 2 +- .../components/ui/select/select-item.svelte | 2 +- .../components/ui/select/select-label.svelte | 2 +- .../select/select-scroll-down-button.svelte | 2 +- .../ui/select/select-scroll-up-button.svelte | 2 +- .../ui/select/select-separator.svelte | 4 +- .../ui/select/select-trigger.svelte | 2 +- .../components/ui/separator/separator.svelte | 2 +- .../components/ui/skeleton/skeleton.svelte | 2 +- .../lib/components/ui/table/table-body.svelte | 2 +- .../components/ui/table/table-caption.svelte | 2 +- .../lib/components/ui/table/table-cell.svelte | 2 +- .../components/ui/table/table-footer.svelte | 2 +- .../lib/components/ui/table/table-head.svelte | 2 +- .../components/ui/table/table-header.svelte | 2 +- .../lib/components/ui/table/table-row.svelte | 2 +- .../src/lib/components/ui/table/table.svelte | 2 +- .../components/ui/tabs/tabs-content.svelte | 2 +- .../lib/components/ui/tabs/tabs-list.svelte | 2 +- .../components/ui/tabs/tabs-trigger.svelte | 2 +- .../src/lib/components/ui/tabs/tabs.svelte | 2 +- .../ui/tooltip/tooltip-content.svelte | 4 +- apps/web/src/lib/datasets.ts | 4 +- apps/web/src/lib/index.ts | 2 +- apps/web/src/lib/schemas.ts | 2 +- apps/web/src/lib/server/alerts.ts | 432 +----------- apps/web/src/lib/server/datasets.ts | 667 ++---------------- apps/web/src/lib/server/db/coverage.ts | 6 +- apps/web/src/lib/server/db/d1.ts | 94 +++ apps/web/src/lib/server/db/driver.ts | 60 ++ apps/web/src/lib/server/db/local-files.ts | 48 ++ apps/web/src/lib/server/db/sqlite-alerts.ts | 362 ++++++++++ apps/web/src/lib/server/db/sqlite.ts | 377 ++++++++++ apps/web/src/lib/server/netflow-v3.ts | 6 +- apps/web/src/lib/stores/crosshair.ts | 2 +- .../src/lib/utils/netflow-file-navigation.ts | 2 +- apps/web/src/routes/+error.svelte | 6 +- apps/web/src/routes/+layout.svelte | 6 +- apps/web/src/routes/+page.svelte | 4 +- apps/web/src/routes/+page.ts | 2 +- apps/web/src/routes/api/alerts/+server.ts | 11 +- apps/web/src/routes/api/datasets/+server.ts | 8 +- apps/web/src/routes/api/ip/stats/+server.ts | 14 +- .../api/netflow/characteristics/+server.ts | 18 +- .../routes/api/netflow/coverage/+server.ts | 16 +- .../api/netflow/dimension-stats/+server.ts | 14 +- .../netflow/files/[slug]/details/+server.ts | 10 +- .../netflow/files/[slug]/ip-counts/+server.ts | 8 +- .../netflow/files/[slug]/spectrum/+server.ts | 10 +- .../netflow/files/[slug]/structure/+server.ts | 10 +- .../routes/api/netflow/files/[slug]/utils.ts | 26 +- .../routes/api/netflow/maad-status/+server.ts | 10 +- .../api/netflow/spectrum-stats/+server.ts | 14 +- .../src/routes/api/netflow/stats/+server.ts | 16 +- .../api/netflow/structure-stats/+server.ts | 14 +- .../src/routes/api/protocol/stats/+server.ts | 14 +- apps/web/src/routes/api/routers/+server.ts | 8 +- .../routes/datasets/[dataset]/+page.svelte | 2 +- .../src/routes/datasets/[dataset]/+page.ts | 2 +- .../datasets/[dataset]/alerts/+page.svelte | 12 +- .../routes/datasets/[dataset]/alerts/+page.ts | 4 +- .../web/src/routes/netflow/files/+page.svelte | 6 +- apps/web/src/routes/netflow/files/+page.ts | 2 +- .../netflow/files/[slug]/+page.server.ts | 4 +- .../routes/netflow/files/[slug]/+page.svelte | 2 +- apps/web/svelte.config.js | 25 - .../tests/lib/netflow-file-navigation.test.ts | 16 +- apps/web/tests/lib/server/datasets.test.ts | 52 +- apps/web/tests/lib/server/db/coverage.test.ts | 4 +- apps/web/tests/performance/seed-large-db.ts | 2 - .../tests/routes/api-aggregate-routes.test.ts | 6 +- apps/web/tests/routes/api-alerts.test.ts | 7 +- apps/web/tests/routes/api-datasets.test.ts | 4 +- .../routes/api-flow-characteristics.test.ts | 6 +- .../tests/routes/api-maad-dimensions.test.ts | 8 +- .../tests/routes/api-netflow-coverage.test.ts | 8 +- .../tests/routes/api-netflow-stats.test.ts | 6 +- .../tests/routes/netflow-file-routes.test.ts | 10 +- apps/web/tests/setup/private-env.ts | 13 + apps/web/tsconfig.json | 26 +- apps/web/vite.config.ts | 72 +- bun.lock | 486 ++++++++----- docs/code/architecture.md | 6 +- docs/code/development.md | 30 + docs/user/operations.md | 13 +- docs/user/requirements.md | 2 +- docs/user/setup-web.md | 2 + package.json | 5 +- patches/runed@0.36.0.patch | 43 ++ shell.nix | 2 +- 149 files changed, 1946 insertions(+), 1711 deletions(-) create mode 100644 apps/web/src/cloudflare-workers.d.ts create mode 100644 apps/web/src/env.ts create mode 100644 apps/web/src/lib/server/db/d1.ts create mode 100644 apps/web/src/lib/server/db/driver.ts create mode 100644 apps/web/src/lib/server/db/local-files.ts create mode 100644 apps/web/src/lib/server/db/sqlite-alerts.ts create mode 100644 apps/web/src/lib/server/db/sqlite.ts delete mode 100644 apps/web/svelte.config.js create mode 100644 apps/web/tests/setup/private-env.ts create mode 100644 patches/runed@0.36.0.patch diff --git a/.cursor/rules/svelte.mdc b/.cursor/rules/svelte.mdc index 7756e65c..9304b187 100644 --- a/.cursor/rules/svelte.mdc +++ b/.cursor/rules/svelte.mdc @@ -67,8 +67,8 @@ UI and Styling - Use Tailwind CSS for utility-first styling approach. - Leverage Shadcn components for pre-built, customizable UI elements. -- Import Shadcn components from `$lib/components/ui`. -- Organize Tailwind classes using the `cn()` utility from `$lib/utils`. +- Import Shadcn components from `#lib/components/ui//index.ts`. +- Organize Tailwind classes using the `cn()` utility from `#lib/utils.ts`. - Use Svelte's built-in transition and animation features. Shadcn Color Conventions @@ -106,8 +106,7 @@ SvelteKit Project Structure - routes/ - app.html - static/ - - svelte.config.js - - vite.config.js + - vite.config.ts ``` Component Development diff --git a/.env.example b/.env.example index 41e865db..1fbb814f 100644 --- a/.env.example +++ b/.env.example @@ -8,8 +8,8 @@ DEFAULT_DATASET=uoregon # The dataset registry can be at a different location. # DATASETS_CONFIG_PATH=./datasets.json -# The local web application reads SQLite by default. -ATLANTIS_DB_DRIVER=sqlite +# ATLANTIS_DB_DRIVER selects the web database driver when the dashboard starts or builds. +# Set it in the shell, not in this file. See docs/code/development.md. # Set one database path or use automatic discovery at data/*/netflow.sqlite. # LOCAL_SQLITE_PATH=/absolute/path/to/netflow.sqlite diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b8f9df72..5e0dfe4e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -116,8 +116,6 @@ jobs: - run: bun install --frozen-lockfile - - run: bun run build:web - - run: cd apps/web && bunx playwright install --with-deps chromium - run: cd apps/web && PLAYWRIGHT_WEB_SERVER=1 bun run test:e2e diff --git a/.node-version b/.node-version index 5b540673..9d11232a 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.16.0 +24.4.1 diff --git a/AGENTS.md b/AGENTS.md index 3e57a385..34e2fbba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ ATLANTIS turns NetFlow captures and CSV imports into queryable aggregate databas ## Repository Map - `tools/netflow-db`: Rust pipeline (`atlantis-netflow-db` crate) for ingestion, aggregation, verification, and analysis-window exports. Native `nfcapd` ingestion uses the pinned `nfdump` fork in `vendor/nfdump`. -- `apps/web`: Svelte 5/SvelteKit 2 dashboard and API routes. It reads local SQLite during development and Cloudflare D1 in deployment. +- `apps/web`: Svelte 5/SvelteKit 3 dashboard and API routes. It reads local SQLite during development and Cloudflare D1 in deployment. - `apps/landing`: Astro marketing and SEO site. - `vendor/*`: Third-party analysis submodules. Treat these as read-only; build repo-local binaries through the scripts in `vendor/scripts/`. - `data/`, `.env`, and `datasets.json`: Machine-local inputs and generated databases. Keep paths and dataset contents out of commits. diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index af7a9e2d..fd4aad16 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -3,7 +3,9 @@ import js from '@eslint/js'; import svelte from 'eslint-plugin-svelte'; import globals from 'globals'; import ts from 'typescript-eslint'; -import svelteConfig from './svelte.config.js'; +import { loadConfig } from '@sveltejs/load-config'; + +const svelteConfig = (await loadConfig('./', { traverse: false }))?.config; export default ts.config( js.configs.recommended, diff --git a/apps/web/package.json b/apps/web/package.json index 584bf014..9a920e41 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -28,14 +28,15 @@ "d1:execute": "wrangler d1 execute atlantis-db" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260511.1", + "@cloudflare/workers-types": "^5.20260924.1", "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", "@internationalized/date": "^3.12.0", "@playwright/test": "1.52.0", - "@sveltejs/adapter-cloudflare": "^7.2.9", - "@sveltejs/kit": "^2.70.3", - "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@sveltejs/adapter-cloudflare": "8.0.0-next.7", + "@sveltejs/kit": "3.0.0-next.27", + "@sveltejs/load-config": "^0.2.3", + "@sveltejs/vite-plugin-svelte": "^7.3.1", "@tailwindcss/vite": "^4.0.0", "@types/better-sqlite3": "^7.6.13", "@types/node": "^22.15.17", @@ -55,11 +56,11 @@ "tailwind-variants": "^3.3.1", "tailwindcss": "^4.0.0", "tw-animate-css": "^1.4.0", - "typescript": "^5.0.0", - "typescript-eslint": "^8.20.0", - "vite": "^7.3.6", - "vitest": "^3.2.7", - "wrangler": "^4.90.0" + "typescript": "^6.0.3", + "typescript-eslint": "^8.70.1", + "vite": "^8.3.0", + "vitest": "^5.0.1", + "wrangler": "^4.137.0" }, "dependencies": { "@lucide/svelte": "^1.33.0", @@ -69,5 +70,13 @@ "drizzle-orm": "^0.45.2", "runed": "^0.36.0", "zod": "^4.3.6" + }, + "imports": { + "#lib": "./src/lib/index.ts", + "#lib/*": "./src/lib/*", + "#db": { + "atlantis-d1": "./src/lib/server/db/d1.ts", + "default": "./src/lib/server/db/sqlite.ts" + } } } diff --git a/apps/web/playwright.config.js b/apps/web/playwright.config.js index 45cde3da..0ec309b9 100644 --- a/apps/web/playwright.config.js +++ b/apps/web/playwright.config.js @@ -37,7 +37,7 @@ export default defineConfig({ ], webServer: shouldManageServer ? { - command: 'bun run preview --host 127.0.0.1 --port 4173', + command: 'bun run build && bun run preview --host 127.0.0.1 --port 4173', env: { ...process.env, ATLANTIS_DB_DRIVER: 'sqlite', @@ -45,7 +45,7 @@ export default defineConfig({ }, port: 4173, reuseExistingServer: false, - timeout: 120_000 + timeout: 180_000 } : undefined }); diff --git a/apps/web/src/app.d.ts b/apps/web/src/app.d.ts index 02993758..749ba9fb 100644 --- a/apps/web/src/app.d.ts +++ b/apps/web/src/app.d.ts @@ -1,5 +1,3 @@ -import type { D1Database } from '@cloudflare/workers-types'; - // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { @@ -8,15 +6,6 @@ declare global { // interface Locals {} // interface PageData {} // interface PageState {} - interface Platform { - env: { - DB: D1Database; - }; - context: { - waitUntil(promise: Promise): void; - }; - caches: CacheStorage & { default: Cache }; - } } } diff --git a/apps/web/src/cloudflare-workers.d.ts b/apps/web/src/cloudflare-workers.d.ts new file mode 100644 index 00000000..ec9ee23a --- /dev/null +++ b/apps/web/src/cloudflare-workers.d.ts @@ -0,0 +1,5 @@ +declare module 'cloudflare:workers' { + export const env: { + DB: import('@cloudflare/workers-types').D1Database; + }; +} diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts new file mode 100644 index 00000000..49025812 --- /dev/null +++ b/apps/web/src/env.ts @@ -0,0 +1,18 @@ +import { defineEnvVars } from '@sveltejs/kit/env'; + +const optional = (value: string | undefined) => value?.trim() || undefined; + +export const variables = defineEnvVars({ + LOCAL_SQLITE_PATH: { + description: 'Path to a single pipeline SQLite product; disables data/ discovery when set', + schema: optional + }, + DATABASE_PATH: { + description: 'Fallback for LOCAL_SQLITE_PATH', + schema: optional + }, + DEFAULT_DATASET: { + description: 'Dataset ID selected when a request does not name one', + schema: optional + } +}); diff --git a/apps/web/src/lib/components/charts/BreakdownChart.svelte b/apps/web/src/lib/components/charts/BreakdownChart.svelte index dc409567..8b39e364 100644 --- a/apps/web/src/lib/components/charts/BreakdownChart.svelte +++ b/apps/web/src/lib/components/charts/BreakdownChart.svelte @@ -5,12 +5,12 @@ import { buildCoveragePointStyle } from './coverage-line-style'; import { getRelativePosition } from 'chart.js/helpers'; import type { ActiveElement, ChartEvent } from 'chart.js'; - import type { GroupByOption, RouterConfig } from '$lib/components/netflow/types.ts'; + import type { GroupByOption, RouterConfig } from '#lib/components/netflow/types.ts'; import ChartCard from './ChartCard.svelte'; - import { Checkbox } from '$lib/components/ui/checkbox'; - import { Skeleton } from '$lib/components/ui/skeleton'; - import SegmentedControl from '$lib/components/common/SegmentedControl.svelte'; - import { navigateToNetflowFile } from '$lib/utils/netflow-file-navigation'; + import { Checkbox } from '#lib/components/ui/checkbox/index.ts'; + import { Skeleton } from '#lib/components/ui/skeleton/index.ts'; + import SegmentedControl from '#lib/components/common/SegmentedControl.svelte'; + import { navigateToNetflowFile } from '#lib/utils/netflow-file-navigation.ts'; import { DEFAULT_MAAD_IP_VERSION, MAAD_IP_VERSION_OPTIONS, @@ -23,9 +23,9 @@ type ProtocolMetricKey, type SpectrumPoint, type TimeBucket - } from '$lib/types/types'; - import type { SpectrumStatsPayload } from '$lib/types/spectrum-stats'; - import type { DimensionMetricKey } from '$lib/types/dimension-stats'; + } from '#lib/types/types.ts'; + import type { SpectrumStatsPayload } from '#lib/types/spectrum-stats.ts'; + import type { DimensionMetricKey } from '#lib/types/dimension-stats.ts'; import { BREAKDOWN_CHART_CONFIGS, DIMENSION_ORDER_OPTIONS, @@ -66,17 +66,17 @@ shouldHighlightIpGranularityGrid, placeTicksOnBucketStarts } from './ip-time-axis'; - import { dateStringToEpochPST, formatDateAsPSTDateString } from '$lib/utils/timezone'; - import { crosshairStore } from '$lib/stores/crosshair'; - import { rangeSelection } from '$lib/stores/rangeSelection.svelte'; - import { theme } from '$lib/stores/theme.svelte'; - import { cancelDrawFrame, requestDrawFrame } from '$lib/utils/animation-frame'; + import { dateStringToEpochPST, formatDateAsPSTDateString } from '#lib/utils/timezone.ts'; + import { crosshairStore } from '#lib/stores/crosshair.ts'; + import { rangeSelection } from '#lib/stores/rangeSelection.svelte.ts'; + import { theme } from '#lib/stores/theme.svelte.ts'; + import { cancelDrawFrame, requestDrawFrame } from '#lib/utils/animation-frame.ts'; import { ensureCachedWindow, getMissingWindowRanges, readCachedWindow, type TimeRange - } from '$lib/utils/window-cache'; + } from '#lib/utils/window-cache.ts'; const IP_TO_GROUP_BY: Record = { '1d': 'date', diff --git a/apps/web/src/lib/components/charts/ChartCard.svelte b/apps/web/src/lib/components/charts/ChartCard.svelte index 7b1a2666..016eceb2 100644 --- a/apps/web/src/lib/components/charts/ChartCard.svelte +++ b/apps/web/src/lib/components/charts/ChartCard.svelte @@ -1,6 +1,6 @@ diff --git a/apps/web/src/lib/components/filters/DirectionFilter.svelte b/apps/web/src/lib/components/filters/DirectionFilter.svelte index ed7d8ef8..7c2832ba 100644 --- a/apps/web/src/lib/components/filters/DirectionFilter.svelte +++ b/apps/web/src/lib/components/filters/DirectionFilter.svelte @@ -1,8 +1,8 @@ diff --git a/apps/web/src/routes/datasets/[dataset]/+page.ts b/apps/web/src/routes/datasets/[dataset]/+page.ts index 7682be7b..bdcc6796 100644 --- a/apps/web/src/routes/datasets/[dataset]/+page.ts +++ b/apps/web/src/routes/datasets/[dataset]/+page.ts @@ -1,6 +1,6 @@ import { error } from '@sveltejs/kit'; import type { PageLoad } from './$types'; -import { loadDatasetSummariesFromFetch } from '$lib/datasets'; +import { loadDatasetSummariesFromFetch } from '#lib/datasets.ts'; export const load: PageLoad = async ({ params, fetch }) => { const { dataset } = params; diff --git a/apps/web/src/routes/datasets/[dataset]/alerts/+page.svelte b/apps/web/src/routes/datasets/[dataset]/alerts/+page.svelte index 752bf6e2..c3b75f06 100644 --- a/apps/web/src/routes/datasets/[dataset]/alerts/+page.svelte +++ b/apps/web/src/routes/datasets/[dataset]/alerts/+page.svelte @@ -2,10 +2,10 @@ import { afterNavigate } from '$app/navigation'; import { untrack } from 'svelte'; import type { PageProps } from './$types'; - import SegmentedControl from '$lib/components/common/SegmentedControl.svelte'; - import DatasetTabs from '$lib/components/datasets/DatasetTabs.svelte'; - import { Button } from '$lib/components/ui/button'; - import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card'; + import SegmentedControl from '#lib/components/common/SegmentedControl.svelte'; + import DatasetTabs from '#lib/components/datasets/DatasetTabs.svelte'; + import { Button } from '#lib/components/ui/button/index.ts'; + import { Card, CardContent, CardHeader, CardTitle } from '#lib/components/ui/card/index.ts'; import { Table, TableBody, @@ -13,8 +13,8 @@ TableHead, TableHeader, TableRow - } from '$lib/components/ui/table'; - import type { AlertHorizon, AlertsFeedResponse, AlertSort, AlertTail } from '$lib/types/types'; + } from '#lib/components/ui/table/index.ts'; + import type { AlertHorizon, AlertsFeedResponse, AlertSort, AlertTail } from '#lib/types/types.ts'; type TailSelection = AlertTail; type ErrorResponse = { data: null; error: string }; diff --git a/apps/web/src/routes/datasets/[dataset]/alerts/+page.ts b/apps/web/src/routes/datasets/[dataset]/alerts/+page.ts index 0389df5f..2300a104 100644 --- a/apps/web/src/routes/datasets/[dataset]/alerts/+page.ts +++ b/apps/web/src/routes/datasets/[dataset]/alerts/+page.ts @@ -1,7 +1,7 @@ import { error } from '@sveltejs/kit'; import type { PageLoad } from './$types'; -import { loadDatasetSummariesFromFetch } from '$lib/datasets'; -import type { AlertsFeedResponse } from '$lib/types/types'; +import { loadDatasetSummariesFromFetch } from '#lib/datasets.ts'; +import type { AlertsFeedResponse } from '#lib/types/types.ts'; type ErrorResponse = { data: null; diff --git a/apps/web/src/routes/netflow/files/+page.svelte b/apps/web/src/routes/netflow/files/+page.svelte index 4313e13b..d517f136 100644 --- a/apps/web/src/routes/netflow/files/+page.svelte +++ b/apps/web/src/routes/netflow/files/+page.svelte @@ -1,8 +1,8 @@ diff --git a/apps/web/svelte.config.js b/apps/web/svelte.config.js deleted file mode 100644 index 7aca2df6..00000000 --- a/apps/web/svelte.config.js +++ /dev/null @@ -1,25 +0,0 @@ -import adapter from '@sveltejs/adapter-cloudflare'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -// The adapter's dev-time platform emulation boots workerd, which local SQLite -// development never needs. Emulate only when explicitly developing against D1. -const cloudflare = adapter(); -if (process.env.ATLANTIS_DB_DRIVER !== 'd1') { - delete cloudflare.emulate; -} - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - // Consult https://svelte.dev/docs/kit/integrations - // for more information about preprocessors - preprocess: vitePreprocess(), - - kit: { - adapter: cloudflare, - env: { - dir: '../..' // Look for .env files in repository root - } - } -}; - -export default config; diff --git a/apps/web/tests/lib/netflow-file-navigation.test.ts b/apps/web/tests/lib/netflow-file-navigation.test.ts index 7c98f8f8..dee91a6f 100644 --- a/apps/web/tests/lib/netflow-file-navigation.test.ts +++ b/apps/web/tests/lib/netflow-file-navigation.test.ts @@ -6,7 +6,7 @@ vi.mock('$app/paths', () => ({ describe('buildNetflowFileHref', () => { it('builds only the dataset search when provided', async () => { - const { buildNetflowFileSearch } = await import('$lib/utils/netflow-file-navigation'); + const { buildNetflowFileSearch } = await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileSearch('uoregon')).toBe('?dataset=uoregon'); expect(buildNetflowFileSearch(' uoregon ')).toBe('?dataset=uoregon'); @@ -15,7 +15,7 @@ describe('buildNetflowFileHref', () => { it('includes a non-all direction when provided', async () => { const { buildNetflowFileHref, buildNetflowFileSearch } = - await import('$lib/utils/netflow-file-navigation'); + await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileSearch('uoregon', 'ingress')).toBe('?dataset=uoregon&direction=ingress'); expect(buildNetflowFileHref('202506192010', 'uoregon', 'ingress')).toBe( @@ -24,13 +24,13 @@ describe('buildNetflowFileHref', () => { }); it('omits an all direction', async () => { - const { buildNetflowFileSearch } = await import('$lib/utils/netflow-file-navigation'); + const { buildNetflowFileSearch } = await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileSearch('uoregon', 'all')).toBe('?dataset=uoregon'); }); it('includes dataset query when provided', async () => { - const { buildNetflowFileHref } = await import('$lib/utils/netflow-file-navigation'); + const { buildNetflowFileHref } = await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileHref('202506192010', 'uoregon')).toBe( '/netflow/files/202506192010?dataset=uoregon' @@ -45,7 +45,7 @@ describe('buildNetflowFileHref', () => { it('omits query when dataset is empty', async () => { const { buildNetflowFileHref, buildNetflowFileSearch } = - await import('$lib/utils/netflow-file-navigation'); + await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileHref('202506192010', '')).toBe('/netflow/files/202506192010'); expect(buildNetflowFileSearch('')).toBe(''); @@ -54,7 +54,7 @@ describe('buildNetflowFileHref', () => { it('adds a non-default MAAD IP family to file links', async () => { const { buildNetflowFileHref, buildNetflowFileSearch } = - await import('$lib/utils/netflow-file-navigation'); + await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileSearch('uoregon', 'all', 4)).toBe('?dataset=uoregon'); expect(buildNetflowFileSearch('uoregon', 'ingress', 6)).toBe( @@ -66,7 +66,7 @@ describe('buildNetflowFileHref', () => { }); it('delegates navigation to the built href', async () => { - const { navigateToNetflowFile } = await import('$lib/utils/netflow-file-navigation'); + const { navigateToNetflowFile } = await import('#lib/utils/netflow-file-navigation.ts'); const navigate = vi.fn().mockResolvedValue(undefined); await navigateToNetflowFile(navigate, '202506192010', 'uoregon'); @@ -92,7 +92,7 @@ describe('buildNetflowFileHref', () => { it('carries a weighted MAAD measure and omits the default', async () => { const { buildNetflowFileHref, buildNetflowFileSearch, navigateToNetflowFile } = - await import('$lib/utils/netflow-file-navigation'); + await import('#lib/utils/netflow-file-navigation.ts'); expect(buildNetflowFileSearch('uoregon', 'all', 4, 'addresses')).toBe('?dataset=uoregon'); expect(buildNetflowFileSearch('uoregon', 'all', 4, 'packets')).toBe( diff --git a/apps/web/tests/lib/server/datasets.test.ts b/apps/web/tests/lib/server/datasets.test.ts index b3b4c692..29cd3192 100644 --- a/apps/web/tests/lib/server/datasets.test.ts +++ b/apps/web/tests/lib/server/datasets.test.ts @@ -25,6 +25,13 @@ async function loadDatasetsModule() { return import('../../../src/lib/server/datasets'); } +async function loadD1DatasetsModule(fake: { database: unknown }) { + vi.resetModules(); + vi.doMock('cloudflare:workers', () => ({ env: { DB: fake.database } })); + vi.doMock('#db', () => import('../../../src/lib/server/db/d1.ts')); + return import('../../../src/lib/server/datasets'); +} + type FakeD1DatasetRow = { id: string; label: string; @@ -33,7 +40,7 @@ type FakeD1DatasetRow = { sortOrder: number; }; -function createD1Platform(initialRows: FakeD1DatasetRow[]) { +function createD1Binding(initialRows: FakeD1DatasetRow[]) { let datasetRows = initialRows; const queries: Array<{ sql: string; params: unknown[] }> = []; const sourceMemberRows = [ @@ -73,7 +80,7 @@ function createD1Platform(initialRows: FakeD1DatasetRow[]) { }; return { - platform: { env: { DB: database } } as unknown as App.Platform, + database, queries, setDatasetRows(rows: FakeD1DatasetRow[]) { datasetRows = rows; @@ -149,6 +156,8 @@ describe('dataset server helpers', () => { process.chdir(originalCwd); vi.unstubAllEnvs(); vi.doUnmock('node:fs/promises'); + vi.doUnmock('cloudflare:workers'); + vi.doUnmock('#db'); }); it('lists dataset summaries from local sqlite metadata', async () => { @@ -196,14 +205,14 @@ describe('dataset server helpers', () => { }); it('loads the D1 catalog once when building summaries', async () => { - const fake = createD1Platform([ + const fake = createD1Binding([ alphaD1Row, { ...alphaD1Row, id: 'beta', label: 'Beta Label', sortOrder: 1 } ]); vi.stubEnv('DEFAULT_DATASET', 'beta'); - const datasets = await loadDatasetsModule(); + const datasets = await loadD1DatasetsModule(fake); - await expect(datasets.listDatasetSummaries(fake.platform)).resolves.toEqual([ + await expect(datasets.listDatasetSummaries()).resolves.toEqual([ { datasetId: 'alpha', label: 'Alpha Label', @@ -227,14 +236,11 @@ describe('dataset server helpers', () => { }); it('resolves an explicit D1 dataset with one targeted query', async () => { - const fake = createD1Platform([alphaD1Row]); - const datasets = await loadDatasetsModule(); + const fake = createD1Binding([alphaD1Row]); + const datasets = await loadD1DatasetsModule(fake); await expect( - datasets.getRequestedDataset( - new URL('http://localhost/api/netflow/stats?dataset=alpha'), - fake.platform - ) + datasets.getRequestedDataset(new URL('http://localhost/api/netflow/stats?dataset=alpha')) ).resolves.toBe('alpha'); expect(fake.queries).toHaveLength(1); expect(fake.queries[0]?.sql).toContain('WHERE id = ?'); @@ -242,21 +248,21 @@ describe('dataset server helpers', () => { }); it('does not cache D1 dataset metadata across calls', async () => { - const fake = createD1Platform([alphaD1Row]); - const datasets = await loadDatasetsModule(); + const fake = createD1Binding([alphaD1Row]); + const datasets = await loadD1DatasetsModule(fake); - await expect(datasets.getDatasetLabel('alpha', fake.platform)).resolves.toBe('Alpha Label'); + await expect(datasets.getDatasetLabel('alpha')).resolves.toBe('Alpha Label'); fake.setDatasetRows([{ ...alphaD1Row, label: 'Updated Alpha' }]); - await expect(datasets.getDatasetLabel('alpha', fake.platform)).resolves.toBe('Updated Alpha'); + await expect(datasets.getDatasetLabel('alpha')).resolves.toBe('Updated Alpha'); expect(fake.queries).toHaveLength(2); expect(fake.queries.every((query) => query.sql.includes('WHERE id = ?'))).toBe(true); }); it('preserves available dataset details for an unknown D1 dataset', async () => { - const fake = createD1Platform([alphaD1Row]); - const datasets = await loadDatasetsModule(); + const fake = createD1Binding([alphaD1Row]); + const datasets = await loadD1DatasetsModule(fake); - await expect(datasets.getDatasetConfig('missing', fake.platform)).rejects.toThrow( + await expect(datasets.getDatasetConfig('missing')).rejects.toThrow( "Unknown dataset 'missing'. Available datasets: alpha" ); expect(fake.queries).toHaveLength(2); @@ -269,7 +275,7 @@ describe('dataset server helpers', () => { vi.stubEnv('LOCAL_SQLITE_PATH', dbPath); const datasets = await loadDatasetsModule(); - await datasets.withDatasetDb('alpha', undefined, async ({ db }) => { + await datasets.withDatasetDb('alpha', async ({ db }) => { await expect(db.get('DELETE FROM datasets RETURNING id')).rejects.toThrow(/readonly/i); }); await expect(datasets.getDatasetConfig('alpha')).resolves.toMatchObject({ id: 'alpha' }); @@ -355,10 +361,10 @@ describe('dataset server helpers', () => { }); it('uses one D1 source-members query for configured definitions', async () => { - const fake = createD1Platform([alphaD1Row]); - const datasets = await loadDatasetsModule(); + const fake = createD1Binding([alphaD1Row]); + const datasets = await loadD1DatasetsModule(fake); - await expect(datasets.listDatasetSourceDefinitions('alpha', fake.platform)).resolves.toEqual([ + await expect(datasets.listDatasetSourceDefinitions('alpha')).resolves.toEqual([ { sourceId: 'router-a', members: ['router-a'] }, { sourceId: 'router-b', members: ['router-b'] } ]); @@ -665,7 +671,7 @@ describe('dataset server helpers', () => { const datasets = await loadDatasetsModule(); let retiredDb: ReadonlyDatasetDb | undefined; - await datasets.withDatasetDb('alpha', undefined, async ({ db: requestDb, listSources }) => { + await datasets.withDatasetDb('alpha', async ({ db: requestDb, listSources }) => { retiredDb = requestDb; fs.renameSync(replacementPath, dbPath); await expect(datasets.listDatasetSources('alpha')).resolves.toEqual(['router-b']); diff --git a/apps/web/tests/lib/server/db/coverage.test.ts b/apps/web/tests/lib/server/db/coverage.test.ts index 4ba3e3d2..3c9a5006 100644 --- a/apps/web/tests/lib/server/db/coverage.test.ts +++ b/apps/web/tests/lib/server/db/coverage.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import { buildCoverageTimelines, loadCoverageRows } from '$lib/server/db/coverage'; -import { dateStringToEpochPST } from '$lib/utils/timezone'; +import { buildCoverageTimelines, loadCoverageRows } from '#lib/server/db/coverage.ts'; +import { dateStringToEpochPST } from '#lib/utils/timezone.ts'; describe('buildCoverageTimelines', () => { it('coalesces repeated coverage reads in one dashboard request burst', async () => { diff --git a/apps/web/tests/performance/seed-large-db.ts b/apps/web/tests/performance/seed-large-db.ts index 4542e8a8..4acc71a9 100644 --- a/apps/web/tests/performance/seed-large-db.ts +++ b/apps/web/tests/performance/seed-large-db.ts @@ -1,8 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; import Database from 'better-sqlite3'; -// Node's type-stripping runner requires the extension; this script is not bundled by Vite. -// @ts-expect-error allowImportingTsExtensions is intentionally disabled for application code. import { localSchemaSql } from '../../src/lib/server/db/local-schema.ts'; const outputPath = path.resolve(process.argv[2] ?? '/tmp/atlantis-web-perf.sqlite'); diff --git a/apps/web/tests/routes/api-aggregate-routes.test.ts b/apps/web/tests/routes/api-aggregate-routes.test.ts index eab7606e..9d30bf5e 100644 --- a/apps/web/tests/routes/api-aggregate-routes.test.ts +++ b/apps/web/tests/routes/api-aggregate-routes.test.ts @@ -4,16 +4,16 @@ import { GET as getIpStats } from '../../src/routes/api/ip/stats/+server'; import { GET as getProtocolStats } from '../../src/routes/api/protocol/stats/+server'; import { GET as getSpectrumStats } from '../../src/routes/api/netflow/spectrum-stats/+server'; import { GET as getStructureStats } from '../../src/routes/api/netflow/structure-stats/+server'; -import { getRequestedDataset, listDatasetSources, withDatasetDb } from '$lib/server/datasets'; +import { getRequestedDataset, listDatasetSources, withDatasetDb } from '#lib/server/datasets.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn(), listDatasetSources: vi.fn(), withDatasetDb: vi.fn() })); function mockDatasetSession(db: object): void { - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: db as never, listSources: async () => [], listSourceDefinitions: async () => [] }) ); } diff --git a/apps/web/tests/routes/api-alerts.test.ts b/apps/web/tests/routes/api-alerts.test.ts index 95868a4e..ea41d2cd 100644 --- a/apps/web/tests/routes/api-alerts.test.ts +++ b/apps/web/tests/routes/api-alerts.test.ts @@ -3,10 +3,10 @@ import os from 'node:os'; import path from 'node:path'; import Database from 'better-sqlite3'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { getRequestedDataset } from '$lib/server/datasets'; +import { getRequestedDataset } from '#lib/server/datasets.ts'; import { GET } from '../../src/routes/api/alerts/+server'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn() })); @@ -133,8 +133,7 @@ function seedAlerts(fixture: Fixture): void { function eventFor(query = '') { return { - url: new URL(`http://localhost/api/alerts${query}`), - platform: undefined + url: new URL(`http://localhost/api/alerts${query}`) } as never; } diff --git a/apps/web/tests/routes/api-datasets.test.ts b/apps/web/tests/routes/api-datasets.test.ts index 9e41bf3c..1defeb36 100644 --- a/apps/web/tests/routes/api-datasets.test.ts +++ b/apps/web/tests/routes/api-datasets.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it, vi } from 'vitest'; import { GET } from '../../src/routes/api/datasets/+server'; -import { listDatasetSummaries } from '$lib/server/datasets'; +import { listDatasetSummaries } from '#lib/server/datasets.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ listDatasetSummaries: vi.fn() })); diff --git a/apps/web/tests/routes/api-flow-characteristics.test.ts b/apps/web/tests/routes/api-flow-characteristics.test.ts index 5285c3fd..b16b4539 100644 --- a/apps/web/tests/routes/api-flow-characteristics.test.ts +++ b/apps/web/tests/routes/api-flow-characteristics.test.ts @@ -4,16 +4,16 @@ import { getRequestedDataset, listDatasetSourceDefinitions, withDatasetDb -} from '$lib/server/datasets'; +} from '#lib/server/datasets.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn(), listDatasetSourceDefinitions: vi.fn(), withDatasetDb: vi.fn() })); function mockDatasetSession(db: object): void { - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: db as never, listSources: async () => [], diff --git a/apps/web/tests/routes/api-maad-dimensions.test.ts b/apps/web/tests/routes/api-maad-dimensions.test.ts index 5cc9ed9e..cad405a7 100644 --- a/apps/web/tests/routes/api-maad-dimensions.test.ts +++ b/apps/web/tests/routes/api-maad-dimensions.test.ts @@ -2,10 +2,10 @@ import Database from 'better-sqlite3'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { GET as getDimensionStats } from '../../src/routes/api/netflow/dimension-stats/+server'; import { GET as getMaadStatus } from '../../src/routes/api/netflow/maad-status/+server'; -import { getRequestedDataset, withDatasetDb } from '$lib/server/datasets'; -import { localSchemaSql } from '$lib/server/db/local-schema'; +import { getRequestedDataset, withDatasetDb } from '#lib/server/datasets.ts'; +import { localSchemaSql } from '#lib/server/db/local-schema.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn(), withDatasetDb: vi.fn() })); @@ -17,7 +17,7 @@ function openDatabase(): Database.Database { database.exec(localSchemaSql); databases.push(database); vi.mocked(getRequestedDataset).mockResolvedValue('alpha'); - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: { get: async (query: string, params: unknown[] = []) => diff --git a/apps/web/tests/routes/api-netflow-coverage.test.ts b/apps/web/tests/routes/api-netflow-coverage.test.ts index 1f91560a..ff837920 100644 --- a/apps/web/tests/routes/api-netflow-coverage.test.ts +++ b/apps/web/tests/routes/api-netflow-coverage.test.ts @@ -1,15 +1,15 @@ import { describe, expect, it, vi } from 'vitest'; import { GET } from '../../src/routes/api/netflow/coverage/+server'; -import { getRequestedDataset, withDatasetDb } from '$lib/server/datasets'; -import { dateStringToEpochPST } from '$lib/utils/timezone'; +import { getRequestedDataset, withDatasetDb } from '#lib/server/datasets.ts'; +import { dateStringToEpochPST } from '#lib/utils/timezone.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn(), withDatasetDb: vi.fn() })); function mockDatasetSession(db: object): void { - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: db as never, listSources: async () => [], listSourceDefinitions: async () => [] }) ); } diff --git a/apps/web/tests/routes/api-netflow-stats.test.ts b/apps/web/tests/routes/api-netflow-stats.test.ts index 25008007..b611370e 100644 --- a/apps/web/tests/routes/api-netflow-stats.test.ts +++ b/apps/web/tests/routes/api-netflow-stats.test.ts @@ -4,16 +4,16 @@ import { getRequestedDataset, listDatasetSourceDefinitions, withDatasetDb -} from '$lib/server/datasets'; +} from '#lib/server/datasets.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getRequestedDataset: vi.fn(), listDatasetSourceDefinitions: vi.fn(), withDatasetDb: vi.fn() })); function mockDatasetSession(db: object): void { - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: db as never, listSources: async () => [], diff --git a/apps/web/tests/routes/netflow-file-routes.test.ts b/apps/web/tests/routes/netflow-file-routes.test.ts index 9deabf1d..1388c7ce 100644 --- a/apps/web/tests/routes/netflow-file-routes.test.ts +++ b/apps/web/tests/routes/netflow-file-routes.test.ts @@ -9,16 +9,16 @@ import { GET as getIpCounts } from '../../src/routes/api/netflow/files/[slug]/ip import { GET as getSpectrum } from '../../src/routes/api/netflow/files/[slug]/spectrum/+server'; import { GET as getStructure } from '../../src/routes/api/netflow/files/[slug]/structure/+server'; import { GET as getDetails } from '../../src/routes/api/netflow/files/[slug]/details/+server'; -import { getDefaultDatasetId, getRequestedDataset, withDatasetDb } from '$lib/server/datasets'; +import { getDefaultDatasetId, getRequestedDataset, withDatasetDb } from '#lib/server/datasets.ts'; -vi.mock('$lib/server/datasets', () => ({ +vi.mock('#lib/server/datasets.ts', () => ({ getDefaultDatasetId: vi.fn(), getRequestedDataset: vi.fn(), withDatasetDb: vi.fn() })); function mockDatasetSession(db: object): void { - vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, _platform, run) => + vi.mocked(withDatasetDb).mockImplementation(async (_datasetId, run) => run({ db: db as never, listSources: async () => [], listSourceDefinitions: async () => [] }) ); } @@ -46,7 +46,7 @@ describe('netflow file helpers and routes', () => { get: vi.fn().mockResolvedValue({ input_locator: '/captures/r1/nfcapd.202503010005' }) }); - await expect(getNetflowFilePath(undefined, '202503010005', 'r1')).resolves.toBe( + await expect(getNetflowFilePath('202503010005', 'r1')).resolves.toBe( '/captures/r1/nfcapd.202503010005' ); }); @@ -311,7 +311,7 @@ describe('netflow file helpers and routes', () => { await expect(response.json()).resolves.toEqual({ error: 'Invalid direction. Expected one of: all, ingress, egress, lateral, transit' }); - expect(withDatasetDb).not.toHaveBeenCalledWith('alpha', undefined, expect.any(Function)); + expect(withDatasetDb).not.toHaveBeenCalledWith('alpha', expect.any(Function)); }); it('fails a per-file structure function stored without a matching q grid', async () => { diff --git a/apps/web/tests/setup/private-env.ts b/apps/web/tests/setup/private-env.ts new file mode 100644 index 00000000..da95c03d --- /dev/null +++ b/apps/web/tests/setup/private-env.ts @@ -0,0 +1,13 @@ +import { vi } from 'vitest'; + +vi.mock('$app/env/private', async () => { + const { variables } = await import('../../src/env.ts'); + const env = {}; + for (const name of Object.keys(variables)) { + Object.defineProperty(env, name, { + enumerable: true, + get: () => process.env[name]?.trim() || undefined + }); + } + return env; +}); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 677fd32b..66385504 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,20 +1,18 @@ { - "extends": "./.svelte-kit/tsconfig.json", + "extends": "$app/tsconfig", "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "bundler", "allowArbitraryExtensions": true - } - // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias - // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in + }, + "include": [ + "src/**/*.js", + "src/**/*.ts", + "src/**/*.svelte", + "tests/**/*.js", + "tests/**/*.ts", + "tests/**/*.svelte", + "vite.config.ts" + ], + "exclude": ["node_modules"] } diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index db38686a..5feccdc8 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,20 +1,70 @@ import { fileURLToPath } from 'node:url'; +import cloudflare from '@sveltejs/adapter-cloudflare'; import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import { defaultServerConditions, type Plugin } from 'vite'; import { defineConfig } from 'vitest/config'; const repoRoot = fileURLToPath(new URL('../..', import.meta.url)); -export default defineConfig({ - plugins: [tailwindcss(), sveltekit()], - server: { - fs: { - allow: [repoRoot] - } - }, - test: { - environment: 'node', - include: ['tests/**/*.test.ts'], - exclude: ['tests/e2e/**'] +const D1_CONDITION = 'atlantis-d1'; + +type DatabaseDriver = 'd1' | 'sqlite'; + +function resolveDatabaseDriver(command: 'build' | 'serve', mode: string): DatabaseDriver { + if (mode === 'test') { + return 'sqlite'; } + const configured = + process.env.ATLANTIS_DB_DRIVER?.trim() || (command === 'build' ? 'd1' : 'sqlite'); + if (configured !== 'd1' && configured !== 'sqlite') { + throw new Error(`ATLANTIS_DB_DRIVER must be 'd1' or 'sqlite', received '${configured}'`); + } + return configured; +} + +function databaseDriver(driver: DatabaseDriver): Plugin { + return { + name: 'atlantis-database-driver', + configEnvironment(name, options) { + if (driver !== 'd1' || name === 'client' || options.consumer === 'client') { + return; + } + options.resolve ??= {}; + options.resolve.conditions = [ + D1_CONDITION, + ...(options.resolve.conditions ?? defaultServerConditions) + ]; + } + }; +} + +export default defineConfig(({ command, mode }) => { + const driver = resolveDatabaseDriver(command, mode); + + return { + plugins: [ + tailwindcss(), + databaseDriver(driver), + sveltekit({ + preprocess: vitePreprocess(), + adapter: driver === 'd1' ? cloudflare() : undefined, + env: { + dir: '../..' + } + }) + ], + server: { + fs: { + allow: [repoRoot] + } + }, + test: { + environment: 'node', + include: ['tests/**/*.test.ts'], + exclude: ['tests/e2e/**'], + setupFiles: ['tests/setup/private-env.ts'] + } + }; }); diff --git a/bun.lock b/bun.lock index 1913c7d1..8823e938 100644 --- a/bun.lock +++ b/bun.lock @@ -40,14 +40,15 @@ "zod": "^4.3.6", }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260511.1", + "@cloudflare/workers-types": "^5.20260924.1", "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", "@internationalized/date": "^3.12.0", "@playwright/test": "1.52.0", - "@sveltejs/adapter-cloudflare": "^7.2.9", - "@sveltejs/kit": "^2.70.3", - "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@sveltejs/adapter-cloudflare": "8.0.0-next.7", + "@sveltejs/kit": "3.0.0-next.27", + "@sveltejs/load-config": "^0.2.3", + "@sveltejs/vite-plugin-svelte": "^7.3.1", "@tailwindcss/vite": "^4.0.0", "@types/better-sqlite3": "^7.6.13", "@types/node": "^22.15.17", @@ -67,14 +68,17 @@ "tailwind-variants": "^3.3.1", "tailwindcss": "^4.0.0", "tw-animate-css": "^1.4.0", - "typescript": "^5.0.0", - "typescript-eslint": "^8.20.0", - "vite": "^7.3.6", - "vitest": "^3.2.7", - "wrangler": "^4.90.0", + "typescript": "^6.0.3", + "typescript-eslint": "^8.70.1", + "vite": "^8.3.0", + "vitest": "^5.0.1", + "wrangler": "^4.137.0", }, }, }, + "patchedDependencies": { + "runed@0.36.0": "patches/runed@0.36.0.patch", + }, "packages": { "@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], @@ -108,19 +112,19 @@ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260507.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-S85aMwcaPJUjKWDiG6iMMnioKWtPLACa6m0j/EhHR1GYfVpnxb974cBc6d25L+sf7jHWHJI2u5hGp0UTJ7MtXQ=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260921.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-3iB2WnYOlZ29T+1zhCwbHFExCBp6E9bgmDUMryATYwrIGEQ1YbvR78m4ydm56XKN/d/yF3803ivMGfZMYDtiMg=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260507.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GMEBu8Zp9Q97HLnf7bWJN4KjWpN5MxpeqdvHjBGWNl8UYprJI0k+Jkp89+Wh5S8vIon+HoVbDfOzPa7VwgL6Eg=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260921.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FpqVR7IQXVBmGtajyonEmhmb5UAsmV7dTaIkpemmHZXHEw7uYpkhkzKPjc4BOPhNQy8iwt2p+RZBPMY3Y7/bvQ=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260507.1", "", { "os": "linux", "cpu": "x64" }, "sha512-QlrKEBdgA3uVc0Ok0Q3+0/CW0CTjgj5ySir1i1YY5FXVv0X6GpwtnB5umjunjF2MFprss+L+iFGZzxcSvMC1nA=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260921.1", "", { "os": "linux", "cpu": "x64" }, "sha512-riAJIohaVp5A8Sqy4yKlzHOaLPOICMf5oey+jC2rm45RVT+wK8+7UU0d31Dy/02Nc8YUkobAFwNVjX06P8WQ5g=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260507.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-eGbbupEtK2nh9V9Dhcx3vv3GTKeXqSVNgAEYVCCN0NGS9tl9HbMoHRX/4JL181FKXROMigWBCQVL//qPhsAzBQ=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260921.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-tnJu08tT7s0XWDqp3O0H/vCp0voy9OqVAzspb89biMo1dh8IiEpnyXnoPmdJ7H4qBnXCmXgy0kuEphuvpDPj9w=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260507.1", "", { "os": "win32", "cpu": "x64" }, "sha512-dmClJ/E0BAcuDetQIZFqbeAXejWrG5pysGRMQ6T83Y0IW/7IAamY2zFEkAJ10I5xwZsdHuYsZtzlOxpEXpJs7A=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260921.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VgNcRPstoZMb1G94JTrx+jU24GtkkazNfox0gnF/2fkuXpcfW/M0e0xvdMovYfwt8ZxG5AB2ZNvanD6ufBwiuQ=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260511.1", "", {}, "sha512-FA+si7cOq9i/gtCHhIc0XJL0l1F/ApF+m00752Aj7WZFJrj3ZulT2T8/+rT3BabMT0QEnqFEGIqCgrmqhgEfMg=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@5.20260924.1", "", {}, "sha512-puvr63PrfAFIkdh3FInGEgS0VTQ49WYNYDC2Eupd+qvwWwGpSRjxJD5Iz22oNHCmV1UEcsDz+OtGF++01vUloA=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], @@ -226,6 +230,8 @@ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.4", "", { "dependencies": { "@img/sharp-wasm32": "0.35.4" }, "os": "freebsd" }, "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], @@ -264,6 +270,8 @@ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.4", "", { "dependencies": { "@img/sharp-wasm32": "0.35.4" }, "cpu": "none" }, "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], @@ -298,6 +306,8 @@ "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], + "@oxc-project/types": ["@oxc-project/types@0.151.0", "", {}, "sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA=="], + "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], "@playwright/test": ["@playwright/test@1.52.0", "", { "dependencies": { "playwright": "1.52.0" }, "bin": { "playwright": "cli.js" } }, "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g=="], @@ -310,6 +320,38 @@ "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.10", "", { "os": "android", "cpu": "arm" }, "sha512-bp9svZb+QurZeh+8H4BhrZkifEB0YBNvTVzNSJnJQkj4NrRwmQoDUCGP0vSN7PbvLeM7l1tK6GXL8mrTiH2myg=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.10", "", { "os": "android", "cpu": "arm64" }, "sha512-wm6Dld3RXUAZ/gRWKyUy+4W1B5CB5UeFaOzsSWJWEdxZXHH8rCYiZ5dGe6oJmhsunAPWzL7FZV+VtvmN5Ye2eA=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UbEfXq/AqGNgRTV3ik+X/iR6mUxu2QdYAadwRxJWquUGnW6gDqdP1FtLtFXRow7RJx0ssRwi80XAPr4r+4DtsA=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-7f5h17q5KZVx/ji1vb8OTq31ch1O2I7K8NPIr44GkyWTApXMIsmhWqZfgpOH10xeauqghDAvGlZktasCkcF6Eg=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ynOk/eEYhC6ZB2xCGvKrEOwE58oBy9LnrAqtkrDF9Fz1VTaNdGZTsV0VarJdhPwb+sOJTGjCLwcuyRJZ1dnMcQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.10", "", { "os": "linux", "cpu": "arm" }, "sha512-ERrAs185meZZhGan7a4l3RiiJK1ArSDlHdST++uvSxe+FDbR4TwUPahT/cbZJvaG6fIpDpF78surN+tX708Y4Q=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-KN7OHKD0J3jy1UzBwZWPxpwhODf9IARUIJcrH+yLYKOcmegZ8luEUM38lDP1bDVj40yP6PsSzCqOJF76vljFnQ=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-8l9wP8O+wa8zD6iw6egSfzVtu7oZVfH3hlUsMM4MwbLMhxleqeoXbZzjddyK3YyNlwLhqznq3tF7PkNJ8T/V2w=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-SeXNKeQzA5kLhz/J0CH6ZP0/HJ3v1xm/0YbiYpE0kK7emfRC2OIGGIaE14xzkISEGv2aYuUSpiLiU5Gbq+OI0A=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-mtht0nR+y8/hart4175Ll15w7lY8dg7CtQ+j2FDNTsDRspOWTK/2V3l0aj9sIj7XmvqxT8Yli/wq22e7feTTWg=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.10", "", { "os": "linux", "cpu": "x64" }, "sha512-FSM94nGd55NYo48usCyM/nHfUKRnqc9+b0vJNuKV0oCCpIp/OGims7rO1Nv/DkFkt0S/s2rxsJ2kkS8J3HcpeA=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.10", "", { "os": "linux", "cpu": "x64" }, "sha512-C3YxNB16myRLs7o+B+6PnQ6jBsdIS4+AE4Ah8glVGhDpEv9AOvxhZ/1duAb4B0UGczEK/lBbccksd8VI+p6zfw=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.10", "", { "os": "none", "cpu": "arm64" }, "sha512-571TlE/F1eeTjjdjYAMMMPs1Mfv3MtX6s3+ZKVU6HiUjZ5Njc6c/qzNy/8K3zALTZnaw3JQVYrHxvNfjm43KAg=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-QXW+ZWaiqs2c7Fi++D/SsW07LTPcUrncxcskJGfGNBoaLik1IU6fJymz4HsqwEO0u5Iq11yTO0B/mc4cPk7jrQ=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.10", "", { "os": "win32", "cpu": "x64" }, "sha512-5FQFGgah17YeMtG1Yd5a+rMxQpTksyNXxRtKz06FVTaQw3RKYUJQbUoKk0/5jrXBpDo+7makNP7UHA2LQyH64A=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.0", "", { "os": "android", "cpu": "arm" }, "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A=="], @@ -384,17 +426,15 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.13", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ=="], - "@sveltejs/adapter-cloudflare": ["@sveltejs/adapter-cloudflare@7.2.9", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250507.0", "worktop": "0.8.0-next.18" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "wrangler": "^4.0.0" } }, "sha512-LEfLRYKZIiNYBPYk9Cu4dFCCIX37NO96jESNRDv7yr3vvblKU4Yh05FPUMRSrOWFKtG6bJcGIvdG/M2DSf4D1w=="], + "@sveltejs/adapter-cloudflare": ["@sveltejs/adapter-cloudflare@8.0.0-next.7", "", { "dependencies": { "@cloudflare/workers-types": "^5.20260813.1", "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@sveltejs/kit": "^3.0.0-next.0", "wrangler": "^4.118.0" } }, "sha512-LZxggjQNLGmmnLZ7uQna6G03KVM1t2WeWer6CSxIwPw8zEMhL/DYjbbX7u3m8dEa4eJM3e/vrkv8F+45H9qhMw=="], - "@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], + "@sveltejs/kit": ["@sveltejs/kit@3.0.0-next.27", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1", "@standard-schema/spec": "^1.1.0", "cookie": "^2.0.1", "devalue": "^5.9.0", "esm-env": "^1.2.2", "magic-string": "^1.1.0", "mrmime": "^2.0.1", "sirv": "^3.0.2" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", "svelte": "^5.56.4", "typescript": "^6.0.0", "vite": "^8.0.12" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-m2R6GvcMhiAbYVKhzRf5mzLSvcXEuy/H0Va72XvxfEl1ZglrSC/321GKYa6idDHPojuRzkyh9EB0jBZmAwf6+Q=="], "@sveltejs/load-config": ["@sveltejs/load-config@0.2.3", "", {}, "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ=="], - "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="], - - "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="], + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.3.1", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^1.0.0", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-ZPsLN8B1e/En+Ak5s4V7srFDT532oS0qieLsQwu63NGKsS+iAjoO2Js1BochlHlglcU+Pt7WAO3C5Ee+4f6gVA=="], "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], @@ -460,45 +500,35 @@ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/type-utils": "8.57.2", "@typescript-eslint/utils": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.70.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.70.1", "@typescript-eslint/type-utils": "8.70.1", "@typescript-eslint/utils": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.70.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA=="], + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.70.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.70.1", "@typescript-eslint/types": "8.70.1", "@typescript-eslint/typescript-estree": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.2", "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.70.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.70.1", "@typescript-eslint/types": "^8.70.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2" } }, "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.70.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "@typescript-eslint/typescript-estree": "8.70.1", "@typescript-eslint/utils": "8.70.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.57.2", "", {}, "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA=="], - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.2", "@typescript-eslint/tsconfig-utils": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA=="], + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.70.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.70.1", "@typescript-eslint/tsconfig-utils": "8.70.1", "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g=="], - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg=="], + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.70.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.70.1", "@typescript-eslint/types": "8.70.1", "@typescript-eslint/typescript-estree": "8.70.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw=="], + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw=="], "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - "@vitest/expect": ["@vitest/expect@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w=="], - - "@vitest/mocker": ["@vitest/mocker@3.2.7", "", { "dependencies": { "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.7", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA=="], - - "@vitest/runner": ["@vitest/runner@3.2.7", "", { "dependencies": { "@vitest/utils": "3.2.7", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA=="], + "@vitest/mocker": ["@vitest/mocker@5.0.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@vitest/spy": "5.0.1", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q=="], - "@vitest/snapshot": ["@vitest/snapshot@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g=="], - - "@vitest/spy": ["@vitest/spy@3.2.7", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ=="], - - "@vitest/utils": ["@vitest/utils@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw=="], + "@vitest/spy": ["@vitest/spy@5.0.1", "", {}, "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q=="], "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], - "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -566,15 +596,13 @@ "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], - "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "cacache": ["cacache@15.3.0", "", { "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "glob": "^7.1.4", "infer-owner": "^1.0.4", "lru-cache": "^6.0.0", "minipass": "^3.1.1", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.2", "mkdirp": "^1.0.3", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^8.0.1", "tar": "^6.0.2", "unique-filename": "^1.1.1" } }, "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -588,8 +616,6 @@ "chartjs-plugin-annotation": ["chartjs-plugin-annotation@3.1.0", "", { "peerDependencies": { "chart.js": ">=4.0.0" } }, "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ=="], - "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], - "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], @@ -644,8 +670,6 @@ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], @@ -742,7 +766,7 @@ "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], @@ -898,8 +922,6 @@ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -916,29 +938,29 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], @@ -950,8 +972,6 @@ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], - "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -1054,7 +1074,7 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "miniflare": ["miniflare@4.20260507.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", "workerd": "1.20260507.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-PSXBiLExTdZ4UGO/raKCHQauUpYL7F880ZRB7j0+78Rv8h7TsdN2E/iEDK9sK2Y+SPQ5wJSeAa+rDeVKoZZoEw=="], + "miniflare": ["miniflare@5.20260921.0-alpha", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.4", "undici": "7.29.0", "workerd": "1.20260921.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-vHH/unOYvV2jA1Q9SdkmzrQhhMoksdwg5jegu6ZeKaaRzgxZhVbt1NdTpQjHF2VTgiBjgP8SiUlUMfruB3N3SQ=="], "minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], @@ -1156,8 +1176,6 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -1218,8 +1236,6 @@ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], - "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], - "rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], @@ -1258,6 +1274,8 @@ "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "rolldown": ["rolldown@1.2.10", "", { "dependencies": { "@oxc-project/types": "=0.151.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.10", "@rolldown/binding-android-arm64": "1.2.10", "@rolldown/binding-darwin-arm64": "1.2.10", "@rolldown/binding-darwin-x64": "1.2.10", "@rolldown/binding-freebsd-x64": "1.2.10", "@rolldown/binding-linux-arm-gnueabihf": "1.2.10", "@rolldown/binding-linux-arm64-gnu": "1.2.10", "@rolldown/binding-linux-arm64-musl": "1.2.10", "@rolldown/binding-linux-ppc64-gnu": "1.2.10", "@rolldown/binding-linux-s390x-gnu": "1.2.10", "@rolldown/binding-linux-x64-gnu": "1.2.10", "@rolldown/binding-linux-x64-musl": "1.2.10", "@rolldown/binding-openharmony-arm64": "1.2.10", "@rolldown/binding-win32-arm64-msvc": "1.2.10", "@rolldown/binding-win32-x64-msvc": "1.2.10" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-OxkA08pSryMK7B3XiFA09B4OJ1xJMPgIYCBMY2xchzpqgBGsV1o0DetPAE+Sl3N3L4oCPiEzmHVSOj7iR04Zog=="], + "rollup": ["rollup@4.60.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.0", "@rollup/rollup-android-arm64": "4.60.0", "@rollup/rollup-darwin-arm64": "4.60.0", "@rollup/rollup-darwin-x64": "4.60.0", "@rollup/rollup-freebsd-arm64": "4.60.0", "@rollup/rollup-freebsd-x64": "4.60.0", "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", "@rollup/rollup-linux-arm-musleabihf": "4.60.0", "@rollup/rollup-linux-arm64-gnu": "4.60.0", "@rollup/rollup-linux-arm64-musl": "4.60.0", "@rollup/rollup-linux-loong64-gnu": "4.60.0", "@rollup/rollup-linux-loong64-musl": "4.60.0", "@rollup/rollup-linux-ppc64-gnu": "4.60.0", "@rollup/rollup-linux-ppc64-musl": "4.60.0", "@rollup/rollup-linux-riscv64-gnu": "4.60.0", "@rollup/rollup-linux-riscv64-musl": "4.60.0", "@rollup/rollup-linux-s390x-gnu": "4.60.0", "@rollup/rollup-linux-x64-gnu": "4.60.0", "@rollup/rollup-linux-x64-musl": "4.60.0", "@rollup/rollup-openbsd-x64": "4.60.0", "@rollup/rollup-openharmony-arm64": "4.60.0", "@rollup/rollup-win32-arm64-msvc": "4.60.0", "@rollup/rollup-win32-ia32-msvc": "4.60.0", "@rollup/rollup-win32-x64-gnu": "4.60.0", "@rollup/rollup-win32-x64-msvc": "4.60.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1332,7 +1350,7 @@ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "std-env": ["std-env@4.2.0", "", {}, "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw=="], "stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="], @@ -1346,8 +1364,6 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], "suf-log": ["suf-log@2.5.3", "", { "dependencies": { "s.color": "0.0.15" } }, "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow=="], @@ -1384,7 +1400,7 @@ "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + "tinybench": ["tinybench@6.1.4", "", {}, "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ=="], "tinyclip": ["tinyclip@0.1.12", "", {}, "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA=="], @@ -1392,12 +1408,6 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], @@ -1422,9 +1432,9 @@ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "typescript-eslint": ["typescript-eslint@8.57.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A=="], + "typescript-eslint": ["typescript-eslint@8.70.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.70.1", "@typescript-eslint/parser": "8.70.1", "@typescript-eslint/typescript-estree": "8.70.1", "@typescript-eslint/utils": "8.70.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA=="], "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], @@ -1432,7 +1442,7 @@ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], - "undici": ["undici@7.24.8", "", {}, "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ=="], + "undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], @@ -1476,13 +1486,11 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], - - "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "vite": ["vite@8.3.0", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.28", "rolldown": "~1.2.6", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ=="], "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], - "vitest": ["vitest@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.7", "@vitest/mocker": "3.2.7", "@vitest/pretty-format": "^3.2.7", "@vitest/runner": "3.2.7", "@vitest/snapshot": "3.2.7", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.7", "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg=="], + "vitest": ["vitest@5.0.1", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/mocker": "5.0.1", "chai": "^6.2.2", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", "magic-string": "^1.2.3", "obug": "^2.1.4", "picomatch": "^4.0.7", "std-env": "^4.2.0", "tinybench": "6.1.4", "tinyexec": "1.3.0", "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "5.0.1", "@vitest/browser-preview": "5.0.1", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", "@vitest/coverage-istanbul": "5.0.1", "@vitest/coverage-v8": "5.0.1", "@vitest/ui": "5.0.1", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], @@ -1496,17 +1504,15 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "workerd": ["workerd@1.20260507.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260507.1", "@cloudflare/workerd-darwin-arm64": "1.20260507.1", "@cloudflare/workerd-linux-64": "1.20260507.1", "@cloudflare/workerd-linux-arm64": "1.20260507.1", "@cloudflare/workerd-windows-64": "1.20260507.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-z7JhsFSe6+X1b5fUHaVpo15VM1IRMJiLofEkq8iKdCo+Veqc+FUg5lIsuz8NwePxuSKrXtO4ZQpGkQLbPVXFhg=="], - - "worktop": ["worktop@0.8.0-next.18", "", { "dependencies": { "mrmime": "^2.0.0", "regexparam": "^3.0.0" } }, "sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw=="], + "workerd": ["workerd@1.20260921.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260921.1", "@cloudflare/workerd-darwin-arm64": "1.20260921.1", "@cloudflare/workerd-linux-64": "1.20260921.1", "@cloudflare/workerd-linux-arm64": "1.20260921.1", "@cloudflare/workerd-windows-64": "1.20260921.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-4HyG7G1W4ksa6tUZ8bV2jxDRWuL5PXnHm9+Z1sjFPb9OZNoYtXz4y7QQRh4ibi0BF/lOmlAVjbhkUqsAVZuUKA=="], - "wrangler": ["wrangler@4.90.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260507.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260507.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260507.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-bmNIykl59TfCUn5xQgU7IWylSsPx3LQaPLMSAq2VQHt89CBrcj9qXQ0eYfjBCWA5XTBVgten391evt7xxtXwcA=="], + "wrangler": ["wrangler@4.137.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.2", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "5.20260921.0-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260921.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260921.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-vq2JmxkvwOjnsMUejQwd89/EK6u1d20OMlGUVVmb55gVi2zSBKp2rvmxUiPqrSEntK8NwzpTF3DQ/S2I9/TLsg=="], "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], @@ -1550,12 +1556,24 @@ "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "@img/sharp-freebsd-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.4", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA=="], + + "@img/sharp-webcontainers-wasm32/@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.4", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA=="], + "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - "@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + "@sveltejs/kit/cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="], "@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], + "@sveltejs/kit/magic-string": ["magic-string@1.4.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g=="], + + "@sveltejs/vite-plugin-svelte/magic-string": ["magic-string@1.4.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g=="], + + "@sveltejs/vite-plugin-svelte/obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="], + + "@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], @@ -1570,10 +1588,36 @@ "@types/sax/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], + "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1" } }, "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1" } }, "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw=="], + + "@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw=="], + + "@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "@typescript-eslint/typescript-estree/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1" } }, "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw=="], + + "@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "@vitest/mocker/magic-string": ["magic-string@1.4.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "astro/@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="], @@ -1604,6 +1648,8 @@ "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "esrap/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1620,6 +1666,8 @@ "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "miniflare/sharp": ["sharp@0.35.4", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.4", "@img/sharp-darwin-x64": "0.35.4", "@img/sharp-freebsd-wasm32": "0.35.4", "@img/sharp-libvips-darwin-arm64": "1.3.3", "@img/sharp-libvips-darwin-x64": "1.3.3", "@img/sharp-libvips-linux-arm": "1.3.3", "@img/sharp-libvips-linux-arm64": "1.3.3", "@img/sharp-libvips-linux-ppc64": "1.3.3", "@img/sharp-libvips-linux-riscv64": "1.3.3", "@img/sharp-libvips-linux-s390x": "1.3.3", "@img/sharp-libvips-linux-x64": "1.3.3", "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", "@img/sharp-libvips-linuxmusl-x64": "1.3.3", "@img/sharp-linux-arm": "0.35.4", "@img/sharp-linux-arm64": "0.35.4", "@img/sharp-linux-ppc64": "0.35.4", "@img/sharp-linux-riscv64": "0.35.4", "@img/sharp-linux-s390x": "0.35.4", "@img/sharp-linux-x64": "0.35.4", "@img/sharp-linuxmusl-arm64": "0.35.4", "@img/sharp-linuxmusl-x64": "0.35.4", "@img/sharp-webcontainers-wasm32": "0.35.4", "@img/sharp-win32-arm64": "0.35.4", "@img/sharp-win32-ia32": "0.35.4", "@img/sharp-win32-x64": "0.35.4" } }, "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA=="], + "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -1640,6 +1688,8 @@ "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "runed/@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], + "shadcn-svelte/postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], "sitemap/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], @@ -1648,12 +1698,8 @@ "svelte/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], - "svelte/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.13", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ=="], - "svelte/@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], - "svelte/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], - "svelte/aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], "svelte/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], @@ -1670,19 +1716,31 @@ "tar/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + "tsconfck/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "tsx/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], "unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], - "vite/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "vite/picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "vite/postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="], - "vite/postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], + "vite/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], - "vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "vitest/es-module-lexer": ["es-module-lexer@2.3.2", "", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="], - "vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "vitest/magic-string": ["magic-string@1.4.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g=="], - "wrangler/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], + "vitest/obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="], + + "vitest/picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "vitest/tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], + + "vitest/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "wrangler/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -1760,12 +1818,52 @@ "@eslint/compat/eslint/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + "@eslint/eslintrc/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "@eslint/eslintrc/espree/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], "@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "@img/sharp-freebsd-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@img/sharp-webcontainers-wasm32/@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@sveltejs/kit/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@sveltejs/vite-plugin-svelte/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "@types/sax/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="], + + "@typescript-eslint/typescript-estree/tinyglobby/picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "@vitest/mocker/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "astro-eslint-parser/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], "astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], @@ -1818,16 +1916,78 @@ "astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "bits-ui/runed/@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA=="], + + "miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.3" }, "os": "darwin", "cpu": "x64" }, "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw=="], + + "miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg=="], + + "miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw=="], + + "miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA=="], + + "miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A=="], + + "miniflare/sharp/@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w=="], + + "miniflare/sharp/@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.3", "", { "os": "linux", "cpu": "none" }, "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ=="], + + "miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q=="], + + "miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA=="], + + "miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw=="], + + "miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw=="], + + "miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.3" }, "os": "linux", "cpu": "arm" }, "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A=="], + + "miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.3" }, "os": "linux", "cpu": "arm64" }, "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ=="], + + "miniflare/sharp/@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ=="], + + "miniflare/sharp/@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.3" }, "os": "linux", "cpu": "none" }, "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA=="], + + "miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.3" }, "os": "linux", "cpu": "s390x" }, "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ=="], + + "miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.3" }, "os": "linux", "cpu": "x64" }, "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA=="], + + "miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" }, "os": "linux", "cpu": "arm64" }, "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA=="], + + "miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.3" }, "os": "linux", "cpu": "x64" }, "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg=="], + + "miniflare/sharp/@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw=="], + + "miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw=="], + + "miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.4", "", { "os": "win32", "cpu": "x64" }, "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg=="], + + "miniflare/sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "runed/@sveltejs/kit/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], + + "runed/@sveltejs/kit/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "runed/@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "runed/@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], + "shadcn-svelte/postcss/nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], "sitemap/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "svelte-eslint-parser/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "svelte-toolbelt/runed/@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], @@ -1882,123 +2042,93 @@ "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], - - "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], - - "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], - - "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], - - "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], - - "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], - - "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], - - "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], - - "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], - - "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], - - "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], - - "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], - - "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], - - "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], - - "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + "vite/postcss/nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], - "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + "vitest/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], - "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - "vite/postcss/nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], + "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], + "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], + "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], + "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], + "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], + "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], + "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], + "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], + "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], + "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], + "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], + "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], + "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], + "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], + "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], + "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], - "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - - "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], + "@atlantis/web/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], + "@atlantis/web/eslint/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], + "@atlantis/web/eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], + "@eslint/compat/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], - "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], + "@eslint/compat/eslint/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], + "@eslint/compat/eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], + "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], + "bits-ui/runed/@sveltejs/kit/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], - "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], + "bits-ui/runed/@sveltejs/kit/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], + "bits-ui/runed/@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], - "@atlantis/web/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + "bits-ui/runed/@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], - "@atlantis/web/eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "@eslint/compat/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + "svelte-toolbelt/runed/@sveltejs/kit/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], - "@eslint/compat/eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "svelte-toolbelt/runed/@sveltejs/kit/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "svelte-toolbelt/runed/@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], - "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "svelte-toolbelt/runed/@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], "@atlantis/web/eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], diff --git a/docs/code/architecture.md b/docs/code/architecture.md index 55e9bdfb..9ca576ad 100644 --- a/docs/code/architecture.md +++ b/docs/code/architecture.md @@ -34,7 +34,9 @@ The repository does not contain the general SQLite-to-D1 data-load process. The local dashboard reads SQLite databases with `better-sqlite3`. It opens these databases in read-only mode. -The deployed dashboard reads the `DB` Cloudflare D1 binding. `ATLANTIS_DB_DRIVER=sqlite` forces the local SQLite path. +The deployed dashboard reads the `DB` Cloudflare D1 binding from `cloudflare:workers`. + +The build selects one database driver. `apps/web/src/lib/server/db/d1.ts` reads D1, and `apps/web/src/lib/server/db/sqlite.ts` reads the pipeline SQLite files. Server code imports the driver as `#db`. [Development](development.md#choose-the-database-driver) explains the selection. The landing site has no database. It builds static files in `apps/landing/dist`. @@ -50,7 +52,7 @@ Both implementations must keep compatible table and column contracts. No automat ## Main technology -- SvelteKit 2 and Svelte 5 +- SvelteKit 3, Svelte 5, and Vite 8 - Astro 6 - TypeScript - Tailwind CSS 4 diff --git a/docs/code/development.md b/docs/code/development.md index f916ebdf..501c8706 100644 --- a/docs/code/development.md +++ b/docs/code/development.md @@ -43,6 +43,34 @@ bun run dev The root `dev` command starts both applications. It does not start the pipeline. +## Choose the database driver + +The dashboard has two database drivers. Each build and each development server includes only one of them. + +| `ATLANTIS_DB_DRIVER` | Driver | Adapter | Default for | +| -------------------- | --------------------------------------------------------------------------------------------- | -------------------- | ------------ | +| `sqlite` | `src/lib/server/db/sqlite.ts` reads `data//netflow.sqlite` or `LOCAL_SQLITE_PATH` | None (Node output) | `vite dev` | +| `d1` | `src/lib/server/db/d1.ts` reads the `DB` binding from `cloudflare:workers` | `adapter-cloudflare` | `vite build` | + +Server code imports the driver as `#db`. The `imports` field in `apps/web/package.json` maps `#db` to the D1 driver under the `atlantis-d1` export condition, and to the SQLite driver otherwise. `apps/web/vite.config.ts` adds that condition to the server environments when the driver is `d1`. Both drivers implement `DatabaseDriver` in `src/lib/server/db/driver.ts`. + +Set `ATLANTIS_DB_DRIVER` in the shell. The value in `.env` does not select the driver, so a deploy build cannot pick up a local SQLite setting. + +```bash +bun run dev:web # SQLite, no Workers runtime +ATLANTIS_DB_DRIVER=d1 bun run dev:web # local D1 through the Workers runtime +bun run build:web # Cloudflare worker with D1 +ATLANTIS_DB_DRIVER=sqlite bun run build:web # Node output with SQLite +``` + +## Configure the dashboard + +SvelteKit options are in the `sveltekit()` call in `apps/web/vite.config.ts`. The project has no `svelte.config.js`. + +The dashboard loads `.env` from the repository root. `apps/web/src/env.ts` declares the runtime variables, and server code imports them from `$app/env/private`. Add a variable to `src/env.ts` before you use it. + +Import library modules through `#lib` with the file extension, for example `#lib/utils.ts` or `#lib/components/charts/ChartCard.svelte`. + ## Run required checks Run these commands before you complete a change: @@ -76,6 +104,8 @@ Run the Playwright suite when a browser flow changes: bun run test:e2e ``` +The suite builds a SQLite bundle, serves it with `vite preview`, and runs against a seeded fixture database. + Always use `bun run test`. Do not use `bun test` in this repository. ## Build applications diff --git a/docs/user/operations.md b/docs/user/operations.md index 56156788..50eaa952 100644 --- a/docs/user/operations.md +++ b/docs/user/operations.md @@ -82,13 +82,20 @@ Apply migrations to a local D1 database: bun run --cwd apps/web d1:migrations:apply:local ``` -To develop the dashboard against the local D1 database, export the driver before you start it: +To develop the dashboard against the local D1 database, set the driver in the shell when you start it: ```bash ATLANTIS_DB_DRIVER=d1 bun run dev:web ``` -Without this variable, local development uses SQLite and does not start the Workers runtime. +Without this variable, local development uses SQLite and does not start the Workers runtime. The dashboard ignores `ATLANTIS_DB_DRIVER` in `.env`. + +To test the built worker against the local D1 database, build it and start Wrangler: + +```bash +bun run build:web +cd apps/web && bunx wrangler dev +``` Apply migrations to the configured remote D1 database: @@ -127,7 +134,7 @@ The restore command needs Cloudflare access and confirmation. Check the database ## Deploy the dashboard -1. Build and check the worker package. +1. Build and check the worker package. Do not set `ATLANTIS_DB_DRIVER` in the shell. The build uses D1 by default. ```bash bun run build:web diff --git a/docs/user/requirements.md b/docs/user/requirements.md index 6d9c0cdc..8264b9e0 100644 --- a/docs/user/requirements.md +++ b/docs/user/requirements.md @@ -20,7 +20,7 @@ The dashboard and all `bun run` commands need these tools: | ------- | ------------------------- | --------------- | | Git | Current supported version | Git releases | | Bun | 1.2.16 | `package.json` | -| Node.js | 22.16.0 | `.node-version` | +| Node.js | 24.4.1 | `.node-version` | Node.js is necessary even though Bun installs the packages. The development server runs under Node.js, and `bun install` needs Node.js on `PATH` to download the prebuilt SQLite driver. Without it, the install prints a `better-sqlite3` warning and the dashboard cannot open a database (see [Troubleshooting](troubleshooting.md)). diff --git a/docs/user/setup-web.md b/docs/user/setup-web.md index bd621969..c7156cce 100644 --- a/docs/user/setup-web.md +++ b/docs/user/setup-web.md @@ -37,6 +37,8 @@ First, [configure a dataset](datasets.md). Then, [set up the data pipeline](setu 2. In `.env`, set `DEFAULT_DATASET` to your dataset ID. This step is optional. Without it, the dashboard uses the first discovered dataset. + The dashboard reads `DEFAULT_DATASET`, `LOCAL_SQLITE_PATH`, and `DATABASE_PATH` from `.env`. `apps/web/src/env.ts` lists these variables. + 3. Start the dashboard. ```bash diff --git a/package.json b/package.json index 9623fa96..3ff87351 100644 --- a/package.json +++ b/package.json @@ -34,5 +34,8 @@ }, "workspaces": [ "apps/*" - ] + ], + "patchedDependencies": { + "runed@0.36.0": "patches/runed@0.36.0.patch" + } } diff --git a/patches/runed@0.36.0.patch b/patches/runed@0.36.0.patch new file mode 100644 index 00000000..f10ebc8e --- /dev/null +++ b/patches/runed@0.36.0.patch @@ -0,0 +1,43 @@ +diff --git a/dist/utilities/use-search-params/use-search-params.svelte.js b/dist/utilities/use-search-params/use-search-params.svelte.js +index 8ddeb0eacf7ef5884c222536ff76588a4134d0c7..ad33af1fc23d327e8666757144f7340474d8cb41 100644 +--- a/dist/utilities/use-search-params/use-search-params.svelte.js ++++ b/dist/utilities/use-search-params/use-search-params.svelte.js +@@ -683,7 +683,7 @@ class SearchParams { + this.#localCache = new SvelteURLSearchParams(); + // If updateURL is true, clear the URL + if (this.#options.updateURL && BROWSER && !building) { +- goto("?", { replaceState: true, noScroll: this.#options.noScroll }); ++ goto("?", { replace: true, reset: !this.#options.noScroll }); + } + } + } +@@ -786,10 +786,7 @@ class SearchParams { + if (!BROWSER) + return; + // When pushHistory is false, use replaceState to avoid creating a browser history entry +- const gotoOptions = !this.#options.pushHistory +- ? { replaceState: true, keepFocus: true } +- : { keepFocus: true }; +- goto("?" + params.toString(), { ...gotoOptions, noScroll: this.#options.noScroll }); ++ goto("?" + params.toString(), { replace: !this.#options.pushHistory, reset: !this.#options.noScroll }); + }; + // If debounce is set, delay the URL update + if (this.#options.debounce && this.#options.debounce > 0) { +@@ -1395,7 +1392,7 @@ export function useSearchParams(schema, options = {}) { + for (const key of invalidKeys) { + newSearchParams.delete(String(key)); + } +- goto("?" + newSearchParams.toString(), { replaceState: true }); ++ goto("?" + newSearchParams.toString(), { replace: true }); + } + } + } +@@ -1443,7 +1440,7 @@ export function useSearchParams(schema, options = {}) { + if (needsUpdate) { + // Always use replaceState: true for initialization to avoid creating a new history entry + // Don't use debouncing for initialization as this is a one-time operation +- goto("?" + newSearchParams.toString(), { replaceState: true }); ++ goto("?" + newSearchParams.toString(), { replace: true }); + } + } + } diff --git a/shell.nix b/shell.nix index 4e272465..1bc919ef 100644 --- a/shell.nix +++ b/shell.nix @@ -16,7 +16,7 @@ mkShell { pkgs.gnumake pkgs.gnutar pkgs.libtool - pkgs.nodejs + pkgs.nodejs_24 pkgs.pkg-config pkgs.python3 pkgs.rustup From 3dffdaad3c3d5594f0c08bcdacaa5b1bf125da34 Mon Sep 17 00:00:00 2001 From: flamboh Date: Thu, 24 Sep 2026 16:36:45 -0700 Subject: [PATCH 2/7] refactor(web): replace runed with SvelteKit URL state Read dashboard filters from page.url through the zod schema, keep the group-by clamp derived, and write the URL only from event handlers via goto with SvelteKit 3 options. Drop runed and its goto patch. --- apps/web/package.json | 1 - .../flow-characteristics-data.svelte.ts | 249 +++++++++--------- .../netflow/DatasetDashboardPage.svelte | 123 +++------ apps/web/src/lib/schemas.ts | 77 +++++- apps/web/src/lib/utils/search-navigation.ts | 8 + apps/web/tests/lib/schemas.test.ts | 105 ++++++-- apps/web/tests/lib/search-navigation.test.ts | 14 + bun.lock | 30 +-- package.json | 5 +- patches/runed@0.36.0.patch | 43 --- 10 files changed, 339 insertions(+), 316 deletions(-) create mode 100644 apps/web/src/lib/utils/search-navigation.ts create mode 100644 apps/web/tests/lib/search-navigation.test.ts delete mode 100644 patches/runed@0.36.0.patch diff --git a/apps/web/package.json b/apps/web/package.json index 9a920e41..9136fdec 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -68,7 +68,6 @@ "chart.js": "^4.4.9", "chartjs-plugin-annotation": "^3.1.0", "drizzle-orm": "^0.45.2", - "runed": "^0.36.0", "zod": "^4.3.6" }, "imports": { diff --git a/apps/web/src/lib/components/charts/flow-characteristics-data.svelte.ts b/apps/web/src/lib/components/charts/flow-characteristics-data.svelte.ts index 40a3b096..0c6283ba 100644 --- a/apps/web/src/lib/components/charts/flow-characteristics-data.svelte.ts +++ b/apps/web/src/lib/components/charts/flow-characteristics-data.svelte.ts @@ -1,6 +1,4 @@ -import { onDestroy } from 'svelte'; import { SvelteMap, SvelteURLSearchParams } from 'svelte/reactivity'; -import { watch } from 'runed'; import { dateStringToEpochPST } from '#lib/utils/timezone.ts'; import type { GroupByOption, RouterConfig } from '#lib/components/netflow/types.ts'; import type { @@ -113,140 +111,155 @@ function readCachedData(key: string, requestedRange: TimeRange): FlowCharacteris }; } +type CharacteristicsRequest = + | { kind: 'idle' } + | { kind: 'waiting' } + | { kind: 'invalid'; error: string } + | { + kind: 'fetch'; + id: string; + key: string; + requestedRange: TimeRange; + baseParams: Record; + missing: boolean; + }; + +type FetchRequest = Extract; + +type SettledRequest = { + id: string; + data: FlowCharacteristicsResponse | null; + error: string | null; +}; + +function describeRequest(filtersKey: string): CharacteristicsRequest { + const filters = JSON.parse(filtersKey) as FlowCharacteristicsFilters; + if (!filters.enabled) return { kind: 'idle' }; + if (!filters.routersLoaded) return { kind: 'waiting' }; + + const routers = selectedSources(filters.routers); + if (routers.length === 0) { + return { kind: 'invalid', error: 'Select at least one source to view flow characteristics' }; + } + + const requestedRange = { + start: dateStringToEpochPST(filters.startDate), + end: dateStringToEpochPST(filters.endDate, true) + }; + const key = cacheKey(filters, routers); + return { + kind: 'fetch', + id: filtersKey, + key, + requestedRange, + baseParams: { + dataset: filters.dataset, + routers: routers.join(','), + granularity: GROUP_BY_TO_GRANULARITY[filters.groupBy], + direction: filters.direction + }, + missing: getMissingWindowRanges(key, requestedRange).length > 0 + }; +} + +async function fetchCharacteristics( + request: FetchRequest, + signal: AbortSignal +): Promise { + await ensureCachedWindow({ + key: request.key, + requestedRange: request.requestedRange, + signal, + fetchRange: async (range, signal) => { + const params = new SvelteURLSearchParams({ + ...request.baseParams, + startDate: range.start.toString(), + endDate: range.end.toString() + }); + const response = await fetch(`/api/netflow/characteristics?${params}`, { signal }); + if (!response.ok) throw new Error((await response.text()) || 'Request failed'); + const next = (await response.json()) as FlowCharacteristicsResponse; + return [ + ...next.resolvedSources.map( + (sourceId, sourceIndex): CachedCharacteristicsRecord => ({ + kind: 'source', + sourceId, + sourceIndex + }) + ), + ...next.observationBuckets.map( + (bucket): CachedCharacteristicsRecord => ({ kind: 'observation', bucket }) + ), + ...next.portTimelines.flatMap((timeline) => + timeline.buckets.map( + (bucket): CachedCharacteristicsRecord => ({ + kind: 'port', + sourceId: timeline.sourceId, + bucket + }) + ) + ) + ]; + }, + getRecordKey: (record) => { + if (record.kind === 'source') return `source:${record.sourceId}`; + if (record.kind === 'observation') return `observation:${record.bucket.bucketStart}`; + return `port:${record.sourceId}:${record.bucket.bucketStart}`; + }, + compareRecords: (left, right) => + recordStart(left) - recordStart(right) || + (left.kind === 'port' ? left.sourceId : '').localeCompare( + right.kind === 'port' ? right.sourceId : '' + ) + }); + return readCachedData(request.key, request.requestedRange); +} + /** Share one cached characteristics request between the observation and port cards. */ export function createFlowCharacteristicsData( getFilters: () => FlowCharacteristicsFilters ): FlowCharacteristicsData { - let data = $state.raw(null); - let loading = $state(false); - let error = $state(null); + const filtersKey = $derived(JSON.stringify(getFilters())); + const request = $derived(describeRequest(filtersKey)); + let settled = $state.raw(null); const requestGate = createRequestGate(); - let requestController: AbortController | null = null; - async function loadData(filters: FlowCharacteristicsFilters) { + $effect(() => { + if (request.kind !== 'fetch') return; + const current = request; const token = requestGate.begin(); - requestController?.abort(); - requestController = null; - - if (!filters.enabled) { - data = null; - error = null; - loading = false; - return; - } - if (!filters.routersLoaded) { - data = null; - error = null; - loading = true; - return; - } - - const routers = selectedSources(filters.routers); - if (routers.length === 0) { - data = null; - error = 'Select at least one source to view flow characteristics'; - loading = false; - return; - } - - const granularity = GROUP_BY_TO_GRANULARITY[filters.groupBy]; - const requestedRange = { - start: dateStringToEpochPST(filters.startDate), - end: dateStringToEpochPST(filters.endDate, true) - }; - const key = cacheKey(filters, routers); - loading = getMissingWindowRanges(key, requestedRange).length > 0; - error = null; - const baseParams = new SvelteURLSearchParams({ - dataset: filters.dataset, - routers: routers.join(','), - granularity, - direction: filters.direction - }); const controller = new AbortController(); - requestController = controller; - - try { - await ensureCachedWindow({ - key, - requestedRange, - signal: controller.signal, - fetchRange: async (range, signal) => { - const params = new SvelteURLSearchParams({ - ...Object.fromEntries(baseParams.entries()), - startDate: range.start.toString(), - endDate: range.end.toString() - }); - const response = await fetch(`/api/netflow/characteristics?${params}`, { signal }); - if (!response.ok) throw new Error((await response.text()) || 'Request failed'); - const next = (await response.json()) as FlowCharacteristicsResponse; - return [ - ...next.resolvedSources.map( - (sourceId, sourceIndex): CachedCharacteristicsRecord => ({ - kind: 'source', - sourceId, - sourceIndex - }) - ), - ...next.observationBuckets.map( - (bucket): CachedCharacteristicsRecord => ({ kind: 'observation', bucket }) - ), - ...next.portTimelines.flatMap((timeline) => - timeline.buckets.map( - (bucket): CachedCharacteristicsRecord => ({ - kind: 'port', - sourceId: timeline.sourceId, - bucket - }) - ) - ) - ]; - }, - getRecordKey: (record) => { - if (record.kind === 'source') return `source:${record.sourceId}`; - if (record.kind === 'observation') return `observation:${record.bucket.bucketStart}`; - return `port:${record.sourceId}:${record.bucket.bucketStart}`; - }, - compareRecords: (left, right) => - recordStart(left) - recordStart(right) || - (left.kind === 'port' ? left.sourceId : '').localeCompare( - right.kind === 'port' ? right.sourceId : '' - ) - }); - if (requestGate.isCurrent(token)) data = readCachedData(key, requestedRange); - } catch (reason) { - if (requestGate.isCurrent(token)) { + fetchCharacteristics(current, controller.signal).then( + (data) => { + if (requestGate.isCurrent(token)) settled = { id: current.id, data, error: null }; + }, + (reason: unknown) => { + if (!requestGate.isCurrent(token)) return; if (reason instanceof DOMException && reason.name === 'AbortError') return; - data = null; - error = reason instanceof Error ? reason.message : 'Failed to load flow characteristics'; + settled = { + id: current.id, + data: null, + error: reason instanceof Error ? reason.message : 'Failed to load flow characteristics' + }; } - } finally { - if (requestGate.isCurrent(token)) { - loading = false; - if (requestController === controller) requestController = null; - } - } - } - - watch( - () => JSON.stringify(getFilters()), - () => void loadData(getFilters()) - ); - - onDestroy(() => { - requestGate.begin(); - requestController?.abort(); + ); + return () => { + requestGate.begin(); + controller.abort(); + }; }); return { get data() { - return data; + return request.kind === 'fetch' ? (settled?.data ?? null) : null; }, get loading() { - return loading; + if (request.kind === 'waiting') return true; + return request.kind === 'fetch' && settled?.id !== request.id && request.missing; }, get error() { - return error; + if (request.kind === 'invalid') return request.error; + return request.kind === 'fetch' && settled?.id === request.id ? settled.error : null; } }; } diff --git a/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte b/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte index 597cada8..f4aa1a4b 100644 --- a/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte +++ b/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte @@ -1,5 +1,6 @@ diff --git a/apps/web/src/lib/schemas.ts b/apps/web/src/lib/schemas.ts index da2d88dc..608eb508 100644 --- a/apps/web/src/lib/schemas.ts +++ b/apps/web/src/lib/schemas.ts @@ -1,21 +1,74 @@ -// schemas.ts +import type { GroupByOption } from '#lib/components/netflow/types.ts'; import { DEFAULT_MAAD_IP_VERSION, DEFAULT_MAAD_MEASURE, FLOW_DIRECTIONS, MAAD_IP_VERSIONS, - MAAD_MEASURES + MAAD_MEASURES, + type FlowDirection, + type MaadIpVersion, + type MaadMeasure } from '#lib/types/types.ts'; import { z } from 'zod'; -// Requires Zod 4+ - -export function createDateRangeSearchSchema(defaultStartDate: string) { - return z.object({ - startDate: z.iso.date().default(defaultStartDate), - endDate: z.iso.date().default(new Date().toJSON().slice(0, 10)), - groupBy: z.enum(['date', 'hour', '30min', '10min', '5min']).default('date'), - direction: z.enum(FLOW_DIRECTIONS).default('all'), - ipVersion: z.literal(MAAD_IP_VERSIONS).default(DEFAULT_MAAD_IP_VERSION), - measure: z.enum(MAAD_MEASURES).default(DEFAULT_MAAD_MEASURE) + +export type DateRangeSearch = { + startDate: string; + endDate: string; + groupBy: GroupByOption; + direction: FlowDirection; + ipVersion: MaadIpVersion; + measure: MaadMeasure; +}; + +type SearchParamsReader = Pick; + +const GROUP_BY_OPTIONS = [ + 'date', + 'hour', + '30min', + '10min', + '5min' +] as const satisfies GroupByOption[]; + +const IP_VERSION_PARAMS = MAAD_IP_VERSIONS.map(String) as [string, ...string[]]; + +export function createDateRangeSearch( + defaultStartDate: string, + today = new Date().toJSON().slice(0, 10) +) { + const schema = z.object({ + startDate: z.iso.date().catch(defaultStartDate), + endDate: z.iso.date().catch(today), + groupBy: z.enum(GROUP_BY_OPTIONS).catch('date'), + direction: z.enum(FLOW_DIRECTIONS).catch('all'), + ipVersion: z + .enum(IP_VERSION_PARAMS) + .transform((value) => Number(value) as MaadIpVersion) + .catch(DEFAULT_MAAD_IP_VERSION), + measure: z.enum(MAAD_MEASURES).catch(DEFAULT_MAAD_MEASURE) }); + const keys = schema.keyof().options; + const defaults: DateRangeSearch = schema.parse({}); + + function parse(searchParams: SearchParamsReader): DateRangeSearch { + return schema.parse(Object.fromEntries(keys.map((key) => [key, searchParams.get(key)]))); + } + + function serialize(current: SearchParamsReader, next: DateRangeSearch): URLSearchParams { + const params = new URLSearchParams(current.toString()); + for (const key of keys) { + const raw = current.get(key); + const value = String(next[key]); + if (raw === null ? next[key] === defaults[key] : raw === value) continue; + if (next[key] === defaults[key]) params.delete(key); + else params.set(key, value); + } + return params; + } + + function equals(left: DateRangeSearch, right: DateRangeSearch): boolean { + return keys.every((key) => left[key] === right[key]); + } + + return { defaults, parse, serialize, equals }; } diff --git a/apps/web/src/lib/utils/search-navigation.ts b/apps/web/src/lib/utils/search-navigation.ts new file mode 100644 index 00000000..42005f79 --- /dev/null +++ b/apps/web/src/lib/utils/search-navigation.ts @@ -0,0 +1,8 @@ +import type { goto } from '$app/navigation'; + +export function navigateToSearchParams( + navigate: typeof goto, + params: URLSearchParams +): Promise { + return navigate(`?${params}`, { reset: false }); +} diff --git a/apps/web/tests/lib/schemas.test.ts b/apps/web/tests/lib/schemas.test.ts index fd8fa3cb..e8ae953f 100644 --- a/apps/web/tests/lib/schemas.test.ts +++ b/apps/web/tests/lib/schemas.test.ts @@ -1,37 +1,108 @@ import { describe, expect, it } from 'vitest'; -import { createDateRangeSearchSchema } from '../../src/lib/schemas'; +import { createDateRangeSearch } from '#lib/schemas.ts'; -describe('date range search params', () => { - const schema = createDateRangeSearchSchema('2026-03-01'); +const search = createDateRangeSearch('2025-01-01', '2025-02-01'); + +describe('createDateRangeSearch', () => { + it('falls back to defaults for missing params', () => { + expect(search.parse(new URLSearchParams())).toEqual({ + startDate: '2025-01-01', + endDate: '2025-02-01', + groupBy: 'date', + direction: 'all', + ipVersion: 4, + measure: 'addresses' + }); + }); + + it('parses valid params and falls back per key for invalid ones', () => { + expect( + search.parse( + new URLSearchParams( + 'startDate=2025-01-10&endDate=nope&groupBy=hour&direction=bogus&ipVersion=6&measure=bytes' + ) + ) + ).toEqual({ + startDate: '2025-01-10', + endDate: '2025-02-01', + groupBy: 'hour', + direction: 'all', + ipVersion: 6, + measure: 'bytes' + }); + }); it('accepts every dashboard grouping including 10 minutes', () => { for (const groupBy of ['date', 'hour', '30min', '10min', '5min']) { - expect(schema.parse({ groupBy }).groupBy).toBe(groupBy); + expect(search.parse(new URLSearchParams({ groupBy })).groupBy).toBe(groupBy); } }); - it('defaults the MAAD IP family to IPv4 and accepts IPv6', () => { - expect(schema.parse({}).ipVersion).toBe(4); - expect(schema.parse({ ipVersion: 6 }).ipVersion).toBe(6); + it('rejects stored granularity spellings as groupings', () => { + expect(search.parse(new URLSearchParams('groupBy=10m')).groupBy).toBe('date'); }); - it('rejects unknown MAAD IP families', () => { - expect(schema.safeParse({ ipVersion: 5 }).success).toBe(false); - expect(schema.safeParse({ ipVersion: '6' }).success).toBe(false); + it('accepts every flow direction', () => { + for (const direction of ['all', 'ingress', 'egress', 'lateral', 'transit']) { + expect(search.parse(new URLSearchParams({ direction })).direction).toBe(direction); + } }); - it('rejects stored granularity spellings as groupings', () => { - expect(schema.safeParse({ groupBy: '10m' }).success).toBe(false); + it('falls back to IPv4 for unknown MAAD IP families', () => { + for (const ipVersion of ['5', '6.0', 'v6', '']) { + expect(search.parse(new URLSearchParams({ ipVersion })).ipVersion).toBe(4); + } }); - it('defaults the MAAD measure to addresses and accepts weighted measures', () => { - expect(schema.parse({}).measure).toBe('addresses'); + it('accepts weighted MAAD measures and falls back for unknown ones', () => { for (const measure of ['addresses', 'packets', 'bytes']) { - expect(schema.parse({ measure }).measure).toBe(measure); + expect(search.parse(new URLSearchParams({ measure })).measure).toBe(measure); } + expect(search.parse(new URLSearchParams('measure=flows')).measure).toBe('addresses'); + }); + + it('omits default values and keeps unrelated params', () => { + const current = new URLSearchParams('foo=bar'); + const params = search.serialize(current, { + ...search.defaults, + startDate: '2025-01-10', + groupBy: 'hour' + }); + expect(params.toString()).toBe('foo=bar&startDate=2025-01-10&groupBy=hour'); + }); + + it('writes MAAD params and removes them when they return to defaults', () => { + const params = search.serialize(new URLSearchParams(), { + ...search.defaults, + direction: 'ingress', + ipVersion: 6, + measure: 'packets' + }); + expect(params.toString()).toBe('direction=ingress&ipVersion=6&measure=packets'); + expect(search.serialize(params, search.defaults).toString()).toBe(''); + }); + + it('removes params that change back to their defaults', () => { + const current = new URLSearchParams('startDate=2025-01-10&groupBy=hour'); + const params = search.serialize(current, { ...search.defaults, startDate: '2025-01-10' }); + expect(params.toString()).toBe('startDate=2025-01-10'); + }); + + it('leaves unchanged explicit params in place', () => { + const current = new URLSearchParams('groupBy=date&endDate=2025-01-20&ipVersion=4'); + const params = search.serialize(current, { ...search.defaults, endDate: '2025-01-20' }); + expect(params.toString()).toBe('groupBy=date&endDate=2025-01-20&ipVersion=4'); + }); + + it('drops invalid params when the parsed value is written back', () => { + const current = new URLSearchParams('groupBy=bogus&startDate=2025-01-10&ipVersion=5'); + const params = search.serialize(current, search.parse(current)); + expect(params.toString()).toBe('startDate=2025-01-10'); }); - it('rejects unknown MAAD measures', () => { - expect(schema.safeParse({ measure: 'flows' }).success).toBe(false); + it('compares parsed values key by key', () => { + expect(search.equals(search.defaults, { ...search.defaults })).toBe(true); + expect(search.equals(search.defaults, { ...search.defaults, groupBy: 'hour' })).toBe(false); + expect(search.equals(search.defaults, { ...search.defaults, ipVersion: 6 })).toBe(false); }); }); diff --git a/apps/web/tests/lib/search-navigation.test.ts b/apps/web/tests/lib/search-navigation.test.ts new file mode 100644 index 00000000..98b81ce7 --- /dev/null +++ b/apps/web/tests/lib/search-navigation.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { goto } from '$app/navigation'; +import { navigateToSearchParams } from '#lib/utils/search-navigation.ts'; + +describe('navigateToSearchParams', () => { + it('pushes a new history entry without resetting scroll or focus', async () => { + const navigate = vi.fn().mockResolvedValue(); + await navigateToSearchParams( + navigate, + new URLSearchParams('startDate=2025-01-02&groupBy=hour') + ); + expect(navigate).toHaveBeenCalledWith('?startDate=2025-01-02&groupBy=hour', { reset: false }); + }); +}); diff --git a/bun.lock b/bun.lock index 8823e938..18b14b2a 100644 --- a/bun.lock +++ b/bun.lock @@ -36,7 +36,6 @@ "chart.js": "^4.4.9", "chartjs-plugin-annotation": "^3.1.0", "drizzle-orm": "^0.45.2", - "runed": "^0.36.0", "zod": "^4.3.6", }, "devDependencies": { @@ -76,9 +75,6 @@ }, }, }, - "patchedDependencies": { - "runed@0.36.0": "patches/runed@0.36.0.patch", - }, "packages": { "@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], @@ -1280,7 +1276,7 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "runed": ["runed@0.36.0", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0", "zod": "^4.1.0" }, "optionalPeers": ["@sveltejs/kit", "zod"] }, "sha512-CK84KPwAausPQEyWF9t6miCuNW5isAKPMswDsz7jhdueiZZ9du/UrgWc/aggLts8QuppT8KucryrHDFBAqk9Ww=="], + "runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="], "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], @@ -1634,8 +1630,6 @@ "astro-eslint-parser/espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - "bits-ui/runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="], - "cacache/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], "cacache/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], @@ -1710,8 +1704,6 @@ "svelte-eslint-parser/espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], - "svelte-toolbelt/runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="], - "svgo/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "tar/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], @@ -1916,8 +1908,6 @@ "astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], - "bits-ui/runed/@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -1986,8 +1976,6 @@ "svelte-eslint-parser/espree/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "svelte-toolbelt/runed/@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], @@ -2112,24 +2100,8 @@ "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "bits-ui/runed/@sveltejs/kit/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], - - "bits-ui/runed/@sveltejs/kit/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - - "bits-ui/runed/@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], - - "bits-ui/runed/@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], - "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "svelte-toolbelt/runed/@sveltejs/kit/@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], - - "svelte-toolbelt/runed/@sveltejs/kit/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - - "svelte-toolbelt/runed/@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], - - "svelte-toolbelt/runed/@sveltejs/kit/devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], - "@atlantis/web/eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "@eslint/compat/eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], diff --git a/package.json b/package.json index 3ff87351..9623fa96 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,5 @@ }, "workspaces": [ "apps/*" - ], - "patchedDependencies": { - "runed@0.36.0": "patches/runed@0.36.0.patch" - } + ] } diff --git a/patches/runed@0.36.0.patch b/patches/runed@0.36.0.patch deleted file mode 100644 index f10ebc8e..00000000 --- a/patches/runed@0.36.0.patch +++ /dev/null @@ -1,43 +0,0 @@ -diff --git a/dist/utilities/use-search-params/use-search-params.svelte.js b/dist/utilities/use-search-params/use-search-params.svelte.js -index 8ddeb0eacf7ef5884c222536ff76588a4134d0c7..ad33af1fc23d327e8666757144f7340474d8cb41 100644 ---- a/dist/utilities/use-search-params/use-search-params.svelte.js -+++ b/dist/utilities/use-search-params/use-search-params.svelte.js -@@ -683,7 +683,7 @@ class SearchParams { - this.#localCache = new SvelteURLSearchParams(); - // If updateURL is true, clear the URL - if (this.#options.updateURL && BROWSER && !building) { -- goto("?", { replaceState: true, noScroll: this.#options.noScroll }); -+ goto("?", { replace: true, reset: !this.#options.noScroll }); - } - } - } -@@ -786,10 +786,7 @@ class SearchParams { - if (!BROWSER) - return; - // When pushHistory is false, use replaceState to avoid creating a browser history entry -- const gotoOptions = !this.#options.pushHistory -- ? { replaceState: true, keepFocus: true } -- : { keepFocus: true }; -- goto("?" + params.toString(), { ...gotoOptions, noScroll: this.#options.noScroll }); -+ goto("?" + params.toString(), { replace: !this.#options.pushHistory, reset: !this.#options.noScroll }); - }; - // If debounce is set, delay the URL update - if (this.#options.debounce && this.#options.debounce > 0) { -@@ -1395,7 +1392,7 @@ export function useSearchParams(schema, options = {}) { - for (const key of invalidKeys) { - newSearchParams.delete(String(key)); - } -- goto("?" + newSearchParams.toString(), { replaceState: true }); -+ goto("?" + newSearchParams.toString(), { replace: true }); - } - } - } -@@ -1443,7 +1440,7 @@ export function useSearchParams(schema, options = {}) { - if (needsUpdate) { - // Always use replaceState: true for initialization to avoid creating a new history entry - // Don't use debouncing for initialization as this is a one-time operation -- goto("?" + newSearchParams.toString(), { replaceState: true }); -+ goto("?" + newSearchParams.toString(), { replace: true }); - } - } - } From be736a3e0b66c6da17e5fdc4103231c21bc03bdf Mon Sep 17 00:00:00 2001 From: flamboh Date: Sat, 26 Sep 2026 13:34:43 -0700 Subject: [PATCH 3/7] fix(web): apply dashboard drill-down grouping and dates in one URL update --- .../components/charts/BreakdownChart.svelte | 7 +-- .../netflow/DatasetDashboardPage.svelte | 35 +++++------ .../netflow/NetflowDashboard.svelte | 6 +- .../web/tests/e2e/dashboard-drilldown.spec.ts | 58 +++++++++++++++++++ apps/web/tests/e2e/playwright-fixture.js | 16 ++++- 5 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 apps/web/tests/e2e/dashboard-drilldown.spec.ts diff --git a/apps/web/src/lib/components/charts/BreakdownChart.svelte b/apps/web/src/lib/components/charts/BreakdownChart.svelte index 8b39e364..6ac2c5f3 100644 --- a/apps/web/src/lib/components/charts/BreakdownChart.svelte +++ b/apps/web/src/lib/components/charts/BreakdownChart.svelte @@ -117,8 +117,7 @@ routers?: RouterConfig; activeMetrics?: MetricsForKind; direction?: FlowDirection; - onDateChange?: (payload: { startDate: string; endDate: string }) => void; - onGroupByChange?: (payload: { groupBy: GroupByOption }) => void; + onDrillDown?: (payload: { groupBy: GroupByOption; startDate: string; endDate: string }) => void; onRouterChange?: (payload: { router: string }) => void; onAddressTypeChange?: (payload: { addressType: 'sa' | 'da' }) => void; onMetricsChange?: (payload: { metrics: MetricsForKind }) => void; @@ -507,8 +506,8 @@ } function emitDrilldown(nextGroupBy: GroupByOption, start: Date, end: Date) { - props.onGroupByChange?.({ groupBy: nextGroupBy }); - props.onDateChange?.({ + props.onDrillDown?.({ + groupBy: nextGroupBy, startDate: formatDate(start), endDate: formatDate(end) }); diff --git a/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte b/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte index f4aa1a4b..5126e99d 100644 --- a/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte +++ b/apps/web/src/lib/components/netflow/DatasetDashboardPage.svelte @@ -373,20 +373,20 @@ updateSearch({ endDate: payload.endDate }); } - function handleDateChange(payload: { startDate: string; endDate: string }) { - updateSearch({ startDate: payload.startDate, endDate: payload.endDate }); - } - function handleGroupByChange(payload: { groupBy: GroupByOption }) { updateSearch({ groupBy: payload.groupBy }); } - function handleMetricDrillDown( - groupBy: GroupByOption, - nextStartDate: string, - nextEndDate: string - ) { - updateSearch({ groupBy, startDate: nextStartDate, endDate: nextEndDate }); + function handleDrillDown(payload: { + groupBy: GroupByOption; + startDate: string; + endDate: string; + }) { + updateSearch(payload); + } + + function handleMetricDrillDown(groupBy: GroupByOption, startDate: string, endDate: string) { + handleDrillDown({ groupBy, startDate, endDate }); } function handleMetricNavigateToFile(slug: string) { @@ -527,8 +527,7 @@ {direction} {ipVersion} {measure} - onDateChange={handleDateChange} - onGroupByChange={handleGroupByChange} + onDrillDown={handleDrillDown} onDataOptionsChange={handleDataOptionsChange} /> {:else if chartId === 'characteristics'} @@ -560,8 +559,7 @@ activeMetrics={ipMetrics} {direction} {ipVersion} - onDateChange={handleDateChange} - onGroupByChange={handleGroupByChange} + onDrillDown={handleDrillDown} onMetricsChange={handleIpMetricsChange} /> {:else if chartId === 'protocol'} @@ -575,8 +573,7 @@ activeMetrics={protocolMetrics} {direction} {ipVersion} - onDateChange={handleDateChange} - onGroupByChange={handleGroupByChange} + onDrillDown={handleDrillDown} onMetricsChange={(payload) => { protocolMetrics = payload.metrics; }} @@ -594,8 +591,7 @@ {measure} unavailableCopy={maadUnavailableCopy} {direction} - onDateChange={handleDateChange} - onGroupByChange={handleGroupByChange} + onDrillDown={handleDrillDown} onMetricsChange={(payload) => { dimensionMetrics = payload.metrics; }} @@ -614,8 +610,7 @@ unavailableCopy={spectrumUnavailableCopy} availableRouters={availableSpectrumRouters} {direction} - onDateChange={handleDateChange} - onGroupByChange={handleGroupByChange} + onDrillDown={handleDrillDown} onRouterChange={(payload) => { selectedSpectrumRouter = payload.router; }} diff --git a/apps/web/src/lib/components/netflow/NetflowDashboard.svelte b/apps/web/src/lib/components/netflow/NetflowDashboard.svelte index da2f109e..e095a2b0 100644 --- a/apps/web/src/lib/components/netflow/NetflowDashboard.svelte +++ b/apps/web/src/lib/components/netflow/NetflowDashboard.svelte @@ -42,8 +42,7 @@ direction: FlowDirection; ipVersion?: MaadIpVersion; measure?: MaadMeasure; - onDateChange?: (payload: { startDate: string; endDate: string }) => void; - onGroupByChange?: (payload: { groupBy: GroupByOption }) => void; + onDrillDown?: (payload: { groupBy: GroupByOption; startDate: string; endDate: string }) => void; onDataOptionsChange?: (payload: { options: DataOption[] }) => void; }>(); const IP_FAMILY_LABELS: Record = { @@ -236,8 +235,7 @@ } function handleDrillDown(newGroupBy: GroupByOption, newStartDate: string, newEndDate: string) { - props.onGroupByChange?.({ groupBy: newGroupBy }); - props.onDateChange?.({ startDate: newStartDate, endDate: newEndDate }); + props.onDrillDown?.({ groupBy: newGroupBy, startDate: newStartDate, endDate: newEndDate }); } function handleNavigateToFile(slug: string) { diff --git a/apps/web/tests/e2e/dashboard-drilldown.spec.ts b/apps/web/tests/e2e/dashboard-drilldown.spec.ts new file mode 100644 index 00000000..892f0f88 --- /dev/null +++ b/apps/web/tests/e2e/dashboard-drilldown.spec.ts @@ -0,0 +1,58 @@ +import { expect, test, type Page } from '@playwright/test'; + +const WIDE_DAILY_RANGE = + '/datasets/playwright?startDate=2025-01-01&endDate=2025-03-31&groupBy=date'; + +async function clickChartCenter(page: Page, chartId: string) { + const canvas = page.locator(`[data-chart-id="${chartId}"] canvas`).first(); + await expect(canvas).toBeVisible(); + await canvas.scrollIntoViewIfNeeded(); + const box = await canvas.boundingBox(); + if (!box) throw new Error(`Missing ${chartId} canvas bounds`); + await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2); +} + +function searchParam(page: Page, key: string) { + return new URL(page.url()).searchParams.get(key); +} + +test('traffic overview drill-down from a wide daily range switches to hourly', async ({ page }) => { + const initialRequest = page.waitForRequest( + (request) => new URL(request.url()).pathname === '/api/netflow/stats' + ); + await page.goto(WIDE_DAILY_RANGE); + expect(new URL((await initialRequest).url()).searchParams.get('groupBy')).toBe('date'); + await page.waitForLoadState('networkidle'); + + const drillDownRequest = page.waitForRequest((request) => { + const url = new URL(request.url()); + return url.pathname === '/api/netflow/stats' && url.searchParams.get('groupBy') === 'hour'; + }); + await clickChartCenter(page, 'dashboard'); + + await expect.poll(() => searchParam(page, 'groupBy')).toBe('hour'); + expect(searchParam(page, 'startDate')).not.toBe('2025-01-01'); + expect(searchParam(page, 'endDate')).not.toBe('2025-03-31'); + await drillDownRequest; +}); + +test('IP breakdown drill-down from a wide daily range switches to hourly', async ({ page }) => { + await page.goto(WIDE_DAILY_RANGE); + const initialRequest = page.waitForRequest( + (request) => new URL(request.url()).pathname === '/api/ip/stats' + ); + await page.locator('[data-chart-sentinel="ip"]').scrollIntoViewIfNeeded(); + expect(new URL((await initialRequest).url()).searchParams.get('granularity')).toBe('1d'); + await page.waitForLoadState('networkidle'); + + const drillDownRequest = page.waitForRequest((request) => { + const url = new URL(request.url()); + return url.pathname === '/api/ip/stats' && url.searchParams.get('granularity') === '1h'; + }); + await clickChartCenter(page, 'ip'); + + await expect.poll(() => searchParam(page, 'groupBy')).toBe('hour'); + expect(searchParam(page, 'startDate')).not.toBe('2025-01-01'); + expect(searchParam(page, 'endDate')).not.toBe('2025-03-31'); + await drillDownRequest; +}); diff --git a/apps/web/tests/e2e/playwright-fixture.js b/apps/web/tests/e2e/playwright-fixture.js index c233f7a1..c59c0ff6 100644 --- a/apps/web/tests/e2e/playwright-fixture.js +++ b/apps/web/tests/e2e/playwright-fixture.js @@ -28,11 +28,25 @@ export function seedPlaywrightDatabase() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ), ( + 'fixture-router', '1d', 1740816000, 1740902400, 4, 'all', 'all', + 10, 10, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 ); + INSERT INTO address_count_stats ( + source_id, granularity, bucket_start, bucket_end, + ip_version, src_locality, dst_locality, address_side, unique_address_count + ) VALUES + ('fixture-router', '1d', 1740816000, 1740902400, 4, 'all', 'all', 'source', 5), + ('fixture-router', '1d', 1740816000, 1740902400, 4, 'all', 'all', 'destination', 5); INSERT INTO bucket_coverage ( source_id, granularity, bucket_start, bucket_end, coverage_state, observed_units, expected_units, rejected_units - ) VALUES ('fixture-router', '5m', 1740823200, 1740823500, 'complete', 1, 1, 0); + ) VALUES + ('fixture-router', '5m', 1740823200, 1740823500, 'complete', 1, 1, 0), + ('fixture-router', '1d', 1740816000, 1740902400, 'complete', 288, 288, 0); INSERT INTO address_maad_stats ( source_id, granularity, bucket_start, bucket_end, ip_version, src_locality, dst_locality, address_side, measure, total_addrs, d0, d1, d2, tau, tau_sd, spectrum From 70bb59b588d3385599dd2954a00affbdad048494 Mon Sep 17 00:00:00 2001 From: flamboh Date: Sat, 26 Sep 2026 14:25:57 -0700 Subject: [PATCH 4/7] fix(web): build with SQLite before vite preview --- apps/web/package.json | 2 +- apps/web/playwright.config.js | 2 +- apps/web/vite.config.ts | 12 ++++++++---- docs/code/development.md | 13 ++++++++----- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 9136fdec..5742cc3d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -8,7 +8,7 @@ "build": "vite build", "deploy": "wrangler deploy", "deploy:dry-run": "wrangler deploy --dry-run", - "preview": "vite preview", + "preview": "ATLANTIS_DB_DRIVER=sqlite vite build && vite preview", "playwright": "bunx playwright test", "prepare": "svelte-kit sync || echo ''", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", diff --git a/apps/web/playwright.config.js b/apps/web/playwright.config.js index 0ec309b9..4d5445d7 100644 --- a/apps/web/playwright.config.js +++ b/apps/web/playwright.config.js @@ -40,7 +40,7 @@ export default defineConfig({ command: 'bun run build && bun run preview --host 127.0.0.1 --port 4173', env: { ...process.env, - ATLANTIS_DB_DRIVER: 'sqlite', + ATLANTIS_DB_DRIVER: 'd1', LOCAL_SQLITE_PATH: playwrightFixture?.databasePath }, port: 4173, diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 5feccdc8..1e6f21c3 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -12,8 +12,12 @@ const D1_CONDITION = 'atlantis-d1'; type DatabaseDriver = 'd1' | 'sqlite'; -function resolveDatabaseDriver(command: 'build' | 'serve', mode: string): DatabaseDriver { - if (mode === 'test') { +function resolveDatabaseDriver( + command: 'build' | 'serve', + mode: string, + isPreview: boolean +): DatabaseDriver { + if (mode === 'test' || isPreview) { return 'sqlite'; } const configured = @@ -40,8 +44,8 @@ function databaseDriver(driver: DatabaseDriver): Plugin { }; } -export default defineConfig(({ command, mode }) => { - const driver = resolveDatabaseDriver(command, mode); +export default defineConfig(({ command, mode, isPreview = false }) => { + const driver = resolveDatabaseDriver(command, mode, isPreview); return { plugins: [ diff --git a/docs/code/development.md b/docs/code/development.md index 501c8706..28541ac9 100644 --- a/docs/code/development.md +++ b/docs/code/development.md @@ -47,10 +47,10 @@ The root `dev` command starts both applications. It does not start the pipeline. The dashboard has two database drivers. Each build and each development server includes only one of them. -| `ATLANTIS_DB_DRIVER` | Driver | Adapter | Default for | -| -------------------- | --------------------------------------------------------------------------------------------- | -------------------- | ------------ | -| `sqlite` | `src/lib/server/db/sqlite.ts` reads `data//netflow.sqlite` or `LOCAL_SQLITE_PATH` | None (Node output) | `vite dev` | -| `d1` | `src/lib/server/db/d1.ts` reads the `DB` binding from `cloudflare:workers` | `adapter-cloudflare` | `vite build` | +| `ATLANTIS_DB_DRIVER` | Driver | Adapter | Default for | +| -------------------- | --------------------------------------------------------------------------------------------- | -------------------- | ----------------------------------- | +| `sqlite` | `src/lib/server/db/sqlite.ts` reads `data//netflow.sqlite` or `LOCAL_SQLITE_PATH` | None (Node output) | `vite dev`, `vite preview` (always) | +| `d1` | `src/lib/server/db/d1.ts` reads the `DB` binding from `cloudflare:workers` | `adapter-cloudflare` | `vite build` | Server code imports the driver as `#db`. The `imports` field in `apps/web/package.json` maps `#db` to the D1 driver under the `atlantis-d1` export condition, and to the SQLite driver otherwise. `apps/web/vite.config.ts` adds that condition to the server environments when the driver is `d1`. Both drivers implement `DatabaseDriver` in `src/lib/server/db/driver.ts`. @@ -61,8 +61,11 @@ bun run dev:web # SQLite, no Workers runtime ATLANTIS_DB_DRIVER=d1 bun run dev:web # local D1 through the Workers runtime bun run build:web # Cloudflare worker with D1 ATLANTIS_DB_DRIVER=sqlite bun run build:web # Node output with SQLite +bun run --cwd apps/web preview # SQLite build, then vite preview ``` +`preview` always rebuilds with SQLite before it serves, because `vite preview` runs the server in Node and cannot load the D1 worker. Serve a D1 build with `bunx wrangler dev` instead, as described in [Operations](../user/operations.md). + ## Configure the dashboard SvelteKit options are in the `sveltekit()` call in `apps/web/vite.config.ts`. The project has no `svelte.config.js`. @@ -104,7 +107,7 @@ Run the Playwright suite when a browser flow changes: bun run test:e2e ``` -The suite builds a SQLite bundle, serves it with `vite preview`, and runs against a seeded fixture database. +The suite first builds the default D1 worker, then runs `preview`, which rebuilds with SQLite and serves it against a seeded fixture database. This checks that `preview` works after a deploy build. Always use `bun run test`. Do not use `bun test` in this repository. From f49f3840b3b465a041fa699fd0277233b1d59d79 Mon Sep 17 00:00:00 2001 From: flamboh Date: Mon, 28 Sep 2026 02:33:59 -0700 Subject: [PATCH 5/7] test(web): check SQLite and D1 driver parity for MAAD routes --- apps/web/tests/lib/server/db/drivers.test.ts | 152 +++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 apps/web/tests/lib/server/db/drivers.test.ts diff --git a/apps/web/tests/lib/server/db/drivers.test.ts b/apps/web/tests/lib/server/db/drivers.test.ts new file mode 100644 index 00000000..ee94f887 --- /dev/null +++ b/apps/web/tests/lib/server/db/drivers.test.ts @@ -0,0 +1,152 @@ +import Database from 'better-sqlite3'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { + cleanupPlaywrightDatabase, + seedPlaywrightDatabase +} from '../../../e2e/playwright-fixture.js'; + +type RouteModule = { GET: (event: never) => unknown }; + +const fixtures: string[] = []; + +function seedDatabase(): string { + const { databasePath, fixtureDirectory } = seedPlaywrightDatabase(); + fixtures.push(fixtureDirectory); + return databasePath; +} + +function createD1Binding(databasePath: string) { + const sqlite = new Database(databasePath, { readonly: true }); + const toD1Value = (value: unknown) => (Buffer.isBuffer(value) ? [...value] : value); + return { + prepare(sql: string) { + return { + bind(...params: unknown[]) { + return { + async all() { + const rows = sqlite.prepare(sql).all(...params) as Record[]; + return { + results: rows.map((row) => + Object.fromEntries( + Object.entries(row).map(([key, value]) => [key, toD1Value(value)]) + ) + ) + }; + } + }; + } + }; + }, + close: () => sqlite.close() + }; +} + +async function loadDriver(driver: 'sqlite' | 'd1', databasePath: string) { + vi.resetModules(); + if (driver === 'd1') { + const binding = createD1Binding(databasePath); + vi.doMock('cloudflare:workers', () => ({ env: { DB: binding } })); + vi.doMock('#db', () => import('../../../../src/lib/server/db/d1.ts')); + return binding; + } + vi.stubEnv('LOCAL_SQLITE_PATH', databasePath); + vi.doMock('#db', () => import('../../../../src/lib/server/db/sqlite.ts')); + return null; +} + +async function callRoute( + driver: 'sqlite' | 'd1', + databasePath: string, + route: () => Promise, + url: string, + params: Record = {} +) { + const binding = await loadDriver(driver, databasePath); + try { + const { GET } = await route(); + const response = (await GET({ url: new URL(url), params } as never)) as Response; + return { status: response.status, body: await response.json() }; + } finally { + binding?.close(); + } +} + +const WINDOW = 'startDate=1740823200&endDate=1740823500&granularity=5m&routers=fixture-router'; + +const routes: Array<{ + name: string; + route: () => Promise; + url: string; + params?: Record; +}> = [ + { + name: 'datasets', + route: () => import('../../../../src/routes/api/datasets/+server.ts'), + url: 'http://localhost/api/datasets' + }, + { + name: 'maad-status', + route: () => import('../../../../src/routes/api/netflow/maad-status/+server.ts'), + url: 'http://localhost/api/netflow/maad-status?dataset=playwright' + }, + { + name: 'dimension-stats', + route: () => import('../../../../src/routes/api/netflow/dimension-stats/+server.ts'), + url: `http://localhost/api/netflow/dimension-stats?dataset=playwright&${WINDOW}&measure=packets` + }, + { + name: 'structure-stats', + route: () => import('../../../../src/routes/api/netflow/structure-stats/+server.ts'), + url: `http://localhost/api/netflow/structure-stats?dataset=playwright&${WINDOW}` + }, + { + name: 'spectrum-stats', + route: () => import('../../../../src/routes/api/netflow/spectrum-stats/+server.ts'), + url: `http://localhost/api/netflow/spectrum-stats?dataset=playwright&${WINDOW}` + }, + { + name: 'file spectrum', + route: () => import('../../../../src/routes/api/netflow/files/[slug]/spectrum/+server.ts'), + url: 'http://localhost/api/netflow/files/202503010200/spectrum?dataset=playwright&router=fixture-router&source=true', + params: { slug: '202503010200' } + }, + { + name: 'file structure', + route: () => import('../../../../src/routes/api/netflow/files/[slug]/structure/+server.ts'), + url: 'http://localhost/api/netflow/files/202503010200/structure?dataset=playwright&router=fixture-router&source=true', + params: { slug: '202503010200' } + } +]; + +describe('database drivers', () => { + afterEach(() => { + vi.doUnmock('cloudflare:workers'); + vi.doUnmock('#db'); + vi.unstubAllEnvs(); + vi.resetModules(); + for (const fixture of fixtures.splice(0)) { + cleanupPlaywrightDatabase(fixture); + } + }); + + it('export the same driver interface', async () => { + vi.doMock('cloudflare:workers', () => ({ env: { DB: null } })); + const [{ default: sqlite }, { default: d1 }] = await Promise.all([ + import('../../../../src/lib/server/db/sqlite.ts'), + import('../../../../src/lib/server/db/d1.ts') + ]); + expect(Object.keys(d1).sort()).toEqual(Object.keys(sqlite).sort()); + }); + + it.each(routes)( + 'return identical $name responses from SQLite and D1 blob rows', + async ({ route, url, params }) => { + const databasePath = seedDatabase(); + const sqlite = await callRoute('sqlite', databasePath, route, url, params); + const d1 = await callRoute('d1', databasePath, route, url, params); + + expect(sqlite.status).toBe(200); + expect(d1).toEqual(sqlite); + } + ); +}); From 5d4d2c3b380cbcaf836cb961bd1a5ba2bcdaf3f1 Mon Sep 17 00:00:00 2001 From: flamboh Date: Mon, 28 Sep 2026 02:40:26 -0700 Subject: [PATCH 6/7] test(web): cover the MAAD dimensions drill-down in one URL update --- .../web/tests/e2e/dashboard-drilldown.spec.ts | 30 +++++++++++++++++++ apps/web/tests/e2e/playwright-fixture.js | 6 +++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/web/tests/e2e/dashboard-drilldown.spec.ts b/apps/web/tests/e2e/dashboard-drilldown.spec.ts index 892f0f88..a3fec405 100644 --- a/apps/web/tests/e2e/dashboard-drilldown.spec.ts +++ b/apps/web/tests/e2e/dashboard-drilldown.spec.ts @@ -56,3 +56,33 @@ test('IP breakdown drill-down from a wide daily range switches to hourly', async expect(searchParam(page, 'endDate')).not.toBe('2025-03-31'); await drillDownRequest; }); + +test('MAAD dimensions drill-down keeps the MAAD measure while switching to hourly', async ({ + page +}) => { + await page.goto(`${WIDE_DAILY_RANGE}&measure=packets`); + const initialRequest = page.waitForRequest( + (request) => new URL(request.url()).pathname === '/api/netflow/dimension-stats' + ); + await page.locator('[data-chart-sentinel="dimensions"]').scrollIntoViewIfNeeded(); + const initialUrl = new URL((await initialRequest).url()); + expect(initialUrl.searchParams.get('granularity')).toBe('1d'); + expect(initialUrl.searchParams.get('measure')).toBe('packets'); + await page.waitForLoadState('networkidle'); + + const drillDownRequest = page.waitForRequest((request) => { + const url = new URL(request.url()); + return ( + url.pathname === '/api/netflow/dimension-stats' && + url.searchParams.get('granularity') === '1h' && + url.searchParams.get('measure') === 'packets' + ); + }); + await clickChartCenter(page, 'dimensions'); + + await expect.poll(() => searchParam(page, 'groupBy')).toBe('hour'); + expect(searchParam(page, 'measure')).toBe('packets'); + expect(searchParam(page, 'startDate')).not.toBe('2025-01-01'); + expect(searchParam(page, 'endDate')).not.toBe('2025-03-31'); + await drillDownRequest; +}); diff --git a/apps/web/tests/e2e/playwright-fixture.js b/apps/web/tests/e2e/playwright-fixture.js index c59c0ff6..d992c15a 100644 --- a/apps/web/tests/e2e/playwright-fixture.js +++ b/apps/web/tests/e2e/playwright-fixture.js @@ -62,7 +62,11 @@ export function seedPlaywrightDatabase() { ('fixture-router', '5m', 1740823200, 1740823500, 4, 'all', 'all', 'source', 'bytes', 40, 0.92, 0.64, 0.55, ${tau}, ${tauSd}, NULL), ('fixture-router', '5m', 1740823200, 1740823500, 4, 'all', 'all', 'destination', 'bytes', - 40, 0.9, 0.61, 0.52, ${tau}, ${tauSd}, NULL); + 40, 0.9, 0.61, 0.52, ${tau}, ${tauSd}, NULL), + ('fixture-router', '1d', 1740816000, 1740902400, 4, 'all', 'all', 'source', 'packets', + 40, 0.92, 0.71, 0.63, ${tau}, ${tauSd}, NULL), + ('fixture-router', '1d', 1740816000, 1740902400, 4, 'all', 'all', 'destination', 'packets', + 40, 0.9, 0.69, 0.6, ${tau}, ${tauSd}, NULL); INSERT INTO maad_q_grid (ip_version, q_min, q_step, q_count) VALUES (4, -0.5, 0.125, 33), (6, -0.5, 0.125, 33); From b72af5856c99a520a25c9c1a604dbd39e56a4153 Mon Sep 17 00:00:00 2001 From: flamboh Date: Mon, 28 Sep 2026 14:49:16 -0700 Subject: [PATCH 7/7] test(web): hoist the cloudflare:workers mock in the driver parity test --- apps/web/tests/lib/server/db/drivers.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/web/tests/lib/server/db/drivers.test.ts b/apps/web/tests/lib/server/db/drivers.test.ts index ee94f887..c9ef9978 100644 --- a/apps/web/tests/lib/server/db/drivers.test.ts +++ b/apps/web/tests/lib/server/db/drivers.test.ts @@ -7,6 +7,10 @@ import { type RouteModule = { GET: (event: never) => unknown }; +const cloudflareEnv = vi.hoisted(() => ({ DB: null as unknown })); + +vi.mock('cloudflare:workers', () => ({ env: cloudflareEnv })); + const fixtures: string[] = []; function seedDatabase(): string { @@ -45,7 +49,7 @@ async function loadDriver(driver: 'sqlite' | 'd1', databasePath: string) { vi.resetModules(); if (driver === 'd1') { const binding = createD1Binding(databasePath); - vi.doMock('cloudflare:workers', () => ({ env: { DB: binding } })); + cloudflareEnv.DB = binding; vi.doMock('#db', () => import('../../../../src/lib/server/db/d1.ts')); return binding; } @@ -120,7 +124,7 @@ const routes: Array<{ describe('database drivers', () => { afterEach(() => { - vi.doUnmock('cloudflare:workers'); + cloudflareEnv.DB = null; vi.doUnmock('#db'); vi.unstubAllEnvs(); vi.resetModules(); @@ -130,7 +134,7 @@ describe('database drivers', () => { }); it('export the same driver interface', async () => { - vi.doMock('cloudflare:workers', () => ({ env: { DB: null } })); + vi.resetModules(); const [{ default: sqlite }, { default: d1 }] = await Promise.all([ import('../../../../src/lib/server/db/sqlite.ts'), import('../../../../src/lib/server/db/d1.ts')