From 3dbf10c5d979a16630f13a05206a77cc38ad361c Mon Sep 17 00:00:00 2001 From: eleith Date: Fri, 30 Oct 2026 18:10:37 -0700 Subject: [PATCH 1/4] [web] prepare shared toolchain for SvelteKit 3 --- .changeset/kit3-toolchain-preparation.md | 11 + apps/cli/package.json | 2 +- apps/cli/tsconfig.build.json | 3 +- apps/cli/tsconfig.json | 1 + apps/cli/vite.config.ts | 2 +- apps/core/package.json | 2 +- apps/core/tsconfig.json | 1 + apps/core/vite.config.ts | 1 + apps/lsp/tsconfig.build.json | 1 + apps/lsp/tsconfig.json | 3 +- apps/lsp/vite.config.ts | 1 + apps/web/.gitignore | 2 +- apps/web/eslint.config.js | 3 + apps/web/package.json | 12 +- .../src/lib/pieces/components/icon.test.ts | 7 +- .../components/opengraph.catalog.test.ts | 5 +- .../pieces/components/page.catalog.test.ts | 5 +- .../pieces/components/page.default.test.ts | 5 +- apps/web/tsconfig.json | 1 + apps/web/vitest.config.ts | 3 +- packages/web/config/tsconfig.json | 2 +- packages/web/config/vitest.config.ts | 1 + packages/web/db/src/migrations.test.ts | 104 +- packages/web/db/tsconfig.json | 2 +- packages/web/db/vite.config.ts | 1 + packages/web/jobs/src/specs.ts | 58 +- packages/web/jobs/tsconfig.json | 2 +- packages/web/jobs/vite.config.ts | 1 + packages/web/lsp/vite.config.js | 2 + packages/web/pieces/tsconfig.json | 2 +- packages/web/pieces/vite.config.ts | 1 + packages/web/templates/package.json | 4 +- packages/web/theme/tsconfig.json | 2 +- packages/web/theme/vite.config.ts | 1 + packages/web/worker/package.json | 2 +- .../worker/src/steps/assets-generate.test.ts | 61 +- .../web/worker/src/steps/luzzle-audit.test.ts | 27 +- .../web/worker/src/steps/luzzle-sync.test.ts | 102 +- packages/web/worker/tsconfig.json | 2 +- packages/web/worker/vite.config.ts | 1 + pnpm-lock.yaml | 2278 ++++++++--------- pnpm-workspace.yaml | 11 +- 42 files changed, 1302 insertions(+), 1436 deletions(-) create mode 100644 .changeset/kit3-toolchain-preparation.md diff --git a/.changeset/kit3-toolchain-preparation.md b/.changeset/kit3-toolchain-preparation.md new file mode 100644 index 00000000..f36256b3 --- /dev/null +++ b/.changeset/kit3-toolchain-preparation.md @@ -0,0 +1,11 @@ +--- +"@luzzle/web": patch +"@luzzle/web.worker": patch +"@luzzle/web.templates": patch +"@luzzle/web.jobs": patch +--- + +Prepare the shared build and test toolchain for SvelteKit 3 while retaining Kit 2 +and the current application behavior. Align the web, worker and template Svelte +compiler/runtime at 5.57.3, preserving native runtime OG compilation and rendering. +Keep workflow-spec declarations portable with the updated TypeScript compiler. diff --git a/apps/cli/package.json b/apps/cli/package.json index 0718fafe..5157b6ea 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -34,7 +34,7 @@ "@types/node": "catalog:", "@types/yargs": "17.0.33", "@vitest/coverage-v8": "catalog:", - "@vitest/ui": "3.2.4", + "@vitest/ui": "catalog:", "eslint": "catalog:", "eslint-config-prettier": "catalog:", "eslint-plugin-prettier": "5.2.1", diff --git a/apps/cli/tsconfig.build.json b/apps/cli/tsconfig.build.json index 2a72bb9e..0095a880 100644 --- a/apps/cli/tsconfig.build.json +++ b/apps/cli/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "exclude": [ - "**/*.test.ts" + "**/*.test.ts", + "**/*.mock.ts" ] } diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json index 110995ce..f411c64e 100644 --- a/apps/cli/tsconfig.json +++ b/apps/cli/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, + "types": ["node"], "target": "ES2022", "lib": ["dom", "dom.iterable", "ES2022"], "allowJs": true, diff --git a/apps/cli/vite.config.ts b/apps/cli/vite.config.ts index 812e145b..1f572c38 100644 --- a/apps/cli/vite.config.ts +++ b/apps/cli/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['generated/**', 'dist/**', 'build/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], thresholds: { lines: 92, branches: 92, @@ -23,7 +24,6 @@ export default defineConfig({ '**/*.d.ts', '**/virtual:*', '**/__x00__*', - '**/\x00*', 'cypress/**', 'test?(s)/**', 'test?(-*).?(c|m)[jt]s?(x)', diff --git a/apps/core/package.json b/apps/core/package.json index 1d8a4c4f..cc64bc1c 100644 --- a/apps/core/package.json +++ b/apps/core/package.json @@ -37,7 +37,7 @@ "@types/mdast": "4.0.4", "@types/node": "catalog:", "@vitest/coverage-v8": "catalog:", - "@vitest/ui": "3.2.4", + "@vitest/ui": "catalog:", "eslint": "catalog:", "eslint-config-prettier": "catalog:", "globals": "catalog:", diff --git a/apps/core/tsconfig.json b/apps/core/tsconfig.json index b3835a43..0953f207 100644 --- a/apps/core/tsconfig.json +++ b/apps/core/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/apps/core/vite.config.ts b/apps/core/vite.config.ts index db8f3ef3..853728c5 100644 --- a/apps/core/vite.config.ts +++ b/apps/core/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['generated/**', 'dist/**', 'build/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], thresholds: { lines: 92, branches: 92, diff --git a/apps/lsp/tsconfig.build.json b/apps/lsp/tsconfig.build.json index 2a72bb9e..759da7f2 100644 --- a/apps/lsp/tsconfig.build.json +++ b/apps/lsp/tsconfig.build.json @@ -1,5 +1,6 @@ { "extends": "./tsconfig.json", + "compilerOptions": { "tsBuildInfoFile": "./dist/tsconfig.build.tsbuildinfo" }, "exclude": [ "**/*.test.ts" ] diff --git a/apps/lsp/tsconfig.json b/apps/lsp/tsconfig.json index 293e7229..633f2fb1 100644 --- a/apps/lsp/tsconfig.json +++ b/apps/lsp/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", + "rootDir": "./src", "target": "ES2022", "lib": ["ES2022"], "skipLibCheck": true, diff --git a/apps/lsp/vite.config.ts b/apps/lsp/vite.config.ts index f4f5c71d..cda1ac30 100644 --- a/apps/lsp/vite.config.ts +++ b/apps/lsp/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], thresholds: { lines: 80, branches: 80, diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 6be210bb..321cd827 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -5,7 +5,7 @@ node_modules # Output .output .vercel -/.svelte-kit +.svelte-kit/ /build /package-lock.json /src/lib/ui/styles/theme.build.css diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index f88f8791..1d31c9f9 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -19,6 +19,9 @@ export default [ } }, rules: { + // Adopt these newly recommended rules in the final lint-cleanup commit. + 'svelte/no-navigation-without-resolve': 'off', + 'svelte/prefer-svelte-reactivity': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { diff --git a/apps/web/package.json b/apps/web/package.json index 4fbfabf1..9b413b38 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -46,9 +46,9 @@ "@luzzle/web.config": "workspace:*", "@luzzle/web.jobs": "workspace:*", "@sveltejs/adapter-node": "5.3.2", - "@sveltejs/enhanced-img": "0.8.1", - "@sveltejs/kit": "2.43.0", - "@sveltejs/vite-plugin-svelte": "6.2.0", + "@sveltejs/enhanced-img": "1.0.0", + "@sveltejs/kit": "2.70.3", + "@sveltejs/vite-plugin-svelte": "7.1.2", "@types/eslint": "catalog:", "@types/mime-types": "3.0.1", "@types/node": "catalog:", @@ -56,7 +56,7 @@ "codemirror": "6.0.2", "eslint": "catalog:", "eslint-config-prettier": "catalog:", - "eslint-plugin-svelte": "3.9.3", + "eslint-plugin-svelte": "3.18.0", "feedsmith": "2.1.0", "globals": "catalog:", "mime-types": "3.0.2", @@ -68,8 +68,8 @@ "remark-lint-no-duplicate-headings": "4.0.1", "remark-preset-lint-consistent": "6.0.1", "remark-preset-lint-recommended": "7.0.1", - "svelte": "5.55.9", - "svelte-check": "4.3.2", + "svelte": "5.57.3", + "svelte-check": "4.7.7", "typescript": "catalog:", "typescript-eslint": "catalog:", "unplugin-icons": "22.3.0", diff --git a/apps/web/src/lib/pieces/components/icon.test.ts b/apps/web/src/lib/pieces/components/icon.test.ts index 0bc6f37f..953643be 100644 --- a/apps/web/src/lib/pieces/components/icon.test.ts +++ b/apps/web/src/lib/pieces/components/icon.test.ts @@ -29,8 +29,11 @@ let page: { data: PageData } beforeAll(async () => { server = await createServer({ configFile: false, + cacheDir: fileURLToPath( + new URL('../../../../node_modules/.cache/catalog-icon', import.meta.url) + ), root: fileURLToPath(new URL('../../../../', import.meta.url)), - server: { middlewareMode: true }, + server: { middlewareMode: true, hmr: false }, optimizeDeps: { noDiscovery: true }, plugins: [ { @@ -43,7 +46,7 @@ beforeAll(async () => { if (id === '\0icon-test-page') return 'export const page = { data: {} }' } }, - svelte({ configFile: false, hot: false }) + svelte({ configFile: false }) ], resolve: { alias: { $lib: lib } } }) diff --git a/apps/web/src/lib/pieces/components/opengraph.catalog.test.ts b/apps/web/src/lib/pieces/components/opengraph.catalog.test.ts index d2e72912..4300f606 100644 --- a/apps/web/src/lib/pieces/components/opengraph.catalog.test.ts +++ b/apps/web/src/lib/pieces/components/opengraph.catalog.test.ts @@ -25,6 +25,9 @@ let page: { beforeAll(async () => { server = await createServer({ configFile: false, + cacheDir: fileURLToPath( + new URL('../../../../node_modules/.cache/catalog-opengraph', import.meta.url) + ), root: fileURLToPath(new URL('../../../../', import.meta.url)), server: { middlewareMode: true, hmr: false }, optimizeDeps: { noDiscovery: true }, @@ -39,7 +42,7 @@ beforeAll(async () => { if (id === '\0og-test-page') return 'export const page = { data: {} }' } }, - svelte({ configFile: false, hot: false }) + svelte({ configFile: false }) ], resolve: { alias: { $lib: fileURLToPath(new URL('../../', import.meta.url)) } } }) diff --git a/apps/web/src/lib/pieces/components/page.catalog.test.ts b/apps/web/src/lib/pieces/components/page.catalog.test.ts index 280d654e..b738bac7 100644 --- a/apps/web/src/lib/pieces/components/page.catalog.test.ts +++ b/apps/web/src/lib/pieces/components/page.catalog.test.ts @@ -26,10 +26,13 @@ let getPage: (name?: string) => Component beforeAll(async () => { server = await createServer({ configFile: false, + cacheDir: fileURLToPath( + new URL('../../../../node_modules/.cache/catalog-pages', import.meta.url) + ), root: fileURLToPath(new URL('../../../../', import.meta.url)), server: { middlewareMode: true, hmr: false }, optimizeDeps: { noDiscovery: true }, - plugins: [svelte({ configFile: false, hot: false })] + plugins: [svelte({ configFile: false })] }) getPage = (await server.ssrLoadModule('@luzzle/web.templates/pages')).getPage render = (await server.ssrLoadModule('svelte/server')).render diff --git a/apps/web/src/lib/pieces/components/page.default.test.ts b/apps/web/src/lib/pieces/components/page.default.test.ts index c66c5365..4137d1fb 100644 --- a/apps/web/src/lib/pieces/components/page.default.test.ts +++ b/apps/web/src/lib/pieces/components/page.default.test.ts @@ -19,10 +19,13 @@ let render: typeof Render beforeAll(async () => { server = await createServer({ configFile: false, + cacheDir: fileURLToPath( + new URL('../../../../node_modules/.cache/catalog-default-page', import.meta.url) + ), root: fileURLToPath(new URL('../../../../', import.meta.url)), server: { middlewareMode: true, hmr: false }, optimizeDeps: { noDiscovery: true }, - plugins: [svelte({ configFile: false, hot: false })] + plugins: [svelte({ configFile: false })] }) // Compile the actual shipped catalog and selected default, not a fixture or legacy adapter. Page = (await server.ssrLoadModule('@luzzle/web.templates/pages')).getPage('default') diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 3273789a..fb52011a 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -12,6 +12,7 @@ "strict": true, "moduleResolution": "bundler", "types": [ + "node", "unplugin-icons/types/svelte" ] }, diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index 6cac2936..a9111ed5 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -4,7 +4,8 @@ import Icons from 'unplugin-icons/vite' import { fileURLToPath } from 'node:url' export default defineConfig({ - plugins: [svelte({ configFile: false, hot: false }), Icons({ compiler: 'svelte' })], + plugins: [svelte({ configFile: false }), Icons({ compiler: 'svelte' })], + server: { hmr: false }, resolve: { alias: { $lib: fileURLToPath(new URL('./src/lib', import.meta.url)) } }, test: { include: ['src/**/*.{test,spec}.{ts,js}', 'scripts/**/*.{test,spec}.{ts,js}'], diff --git a/packages/web/config/tsconfig.json b/packages/web/config/tsconfig.json index b8930a82..5a09c7e8 100644 --- a/packages/web/config/tsconfig.json +++ b/packages/web/config/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/packages/web/config/vitest.config.ts b/packages/web/config/vitest.config.ts index a53f70b4..4fc7a8c2 100644 --- a/packages/web/config/vitest.config.ts +++ b/packages/web/config/vitest.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ["dist/**", "node_modules/**"], coverage: { + include: ["src/**/*.{js,ts}"], thresholds: { lines: 80, branches: 80, diff --git a/packages/web/db/src/migrations.test.ts b/packages/web/db/src/migrations.test.ts index e5ad5ed5..ec9dde34 100644 --- a/packages/web/db/src/migrations.test.ts +++ b/packages/web/db/src/migrations.test.ts @@ -1,63 +1,81 @@ -import { describe, expect, test, vi, afterEach } from 'vitest' -import { Migrator, type Kysely } from 'kysely' -import { readdirSync } from 'node:fs' -import { runWebMigrations } from './migrations.js' -import { migrations } from './migrations/index.js' +import { describe, expect, test, vi, afterEach } from "vitest"; +import { Migrator, type Kysely } from "kysely"; +import { readdirSync } from "node:fs"; +import { runWebMigrations } from "./migrations.js"; +import { migrations } from "./migrations/index.js"; -vi.mock('kysely') +vi.mock("kysely"); const mocks = { - Migrator: vi.mocked(Migrator) -} + Migrator: vi.mocked(Migrator), +}; -describe('runWebMigrations', () => { +describe("runWebMigrations", () => { afterEach(() => { - vi.clearAllMocks() - }) + vi.clearAllMocks(); + }); - test('runs web migrations to latest with the expected table names', async () => { - const db = {} as Kysely - const migrateToLatest = vi.fn().mockResolvedValue({ results: [], error: undefined }) + test("runs web migrations to latest with the expected table names", async () => { + const db = {} as Kysely; + const migrateToLatest = vi + .fn() + .mockResolvedValue({ results: [], error: undefined }); - mocks.Migrator.mockReturnValue({ migrateToLatest } as unknown as Migrator) + mocks.Migrator.mockImplementation( + class { + migrateToLatest = migrateToLatest; + } as unknown as typeof Migrator, + ); - await runWebMigrations(db) + await runWebMigrations(db); - expect(migrateToLatest).toHaveBeenCalled() + expect(migrateToLatest).toHaveBeenCalled(); expect(mocks.Migrator).toHaveBeenCalledWith( expect.objectContaining({ db, - provider: expect.objectContaining({ getMigrations: expect.any(Function) }), - migrationTableName: 'kysely_web_migrations', - migrationLockTableName: 'kysely_web_migrations_lock' - }) - ) - }) + provider: expect.objectContaining({ + getMigrations: expect.any(Function), + }), + migrationTableName: "kysely_web_migrations", + migrationLockTableName: "kysely_web_migrations_lock", + }), + ); + }); - test('returns the MigrationResultSet from the migrator', async () => { - const db = {} as Kysely + test("returns the MigrationResultSet from the migrator", async () => { + const db = {} as Kysely; const resultSet = { - results: [{ migrationName: 'foo', direction: 'Up' as const, status: 'Success' as const }] - } - const migrateToLatest = vi.fn().mockResolvedValue(resultSet) + results: [ + { + migrationName: "foo", + direction: "Up" as const, + status: "Success" as const, + }, + ], + }; + const migrateToLatest = vi.fn().mockResolvedValue(resultSet); - mocks.Migrator.mockReturnValue({ migrateToLatest } as unknown as Migrator) + mocks.Migrator.mockImplementation( + class { + migrateToLatest = migrateToLatest; + } as unknown as typeof Migrator, + ); - const got = await runWebMigrations(db) + const got = await runWebMigrations(db); - expect(got).toBe(resultSet) - }) -}) + expect(got).toBe(resultSet); + }); +}); -describe('migrations/index.ts', () => { - test('every migration file is registered', () => { - const files = readdirSync(new URL('./migrations', import.meta.url)) - .filter((f) => f.endsWith('.ts') && f !== 'index.ts') - .map((f) => f.replace('.ts', '')) - .sort() +describe("migrations/index.ts", () => { + test("every migration file is registered", () => { + const files = readdirSync(new URL("./migrations", import.meta.url)) + .filter((f) => f.endsWith(".ts") && f !== "index.ts") + .map((f) => f.replace(".ts", "")) + .sort(); - const registered = Object.keys(migrations).sort() + const registered = Object.keys(migrations).sort(); - expect(registered).toEqual(files) - }) -}) + expect(registered).toEqual(files); + }); +}); diff --git a/packages/web/db/tsconfig.json b/packages/web/db/tsconfig.json index 13547641..31dae171 100644 --- a/packages/web/db/tsconfig.json +++ b/packages/web/db/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/packages/web/db/vite.config.ts b/packages/web/db/vite.config.ts index 3705c7e7..73af537a 100644 --- a/packages/web/db/vite.config.ts +++ b/packages/web/db/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], exclude: [ 'eslint.config.js', 'scripts/generate-test-schema.js', diff --git a/packages/web/jobs/src/specs.ts b/packages/web/jobs/src/specs.ts index 70875fcb..1dc938ec 100644 --- a/packages/web/jobs/src/specs.ts +++ b/packages/web/jobs/src/specs.ts @@ -14,40 +14,50 @@ import type { TestConnectivityResult, } from './types.js' -export const jobProgressPurgeSpec = defineWorkflowSpec< - JobProgressPurgePayload, - JobProgressPurgeResult ->({ - name: 'JobProgressPurge', - schedules: [ - { - id: 'luzzle:job-progress-purge', - cron: '48 1 * * *', - timezone: 'UTC', - input: { retentionDays: 7 }, - }, - ], -}) +type WorkflowSpec = ReturnType> -export const previewSpec = defineWorkflowSpec({ +export const jobProgressPurgeSpec: WorkflowSpec = + defineWorkflowSpec({ + name: 'JobProgressPurge', + schedules: [ + { + id: 'luzzle:job-progress-purge', + cron: '48 1 * * *', + timezone: 'UTC', + input: { retentionDays: 7 }, + }, + ], + }) + +export const previewSpec: WorkflowSpec = defineWorkflowSpec< + PreviewPayload, + PreviewResult +>({ name: 'Preview', }) -export const publishSpec = defineWorkflowSpec({ +export const publishSpec: WorkflowSpec = defineWorkflowSpec< + PublishPayload, + PublishResult +>({ name: 'Publish', }) -export const publishAuditSpec = defineWorkflowSpec({ +export const publishAuditSpec: WorkflowSpec = defineWorkflowSpec< + PublishAuditPayload, + PiecesDiff +>({ name: 'PublishAudit', }) -export const unpublishSpec = defineWorkflowSpec({ +export const unpublishSpec: WorkflowSpec = defineWorkflowSpec< + UnpublishPayload, + UnpublishResult +>({ name: 'Unpublish', }) -export const testConnectivitySpec = defineWorkflowSpec< - TestConnectivityPayload, - TestConnectivityResult ->({ - name: 'TestConnectivity', -}) +export const testConnectivitySpec: WorkflowSpec = + defineWorkflowSpec({ + name: 'TestConnectivity', + }) diff --git a/packages/web/jobs/tsconfig.json b/packages/web/jobs/tsconfig.json index 13547641..31dae171 100644 --- a/packages/web/jobs/tsconfig.json +++ b/packages/web/jobs/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/packages/web/jobs/vite.config.ts b/packages/web/jobs/vite.config.ts index bc1f7240..df56b73d 100644 --- a/packages/web/jobs/vite.config.ts +++ b/packages/web/jobs/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], exclude: [ 'eslint.config.js', 'dist/**', diff --git a/packages/web/lsp/vite.config.js b/packages/web/lsp/vite.config.js index b6f168b3..2fdae95d 100644 --- a/packages/web/lsp/vite.config.js +++ b/packages/web/lsp/vite.config.js @@ -5,6 +5,8 @@ export default defineConfig({ environment: "node", coverage: { provider: "v8", + include: ["*.js"], + exclude: ["eslint.config.js"], thresholds: { lines: 75, branches: 75, diff --git a/packages/web/pieces/tsconfig.json b/packages/web/pieces/tsconfig.json index 13547641..31dae171 100644 --- a/packages/web/pieces/tsconfig.json +++ b/packages/web/pieces/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/packages/web/pieces/vite.config.ts b/packages/web/pieces/vite.config.ts index bc1f7240..df56b73d 100644 --- a/packages/web/pieces/vite.config.ts +++ b/packages/web/pieces/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], exclude: [ 'eslint.config.js', 'dist/**', diff --git a/packages/web/templates/package.json b/packages/web/templates/package.json index c51bbcc1..d9fbf94b 100644 --- a/packages/web/templates/package.json +++ b/packages/web/templates/package.json @@ -82,7 +82,7 @@ "@sinclair/typebox": "0.34.49" }, "peerDependencies": { - "svelte": "5.55.9" + "svelte": "5.57.3" }, "devDependencies": { "@eslint/js": "catalog:", @@ -90,7 +90,7 @@ "eslint-config-prettier": "catalog:", "globals": "catalog:", "prettier": "catalog:", - "svelte": "5.55.9", + "svelte": "5.57.3", "typescript": "catalog:", "typescript-eslint": "catalog:" } diff --git a/packages/web/theme/tsconfig.json b/packages/web/theme/tsconfig.json index 13547641..31dae171 100644 --- a/packages/web/theme/tsconfig.json +++ b/packages/web/theme/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": [ diff --git a/packages/web/theme/vite.config.ts b/packages/web/theme/vite.config.ts index bc1f7240..df56b73d 100644 --- a/packages/web/theme/vite.config.ts +++ b/packages/web/theme/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], exclude: [ 'eslint.config.js', 'dist/**', diff --git a/packages/web/worker/package.json b/packages/web/worker/package.json index cc09d5c8..58ecaa8f 100644 --- a/packages/web/worker/package.json +++ b/packages/web/worker/package.json @@ -83,7 +83,7 @@ "remark-rehype": "11.1.2", "sharp": "0.33.5", "shiki": "3.22.0", - "svelte": "5.55.9", + "svelte": "5.57.3", "takumi-js": "1.5.0", "unified": "11.0.5", "unist-util-visit": "5.0.0" diff --git a/packages/web/worker/src/steps/assets-generate.test.ts b/packages/web/worker/src/steps/assets-generate.test.ts index 7883d34d..48a2bb42 100644 --- a/packages/web/worker/src/steps/assets-generate.test.ts +++ b/packages/web/worker/src/steps/assets-generate.test.ts @@ -65,8 +65,22 @@ beforeEach(async () => { mocks.runTransformsForPiece.mockResolvedValue(undefined) mocks.cleanupAllTransforms.mockResolvedValue(undefined) mocks.buildAssetMaps.mockReturnValue({ pathToKey: new Map(), keyToPath: new Map() }) - mocks.Pieces.mockReturnValue({} as unknown as Pieces) - mocks.StorageFileSystem.mockReturnValue({} as unknown as StorageFileSystem) + const mockPieces = {} as Pieces + const mockStorage = {} as StorageFileSystem + mocks.Pieces.mockImplementation( + class { + constructor() { + return mockPieces + } + } as typeof Pieces + ) + mocks.StorageFileSystem.mockImplementation( + class { + constructor() { + return mockStorage + } + } as typeof StorageFileSystem + ) }) afterEach(async () => { @@ -101,9 +115,11 @@ async function seedItem(over: Partial> = {}) { assets_json_array: JSON.stringify(['books/great/cover.png']), ...over, } - await (db.insertInto('pieces_items') as unknown as { - values: (v: typeof row) => { execute: () => Promise } - }) + await ( + db.insertInto('pieces_items') as unknown as { + values: (v: typeof row) => { execute: () => Promise } + } + ) .values(row) .execute() return row @@ -278,9 +294,9 @@ describe('assetsGenerateStep', () => { test('propagates global selection failures and still cleans up', async () => { await sql`DROP TABLE web_pieces`.execute(db) - await expect( - assetsGenerateStep.run({ filePaths: ['books/great.md'] }, ctx) - ).rejects.toThrow('no such table: web_pieces') + await expect(assetsGenerateStep.run({ filePaths: ['books/great.md'] }, ctx)).rejects.toThrow( + 'no such table: web_pieces' + ) expect(mocks.runTransformsForPiece).not.toHaveBeenCalled() expect(mocks.cleanupAllTransforms).toHaveBeenCalledOnce() @@ -288,19 +304,22 @@ describe('assetsGenerateStep', () => { expect(ctx.logger.info).not.toHaveBeenCalledWith('assets.generate complete') }) - test.each([false, true])('propagates cleanup failures (piece failed: %s)', async (pieceFailed) => { - await seedPiece() - if (pieceFailed) mocks.runTransformsForPiece.mockRejectedValueOnce(new Error('piece failed')) - const error = new Error('cleanup failed') - mocks.cleanupAllTransforms.mockRejectedValueOnce(error) + test.each([false, true])( + 'propagates cleanup failures (piece failed: %s)', + async (pieceFailed) => { + await seedPiece() + if (pieceFailed) mocks.runTransformsForPiece.mockRejectedValueOnce(new Error('piece failed')) + const error = new Error('cleanup failed') + mocks.cleanupAllTransforms.mockRejectedValueOnce(error) - await expect( - assetsGenerateStep.run({ filePaths: ['books/great.md'] }, ctx) - ).rejects.toBe(error) + await expect(assetsGenerateStep.run({ filePaths: ['books/great.md'] }, ctx)).rejects.toBe( + error + ) - expect(mocks.runTransformsForPiece).toHaveBeenCalledOnce() - expect(mocks.cleanupAllTransforms).toHaveBeenCalledOnce() - expect(ctx.logger.warn).not.toHaveBeenCalled() - expect(ctx.logger.info).not.toHaveBeenCalledWith('assets.generate complete') - }) + expect(mocks.runTransformsForPiece).toHaveBeenCalledOnce() + expect(mocks.cleanupAllTransforms).toHaveBeenCalledOnce() + expect(ctx.logger.warn).not.toHaveBeenCalled() + expect(ctx.logger.info).not.toHaveBeenCalledWith('assets.generate complete') + } + ) }) diff --git a/packages/web/worker/src/steps/luzzle-audit.test.ts b/packages/web/worker/src/steps/luzzle-audit.test.ts index 108fdac0..93f3e047 100644 --- a/packages/web/worker/src/steps/luzzle-audit.test.ts +++ b/packages/web/worker/src/steps/luzzle-audit.test.ts @@ -23,6 +23,24 @@ const mocks = { getDatabaseClient: vi.mocked(getDatabaseClient), } +function mockCoreConstructors(mockPieces: Pick) { + const mockStorage = {} as StorageFileSystem + mocks.StorageFileSystem.mockImplementation( + class { + constructor() { + return mockStorage + } + } as typeof StorageFileSystem + ) + mocks.Pieces.mockImplementation( + class { + constructor() { + return mockPieces + } + } as typeof Pieces + ) +} + function makeCtx(): WorkerContext { return { config: { @@ -58,9 +76,8 @@ describe('luzzleAuditStep', () => { pieces: { added: ['a.md'], updated: ['b.md'], pruned: ['c.md'] }, } const mockPieces = { diff: vi.fn().mockResolvedValue(diff) } - mocks.StorageFileSystem.mockReturnValue({} as never) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as never) - mocks.Pieces.mockReturnValue(mockPieces as never) const result = await luzzleAuditStep.run(undefined, ctx) @@ -79,9 +96,8 @@ describe('luzzleAuditStep', () => { schemas: { added: [], updated: [], pruned: [] }, pieces: { added: ['new.md'], updated: ['changed.md'], pruned: [] }, } - mocks.StorageFileSystem.mockReturnValue({} as never) + mockCoreConstructors({ diff: vi.fn().mockResolvedValue(sourceDiff) }) mocks.getDatabaseClient.mockReturnValue({} as never) - mocks.Pieces.mockReturnValue({ diff: vi.fn().mockResolvedValue(sourceDiff) } as never) vi.mocked(getPendingPublication).mockResolvedValue({ pieces: [], diff: { added: ['new.md'], updated: ['changed.md', 'failed.md'], pruned: ['gone.md'] }, @@ -108,9 +124,8 @@ describe('luzzleAuditStep', () => { pieces: { added: [], updated: [], pruned: [] }, } const mockPieces = { diff: vi.fn().mockResolvedValue(diff) } - mocks.StorageFileSystem.mockReturnValue({} as never) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as never) - mocks.Pieces.mockReturnValue(mockPieces as never) const result = await luzzleAuditStep.run(undefined, ctx) diff --git a/packages/web/worker/src/steps/luzzle-sync.test.ts b/packages/web/worker/src/steps/luzzle-sync.test.ts index 85aed9ba..c3c3b14e 100644 --- a/packages/web/worker/src/steps/luzzle-sync.test.ts +++ b/packages/web/worker/src/steps/luzzle-sync.test.ts @@ -33,6 +33,24 @@ type MockDb = Kysely type MockStorage = StorageType type MockPieces = PiecesType +function mockCoreConstructors(mockPieces: MockPieces) { + const mockStorage = {} as MockStorage + mocks.StorageFileSystem.mockImplementation( + class { + constructor() { + return mockStorage + } + } as typeof StorageFileSystem + ) + mocks.Pieces.mockImplementation( + class { + constructor() { + return mockPieces + } + } as typeof Pieces + ) +} + function makeCtx(): WorkerContext { return { config: { @@ -57,13 +75,15 @@ function asyncIterable(items: T[]): AsyncIterable { return readable[Symbol.asyncIterator]() } -function makePieces(opts: Partial<{ - sync: MockPieces['sync'] - prune: MockPieces['prune'] - getFilesIn: MockPieces['getFilesIn'] - getPiece: MockPieces['getPiece'] - parseFilename: MockPieces['parseFilename'] -}> = {}): MockPieces { +function makePieces( + opts: Partial<{ + sync: MockPieces['sync'] + prune: MockPieces['prune'] + getFilesIn: MockPieces['getFilesIn'] + getPiece: MockPieces['getPiece'] + parseFilename: MockPieces['parseFilename'] + }> = {} +): MockPieces { return { sync: vi.fn().mockResolvedValue(asyncIterable([])), prune: vi.fn().mockResolvedValue(asyncIterable([])), @@ -74,11 +94,13 @@ function makePieces(opts: Partial<{ } as unknown as MockPieces } -function makePiece(opts: Partial<{ - sync: Awaited>['sync'] - prune: Awaited>['prune'] - isOutdated: Awaited>['isOutdated'] -}> = {}) { +function makePiece( + opts: Partial<{ + sync: Awaited>['sync'] + prune: Awaited>['prune'] + isOutdated: Awaited>['isOutdated'] + }> = {} +) { return { sync: vi.fn().mockResolvedValue(asyncIterable([])), prune: vi.fn().mockResolvedValue(asyncIterable([])), @@ -98,9 +120,8 @@ describe('luzzleSyncStep', () => { test('runs schema sync and prune', async () => { const mockDb = {} as unknown as MockDb const mockPieces = makePieces() - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue(mockDb) - mocks.Pieces.mockReturnValue(mockPieces) await luzzleSyncStep.run(undefined, ctx) @@ -119,9 +140,8 @@ describe('luzzleSyncStep', () => { ]) ), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) const result = await luzzleSyncStep.run(undefined, ctx) @@ -136,13 +156,12 @@ describe('luzzleSyncStep', () => { test('logs schema sync errors', async () => { const mockPieces = makePieces({ - sync: vi.fn().mockResolvedValue( - asyncIterable([{ error: true, name: 'bad', message: 'parse failed' }]) - ), + sync: vi + .fn() + .mockResolvedValue(asyncIterable([{ error: true, name: 'bad', message: 'parse failed' }])), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) await luzzleSyncStep.run(undefined, ctx) @@ -152,18 +171,15 @@ describe('luzzleSyncStep', () => { test('runs item sync per type; returns added pieces in the diff', async () => { const mockDb = {} as unknown as MockDb const mockPiece = makePiece({ - sync: vi.fn().mockResolvedValue( - asyncIterable([{ action: 'added', file: 'books/book.md' }]) - ), + sync: vi.fn().mockResolvedValue(asyncIterable([{ action: 'added', file: 'books/book.md' }])), }) const mockPieces = makePieces({ getFilesIn: vi.fn().mockResolvedValue({ types: ['books'], pieces: ['books/book.md'] }), getPiece: vi.fn().mockResolvedValue(mockPiece), parseFilename: vi.fn().mockReturnValue({ type: 'books' }), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue(mockDb) - mocks.Pieces.mockReturnValue(mockPieces) const result = await luzzleSyncStep.run(undefined, ctx) @@ -186,9 +202,8 @@ describe('luzzleSyncStep', () => { getPiece: vi.fn().mockResolvedValue(mockPiece), parseFilename: vi.fn().mockReturnValue({ type: 'books' }), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) await luzzleSyncStep.run(undefined, ctx) expect(mockPiece.sync).toHaveBeenCalledWith( @@ -216,9 +231,8 @@ describe('luzzleSyncStep', () => { getPiece: vi.fn().mockResolvedValue(mockPiece), parseFilename: vi.fn().mockReturnValue({ type: 'books' }), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) const result = await luzzleSyncStep.run(undefined, ctx) if (result.status === 'completed') { @@ -236,9 +250,8 @@ describe('luzzleSyncStep', () => { ]) ), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) const result = await luzzleSyncStep.run(undefined, ctx) @@ -251,21 +264,24 @@ describe('luzzleSyncStep', () => { test('logs item sync and prune errors', async () => { const mockPiece = makePiece({ - sync: vi.fn().mockResolvedValue( - asyncIterable([{ error: true, file: 'books/bad.md', message: 'sync failed' }]) - ), - prune: vi.fn().mockResolvedValue( - asyncIterable([{ error: true, file: 'books/old.md', message: 'prune failed' }]) - ), + sync: vi + .fn() + .mockResolvedValue( + asyncIterable([{ error: true, file: 'books/bad.md', message: 'sync failed' }]) + ), + prune: vi + .fn() + .mockResolvedValue( + asyncIterable([{ error: true, file: 'books/old.md', message: 'prune failed' }]) + ), }) const mockPieces = makePieces({ getFilesIn: vi.fn().mockResolvedValue({ types: ['books'], pieces: ['books/bad.md'] }), getPiece: vi.fn().mockResolvedValue(mockPiece), parseFilename: vi.fn().mockReturnValue({ type: 'books' }), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) await luzzleSyncStep.run(undefined, ctx) @@ -282,9 +298,8 @@ describe('luzzleSyncStep', () => { getPiece: vi.fn().mockResolvedValue(mockPiece), parseFilename: vi.fn().mockReturnValue({ type: 'books' }), }) - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(mockPieces) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(mockPieces) const result = await luzzleSyncStep.run(undefined, ctx) @@ -295,9 +310,8 @@ describe('luzzleSyncStep', () => { }) test('returns completion message with changed count', async () => { - mocks.StorageFileSystem.mockReturnValue({} as unknown as MockStorage) + mockCoreConstructors(makePieces()) mocks.getDatabaseClient.mockReturnValue({} as unknown as MockDb) - mocks.Pieces.mockReturnValue(makePieces()) const result = await luzzleSyncStep.run(undefined, ctx) if (result.status === 'completed') { diff --git a/packages/web/worker/tsconfig.json b/packages/web/worker/tsconfig.json index 2363af5e..037fe21d 100644 --- a/packages/web/worker/tsconfig.json +++ b/packages/web/worker/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "verbatimModuleSyntax": true, - "baseUrl": ".", + "types": ["node"], "outDir": "dist", "target": "ES2022", "lib": ["ES2022"], diff --git a/packages/web/worker/vite.config.ts b/packages/web/worker/vite.config.ts index 8d2009e6..aab5f477 100644 --- a/packages/web/worker/vite.config.ts +++ b/packages/web/worker/vite.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { exclude: ['dist/**', 'node_modules/**'], coverage: { + include: ['src/**/*.{js,ts}'], thresholds: { lines: 80, branches: 80, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c2c1cf8..863c2237 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,8 +16,11 @@ catalogs: specifier: 24.12.4 version: 24.12.4 '@vitest/coverage-v8': - specifier: 3.2.4 - version: 3.2.4 + specifier: 4.1.7 + version: 4.1.7 + '@vitest/ui': + specifier: 4.1.7 + version: 4.1.7 eslint: specifier: 9.22.0 version: 9.22.0 @@ -31,17 +34,17 @@ catalogs: specifier: 3.4.2 version: 3.4.2 typescript: - specifier: 5.7.3 - version: 5.7.3 + specifier: 6.0.3 + version: 6.0.3 typescript-eslint: - specifier: 8.19.1 - version: 8.19.1 + specifier: 8.60.0 + version: 8.60.0 vite: - specifier: 7.1.7 - version: 7.1.7 + specifier: 8.0.14 + version: 8.0.14 vitest: - specifier: 3.2.4 - version: 3.2.4 + specifier: 4.1.7 + version: 4.1.7 importers: @@ -92,10 +95,10 @@ importers: version: 17.0.33 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': - specifier: 3.2.4 - version: 3.2.4(vitest@3.2.4) + specifier: 'catalog:' + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -116,16 +119,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) apps/core: dependencies: @@ -189,10 +192,10 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': - specifier: 3.2.4 - version: 3.2.4(vitest@3.2.4) + specifier: 'catalog:' + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -207,16 +210,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) apps/lsp: dependencies: @@ -235,7 +238,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -250,16 +253,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) apps/web: dependencies: @@ -283,7 +286,7 @@ importers: version: 6.4.0(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0) better-auth: specifier: 1.7.14 - version: 1.7.14(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vitest@3.2.4) + version: 1.7.14(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7) eventsource-parser: specifier: 4.1.1 version: 4.1.1 @@ -353,16 +356,16 @@ importers: version: link:../../packages/web/jobs '@sveltejs/adapter-node': specifier: 5.3.2 - version: 5.3.2(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))) + version: 5.3.2(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))) '@sveltejs/enhanced-img': - specifier: 0.8.1 - version: 0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(rollup@4.60.4)(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + specifier: 1.0.0 + version: 1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(rollup@4.60.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@sveltejs/kit': - specifier: 2.43.0 - version: 2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + specifier: 2.70.3 + version: 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@sveltejs/vite-plugin-svelte': - specifier: 6.2.0 - version: 6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + specifier: 7.1.2 + version: 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@types/eslint': specifier: 'catalog:' version: 9.6.1 @@ -374,7 +377,7 @@ importers: version: 24.12.4 bits-ui: specifier: 2.19.2 - version: 2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + version: 2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) codemirror: specifier: 6.0.2 version: 6.0.2 @@ -385,8 +388,8 @@ importers: specifier: 'catalog:' version: 9.1.0(eslint@9.22.0) eslint-plugin-svelte: - specifier: 3.9.3 - version: 3.9.3(eslint@9.22.0)(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + specifier: 3.18.0 + version: 3.18.0(eslint@9.22.0)(svelte@5.57.3(@typescript-eslint/types@8.60.0)) feedsmith: specifier: 2.1.0 version: 2.1.0 @@ -401,7 +404,7 @@ importers: version: 3.4.2 prettier-plugin-svelte: specifier: 3.3.2 - version: 3.3.2(prettier@3.4.2)(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + version: 3.3.2(prettier@3.4.2)(svelte@5.57.3(@typescript-eslint/types@8.60.0)) remark: specifier: 15.0.1 version: 15.0.1 @@ -421,26 +424,26 @@ importers: specifier: 7.0.1 version: 7.0.1 svelte: - specifier: 5.55.9 - version: 5.55.9(@typescript-eslint/types@8.19.1) + specifier: 5.57.3 + version: 5.57.3(@typescript-eslint/types@8.60.0) svelte-check: - specifier: 4.3.2 - version: 4.3.2(picomatch@4.0.4)(svelte@5.55.9(@typescript-eslint/types@8.19.1))(typescript@5.7.3) + specifier: 4.7.7 + version: 4.7.7(picomatch@4.0.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3) typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) unplugin-icons: specifier: 22.3.0 - version: 22.3.0(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + version: 22.3.0(svelte@5.57.3(@typescript-eslint/types@8.60.0)) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) yaml: specifier: 2.7.0 version: 2.7.0 @@ -465,7 +468,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -483,16 +486,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) packages/web/db: dependencies: @@ -517,7 +520,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -532,16 +535,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages/web/jobs: dependencies: @@ -563,7 +566,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -578,16 +581,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages/web/lsp: dependencies: @@ -612,7 +615,7 @@ importers: version: 9.22.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -627,7 +630,7 @@ importers: version: 3.4.2 vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages/web/pieces: dependencies: @@ -649,7 +652,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -664,16 +667,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages/web/proxy: {} @@ -708,14 +711,14 @@ importers: specifier: 'catalog:' version: 3.4.2 svelte: - specifier: 5.55.9 - version: 5.55.9(@typescript-eslint/types@8.19.1) + specifier: 5.57.3 + version: 5.57.3(@typescript-eslint/types@8.60.0) typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) packages/web/theme: dependencies: @@ -737,7 +740,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -752,16 +755,16 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vite: specifier: 'catalog:' - version: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + version: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages/web/worker: dependencies: @@ -898,8 +901,8 @@ importers: specifier: 3.22.0 version: 3.22.0 svelte: - specifier: 5.55.9 - version: 5.55.9(@typescript-eslint/types@8.19.1) + specifier: 5.57.3 + version: 5.57.3(@typescript-eslint/types@8.60.0) takumi-js: specifier: 1.5.0 version: 1.5.0 @@ -927,7 +930,7 @@ importers: version: 24.12.4 '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.4(vitest@3.2.4) + version: 4.1.7(vitest@4.1.7) eslint: specifier: 'catalog:' version: 9.22.0 @@ -942,38 +945,34 @@ importers: version: 3.4.2 typescript: specifier: 'catalog:' - version: 5.7.3 + version: 6.0.3 typescript-eslint: specifier: 'catalog:' - version: 8.19.1(eslint@9.22.0)(typescript@5.7.3) + version: 8.60.0(eslint@9.22.0)(typescript@6.0.3) vitest: specifier: 'catalog:' - version: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0) + version: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.11': + resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.3': - resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + '@babel/parser@7.29.12': + resolution: {integrity: sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.11': + resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': @@ -1221,164 +1220,17 @@ packages: '@codemirror/view@6.43.0': resolution: {integrity: sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} @@ -1479,9 +1331,9 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] @@ -1491,19 +1343,24 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + '@img/sharp-libvips-darwin-arm64@1.0.4': resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} cpu: [arm64] os: [darwin] @@ -1512,8 +1369,8 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} cpu: [x64] os: [darwin] @@ -1523,8 +1380,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} cpu: [arm64] os: [linux] libc: [glibc] @@ -1535,20 +1392,20 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -1559,8 +1416,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} cpu: [s390x] os: [linux] libc: [glibc] @@ -1571,8 +1428,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} cpu: [x64] os: [linux] libc: [glibc] @@ -1583,8 +1440,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} cpu: [arm64] os: [linux] libc: [musl] @@ -1595,8 +1452,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} cpu: [x64] os: [linux] libc: [musl] @@ -1608,9 +1465,9 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] @@ -1622,23 +1479,23 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] @@ -1650,9 +1507,9 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] @@ -1664,9 +1521,9 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] @@ -1678,9 +1535,9 @@ packages: os: [linux] libc: [musl] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] @@ -1692,9 +1549,9 @@ packages: os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] @@ -1704,14 +1561,18 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] @@ -1721,9 +1582,9 @@ packages: cpu: [ia32] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} cpu: [ia32] os: [win32] @@ -1733,23 +1594,15 @@ packages: cpu: [x64] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] '@internationalized/date@3.12.1': resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} - engines: {node: '>=8'} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1763,6 +1616,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -1835,6 +1691,13 @@ packages: '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@napi-rs/wasm-runtime@1.2.6': + resolution: {integrity: sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || 2.0.0-beta.2 + '@emnapi/runtime': ^1.7.1 || 2.0.0-beta.2 + '@noble/ciphers@2.4.0': resolution: {integrity: sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==} engines: {node: '>= 20.19.0'} @@ -1850,25 +1713,12 @@ packages: '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@pkgr/core@0.1.2': resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -1919,6 +1769,104 @@ packages: '@codemirror/state': 6.x.x '@codemirror/view': 6.x.x + '@rolldown/binding-android-arm64@1.0.2': + resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.2': + resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.2': + resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.2': + resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.2': + resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.2': + resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.2': + resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/plugin-commonjs@28.0.9': resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -2140,45 +2088,50 @@ packages: peerDependencies: acorn: ^8.9.0 + '@sveltejs/acorn-typescript@1.0.13': + resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} + peerDependencies: + acorn: ^8.9.0 + '@sveltejs/adapter-node@5.3.2': resolution: {integrity: sha512-nBJSipMb1KLjnAM7uzb+YpnA1VWKb+WdR+0mXEnXI6K1A3XYWbjkcjnW20ubg07sicK8XaGY/FAX3PItw39qBQ==} peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/enhanced-img@0.8.1': - resolution: {integrity: sha512-Ibom8j6F9vdmOeR+ljQ4q7TEJV0FWN1kB5wJZ2S/GuDVgCrKYrsXBw5gpSKcHwGYpKA3o9EoijPhep/GHgRUWQ==} + '@sveltejs/enhanced-img@1.0.0': + resolution: {integrity: sha512-MVY2SNj1NE/DW7yrAePFfOeEnj/PfkHPQvWfeL23h/a0i7r1qEF5qj3iIyxqCF5Vu+akskQ/h5eEEM7hjvFmrQ==} + engines: {node: '>=22'} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^6.0.0 + '@sveltejs/vite-plugin-svelte': ^7.0.0 svelte: ^5.0.0 - vite: ^6.3.0 || >=7.0.0 + vite: '>=8.0.12' - '@sveltejs/kit@2.43.0': - resolution: {integrity: sha512-lyO/RlyFhdYy2ezSgwUbLYzTfiQGIqOm1/jZeyYYc7sP4Qr2AOOHeLdHuGRAJ1QkvA7Vj0lkJnlAQB6KWlmXEA==} + '@sveltejs/kit@2.70.3': + resolution: {integrity: sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==} engines: {node: '>=18.13'} hasBin: true 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 + '@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 - vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + typescript: ^5.3.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 peerDependenciesMeta: '@opentelemetry/api': optional: true + typescript: + optional: true - '@sveltejs/vite-plugin-svelte-inspector@5.0.2': - resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==} + '@sveltejs/load-config@0.2.3': + resolution: {integrity: sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==} + engines: {node: '>= 18.0.0'} + + '@sveltejs/vite-plugin-svelte@7.1.2': + resolution: {integrity: sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 - svelte: ^5.0.0 - vite: ^6.3.0 || ^7.0.0 - - '@sveltejs/vite-plugin-svelte@6.2.0': - resolution: {integrity: sha512-nJsV36+o7rZUDlrnSduMNl11+RoDE1cKqOI0yUEBCcqFoAZOk47TwD3dPKS2WmRutke9StXnzsPBslY7prDM9w==} - engines: {node: ^20.19 || ^22.12 || >=24} - peerDependencies: - svelte: ^5.0.0 - vite: ^6.3.0 || ^7.0.0 + svelte: ^5.46.4 + vite: ^8.0.0-beta.7 || ^8.0.0 '@swc/helpers@0.5.21': resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} @@ -2260,6 +2213,9 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + '@types/async@3.2.24': resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} @@ -2326,9 +2282,6 @@ packages: '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -2341,98 +2294,110 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.19.1': - resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} + '@typescript-eslint/eslint-plugin@8.60.0': + resolution: {integrity: sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + '@typescript-eslint/parser': ^8.60.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.19.1': - resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} + '@typescript-eslint/parser@8.60.0': + resolution: {integrity: sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.19.1': - resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.19.1': - resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} + '@typescript-eslint/project-service@8.60.0': + resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.19.1': - resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} + '@typescript-eslint/scope-manager@8.60.0': + resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.19.1': - resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} + '@typescript-eslint/tsconfig-utils@8.60.0': + resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.19.1': - resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} + '@typescript-eslint/type-utils@8.60.0': + resolution: {integrity: sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.19.1': - resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} + '@typescript-eslint/types@8.60.0': + resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.60.0': + resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.60.0': + resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.60.0': + resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - '@vitest/coverage-v8@3.2.4': - resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} + '@vitest/coverage-v8@4.1.7': + resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==} peerDependencies: - '@vitest/browser': 3.2.4 - vitest: 3.2.4 + '@vitest/browser': 4.1.7 + vitest: 4.1.7 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/ui@3.2.4': - resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} + '@vitest/ui@4.1.7': + resolution: {integrity: sha512-TP6utB2yX6rsJNVRo2qAlsi48i1YwFTrLV2tnTtWqJaYX7m4lRCCLirZBjU6xC5m0RsPHr+L2+N+eIPhgEzFfw==} peerDependencies: - vitest: 3.2.4 + vitest: 4.1.7 - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} @@ -2447,6 +2412,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -2490,10 +2460,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2505,8 +2471,8 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-v8-to-istanbul@0.3.12: - resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} + ast-v8-to-istanbul@1.0.7: + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} @@ -2612,17 +2578,10 @@ packages: brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} - brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -2630,10 +2589,6 @@ packages: resolution: {integrity: sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==} engines: {node: '>=20'} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - cac@7.0.0: resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} engines: {node: '>=20.19.0'} @@ -2653,8 +2608,8 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} engines: {node: '>=18'} chalk@4.1.2: @@ -2673,10 +2628,6 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -2743,6 +2694,9 @@ packages: confbox@0.2.4: resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} @@ -2790,10 +2744,6 @@ packages: resolution: {integrity: sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==} engines: {node: '>=20'} - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2815,6 +2765,9 @@ packages: devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + devalue@5.9.4: + resolution: {integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -2822,18 +2775,12 @@ packages: resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} engines: {node: '>=18'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -2853,13 +2800,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} @@ -2893,11 +2835,11 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-svelte@3.9.3: - resolution: {integrity: sha512-PlcyK80sqAZ43IITeZkgl3zPFWJytx/Joup9iKGqIOsXM2m3pWfPbWuXPr5PN3loXFEypqTY/JyZwNqlSpSvRw==} + eslint-plugin-svelte@3.18.0: + resolution: {integrity: sha512-vc3P37lrDronWDb2kPXiG8sqkuiMqitGXSSaflb7Y+jpDgNoAzW8i7tdqyJKpcLZmFIqZCD+je2oZRf9qyRyBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.1 || ^9.0.0 + eslint: ^8.57.1 || ^9.0.0 || ^10.0.0 svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: @@ -2915,6 +2857,10 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@9.22.0: resolution: {integrity: sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2937,8 +2883,8 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} - esrap@2.2.9: - resolution: {integrity: sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A==} + esrap@2.4.0: + resolution: {integrity: sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA==} peerDependencies: '@typescript-eslint/types': ^8.2.0 peerDependenciesMeta: @@ -2986,10 +2932,6 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3022,9 +2964,6 @@ packages: resolution: {integrity: sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==} hasBin: true - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -3055,10 +2994,6 @@ packages: resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} engines: {node: '>=20'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3070,10 +3005,6 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data-encoder@4.1.0: resolution: {integrity: sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==} engines: {node: '>= 18'} @@ -3120,19 +3051,10 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3153,9 +3075,6 @@ packages: resolution: {integrity: sha512-Su87c0NNeg97de1sO02gy9I8EmE7DCJ1gzcFLcgGpYeq2PnLg4xz73MWrp6HjqbSsjb6Glf4UBDW6JNyZA6uSg==} engines: {node: '>=20'} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3228,9 +3147,13 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - imagetools-core@8.0.0: - resolution: {integrity: sha512-5i4Cx5vrBpVdvT3gvkSGAzzkUCrg/5Jm54UwWbDUSTMp4AjDI4IxiC6dI4+X1PRJYi6eKqWuE+684NJY2iOn3w==} - engines: {node: '>=18.0.0'} + ignore@7.0.14: + resolution: {integrity: sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==} + engines: {node: '>= 4'} + + imagetools-core@12.0.0: + resolution: {integrity: sha512-9u1nEZrA6qd/dv1okXT/i1bLOnL9YAAg7k5BqnBJe5PxYYRecZ/GNyGxUXG5x6ybyvCa25zlx5XX++nSEAkjQw==} + engines: {node: '>=22.0.0'} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -3280,10 +3203,6 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -3309,17 +3228,10 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - istanbul-reports@3.2.0: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} @@ -3333,9 +3245,6 @@ packages: js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -3399,6 +3308,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -3426,16 +3409,10 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -3447,8 +3424,11 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magic-string@1.4.5: + resolution: {integrity: sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==} + + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} @@ -3523,10 +3503,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -3614,10 +3590,6 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} @@ -3641,17 +3613,9 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -3739,9 +3703,6 @@ packages: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} @@ -3776,24 +3737,12 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -3898,9 +3847,6 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -4091,9 +4037,10 @@ packages: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rolldown@1.0.2: + resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true rollup@4.60.4: resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} @@ -4103,9 +4050,6 @@ packages: rou3@0.9.2: resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - runed@0.35.1: resolution: {integrity: sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==} peerDependencies: @@ -4134,8 +4078,10 @@ packages: engines: {node: '>=10'} hasBin: true - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true set-cookie-parser@3.2.1: resolution: {integrity: sha512-Npd1Pt+4zb3BrSZDPd2qFwRpS6b1EUEasRrO8xS0zMJi0fApXXK5YuzP0minyJnxwnFHoVoJwA55q0A0cu63gQ==} @@ -4144,9 +4090,14 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -4166,10 +4117,6 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - simple-swizzle@0.2.4: resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} @@ -4197,17 +4144,13 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string-width@8.1.0: resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} engines: {node: '>=20'} @@ -4227,9 +4170,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - strnum@2.3.0: resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} @@ -4257,13 +4197,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.3.2: - resolution: {integrity: sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w==} + svelte-check@4.7.7: + resolution: {integrity: sha512-Ge2IMdeTpbJuzNFGRiiMg0RWgbScysKQu7xwcyFJ22+DU9F5nPX51+Q3gHmGqRxYQMx2knreVWt5oCtfe0vf2g==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 - typescript: '>=5.0.0' + typescript: ^5.0.0 || ^6.0.0 svelte-eslint-parser@1.6.1: resolution: {integrity: sha512-hhvSH6kRj46UzrBVO5TaotD+Iuvruj5ccKBcO4wAhVcPTLmIc/c32D8UllBTYO0on4LzYuM0rNzf1lM/gBlkSQ==} @@ -4274,19 +4214,14 @@ packages: svelte: optional: true - svelte-parse-markup@0.1.5: - resolution: {integrity: sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==} - peerDependencies: - svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1 - svelte-toolbelt@0.10.6: resolution: {integrity: sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==} engines: {node: '>=18', pnpm: '>=8.7.0'} peerDependencies: svelte: ^5.30.2 - svelte@5.55.9: - resolution: {integrity: sha512-fTjjT8cHLDwigcu2j3pv7Jq04LklXevPB8uBgyHNiTXv+RMNvVnrjS4UEYrLMkhuq1vpCodHjiW+z/95SDs/fg==} + svelte@5.57.3: + resolution: {integrity: sha512-5mAftM3SoDV9X0zdF386F9p2pv8iKOwi/KfBS1XnBAkVCKFzVOLHUoa8iGWAt4buoicLmyWVRpuuD5wplXOQsg==} engines: {node: '>=18'} synckit@0.9.3: @@ -4299,19 +4234,12 @@ packages: takumi-js@1.5.0: resolution: {integrity: sha512-EYBpBWZps7MNsM4nn3hAhRQqKB9lNSM2jLJA2p6asjWi6tF5ZW9o5IePvg2Y6sLpyPPq0VvZ4C8pw5gBSGAUdQ==} - test-exclude@7.0.2: - resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} - engines: {node: '>=18'} - thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.1.2: resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} engines: {node: '>=18'} @@ -4324,22 +4252,10 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} engines: {node: '>=14.0.0'} - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} - engines: {node: '>=14.0.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - token-types@6.1.2: resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} @@ -4371,15 +4287,15 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typescript-eslint@8.19.1: - resolution: {integrity: sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==} + typescript-eslint@8.60.0: + resolution: {integrity: sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -4459,24 +4375,22 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-imagetools@8.0.0: - resolution: {integrity: sha512-3bkkA0vQ57tMynsetY2j4QhCnZKrxFv0RScaZipzYgkjkkUBEmZL5UIVHOUHhVMfwCetAeM9e3DNwyPK1ff4xg==} - engines: {node: '>=18.0.0'} + vite-imagetools@12.0.1: + resolution: {integrity: sha512-BZcU5TLmCKJyLo6FIk8detM9TYtFkbwOaZ8ebd+Kt7YQKHXL+xJH/Oxg4bBNx8xcP3NPVblE6DI/VCaKja5nYQ==} + engines: {node: '>=22.0.0'} + peerDependencies: + vite: '>=8.0.0' - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - - vite@7.1.7: - resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} + vite@8.0.14: + resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.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' @@ -4487,12 +4401,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -4516,26 +4432,39 @@ packages: vite: optional: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true @@ -4606,10 +4535,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -4679,28 +4604,23 @@ packages: snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.6.0 tinyexec: 1.1.2 - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.11': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/parser@7.29.3': + '@babel/parser@7.29.12': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.11 - '@babel/types@7.29.0': + '@babel/types@7.29.11': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.11 + '@babel/helper-validator-identifier': 7.29.7 '@bcoe/v8-coverage@1.0.2': {} @@ -5134,87 +5054,25 @@ snapshots: style-mod: 4.1.3 w3c-keyname: 2.2.8 + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.12': + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.25.12': - optional: true - - '@esbuild/win32-ia32@0.25.12': - optional: true - - '@esbuild/win32-x64@0.25.12': + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 optional: true '@eslint-community/eslint-utils@4.9.1(eslint@9.22.0)': @@ -5322,9 +5180,9 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true - '@img/sharp-darwin-arm64@0.34.5': + '@img/sharp-darwin-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.3.4 optional: true '@img/sharp-darwin-x64@0.33.5': @@ -5332,63 +5190,68 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true - '@img/sharp-darwin-x64@0.34.5': + '@img/sharp-darwin-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 optional: true '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + '@img/sharp-libvips-darwin-arm64@1.3.4': optional: true '@img/sharp-libvips-darwin-x64@1.0.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + '@img/sharp-libvips-darwin-x64@1.3.4': optional: true '@img/sharp-libvips-linux-arm64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + '@img/sharp-libvips-linux-arm64@1.3.4': optional: true '@img/sharp-libvips-linux-arm@1.0.5': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@img/sharp-libvips-linux-arm@1.3.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + '@img/sharp-libvips-linux-ppc64@1.3.4': optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + '@img/sharp-libvips-linux-riscv64@1.3.4': optional: true '@img/sharp-libvips-linux-s390x@1.0.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + '@img/sharp-libvips-linux-s390x@1.3.4': optional: true '@img/sharp-libvips-linux-x64@1.0.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + '@img/sharp-libvips-linux-x64@1.3.4': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': optional: true '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + '@img/sharp-libvips-linuxmusl-x64@1.3.4': optional: true '@img/sharp-linux-arm64@0.33.5': @@ -5396,9 +5259,9 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true - '@img/sharp-linux-arm64@0.34.5': + '@img/sharp-linux-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 optional: true '@img/sharp-linux-arm@0.33.5': @@ -5406,19 +5269,19 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true - '@img/sharp-linux-arm@0.34.5': + '@img/sharp-linux-arm@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.3.4 optional: true - '@img/sharp-linux-ppc64@0.34.5': + '@img/sharp-linux-ppc64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 optional: true - '@img/sharp-linux-riscv64@0.34.5': + '@img/sharp-linux-riscv64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 optional: true '@img/sharp-linux-s390x@0.33.5': @@ -5426,9 +5289,9 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true - '@img/sharp-linux-s390x@0.34.5': + '@img/sharp-linux-s390x@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 optional: true '@img/sharp-linux-x64@0.33.5': @@ -5436,9 +5299,9 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true - '@img/sharp-linux-x64@0.34.5': + '@img/sharp-linux-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.3.4 optional: true '@img/sharp-linuxmusl-arm64@0.33.5': @@ -5446,9 +5309,9 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + '@img/sharp-linuxmusl-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 optional: true '@img/sharp-linuxmusl-x64@0.33.5': @@ -5456,9 +5319,9 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + '@img/sharp-linuxmusl-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 optional: true '@img/sharp-wasm32@0.33.5': @@ -5466,41 +5329,35 @@ snapshots: '@emnapi/runtime': 1.10.0 optional: true - '@img/sharp-wasm32@0.34.5': + '@img/sharp-wasm32@0.35.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-win32-arm64@0.34.5': + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true - '@img/sharp-win32-ia32@0.34.5': + '@img/sharp-win32-ia32@0.35.5': optional: true '@img/sharp-win32-x64@0.33.5': optional: true - '@img/sharp-win32-x64@0.34.5': + '@img/sharp-win32-x64@0.35.5': optional: true '@internationalized/date@3.12.1': dependencies: '@swc/helpers': 0.5.21 - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/schema@0.1.6': {} - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5515,6 +5372,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -5632,6 +5491,13 @@ snapshots: '@marijn/find-cluster-break@1.0.2': {} + '@napi-rs/wasm-runtime@1.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.4 + optional: true + '@noble/ciphers@2.4.0': {} '@noble/hashes@1.8.0': {} @@ -5640,25 +5506,12 @@ snapshots: '@nodable/entities@2.1.0': {} - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + '@oxc-project/types@0.132.0': {} '@paralleldrive/cuid2@2.2.2': dependencies: '@noble/hashes': 1.8.0 - '@pkgjs/parseargs@0.11.0': - optional: true - '@pkgr/core@0.1.2': {} '@pnpm/deps.graph-sequencer@1100.0.1': {} @@ -5696,6 +5549,57 @@ snapshots: '@codemirror/view': 6.43.0 '@replit/codemirror-vim-core': 0.1.0 + '@rolldown/binding-android-arm64@1.0.2': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.2': + optional: true + + '@rolldown/binding-darwin-x64@1.0.2': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.2': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.2': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.2': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.2': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.2': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.2': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.2': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@rollup/plugin-commonjs@28.0.9(rollup@4.60.4)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) @@ -5859,32 +5763,36 @@ snapshots: dependencies: acorn: 8.16.0 - '@sveltejs/adapter-node@5.3.2(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))': + '@sveltejs/acorn-typescript@1.0.13(acorn@8.19.0)': + dependencies: + acorn: 8.19.0 + + '@sveltejs/adapter-node@5.3.2(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))': dependencies: '@rollup/plugin-commonjs': 28.0.9(rollup@4.60.4) '@rollup/plugin-json': 6.1.0(rollup@4.60.4) '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.4) - '@sveltejs/kit': 2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) rollup: 4.60.4 - '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(rollup@4.60.4)(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))': + '@sveltejs/enhanced-img@1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(rollup@4.60.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - magic-string: 0.30.21 - sharp: 0.34.5 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - svelte-parse-markup: 0.1.5(svelte@5.55.9(@typescript-eslint/types@8.19.1)) - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) - vite-imagetools: 8.0.0(rollup@4.60.4) + '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + magic-string: 1.4.5 + sharp: 0.35.5(@types/node@24.12.4) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) + vite-imagetools: 12.0.1(@types/node@24.12.4)(rollup@4.60.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) zimmerframe: 1.1.4 transitivePeerDependencies: + - '@types/node' - rollup - '@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))': + '@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.6.0 @@ -5893,30 +5801,23 @@ snapshots: kleur: 4.1.5 magic-string: 0.30.21 mrmime: 2.0.1 - sade: 1.8.1 - set-cookie-parser: 2.7.2 + set-cookie-parser: 3.2.1 sirv: 3.0.2 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) + optionalDependencies: + typescript: 6.0.3 - '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))': - dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - obug: 2.1.1 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + '@sveltejs/load-config@0.2.3': {} - '@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))': + '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.21 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) - vitefu: 1.1.3(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - transitivePeerDependencies: - - supports-color + obug: 2.1.1 + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) + vitefu: 1.1.3(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@swc/helpers@0.5.21': dependencies: @@ -5980,6 +5881,11 @@ snapshots: '@tokenizer/token@0.3.0': {} + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + '@types/async@3.2.24': {} '@types/async@3.2.25': {} @@ -6042,8 +5948,6 @@ snapshots: '@types/retry@0.12.0': {} - '@types/trusted-types@2.0.7': {} - '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -6054,164 +5958,172 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.22.0)(typescript@5.7.3))(eslint@9.22.0)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@9.22.0)(typescript@6.0.3))(eslint@9.22.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.19.1(eslint@9.22.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.22.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.22.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/parser': 8.60.0(eslint@9.22.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/type-utils': 8.60.0(eslint@9.22.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@9.22.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.0 eslint: 9.22.0 - graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.14 natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.7.3) - typescript: 5.7.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.19.1(eslint@9.22.0)(typescript@5.7.3)': + '@typescript-eslint/parser@8.60.0(eslint@9.22.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 eslint: 9.22.0 - typescript: 5.7.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.19.1': + '@typescript-eslint/project-service@8.60.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color - '@typescript-eslint/type-utils@8.19.1(eslint@9.22.0)(typescript@5.7.3)': + '@typescript-eslint/scope-manager@8.60.0': dependencies: - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.22.0)(typescript@5.7.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 + + '@typescript-eslint/tsconfig-utils@8.60.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/type-utils@8.60.0(eslint@9.22.0)(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@9.22.0)(typescript@6.0.3) debug: 4.4.3 eslint: 9.22.0 - ts-api-utils: 2.5.0(typescript@5.7.3) - typescript: 5.7.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.19.1': {} + '@typescript-eslint/types@8.60.0': {} - '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.60.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/project-service': 8.60.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@6.0.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.9 + minimatch: 10.2.5 semver: 7.8.1 - ts-api-utils: 2.5.0(typescript@5.7.3) - typescript: 5.7.3 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.19.1(eslint@9.22.0)(typescript@5.7.3)': + '@typescript-eslint/utils@8.60.0(eslint@9.22.0)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.22.0) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) eslint: 9.22.0 - typescript: 5.7.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.19.1': + '@typescript-eslint/visitor-keys@8.60.0': dependencies: - '@typescript-eslint/types': 8.19.1 - eslint-visitor-keys: 4.2.1 + '@typescript-eslint/types': 8.60.0 + eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.1': {} - '@vitest/coverage-v8@3.2.4(vitest@3.2.4)': + '@vitest/coverage-v8@4.1.7(vitest@4.1.7)': dependencies: - '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - ast-v8-to-istanbul: 0.3.12 - debug: 4.4.3 + '@vitest/utils': 4.1.7 + ast-v8-to-istanbul: 1.0.7 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 - magic-string: 0.30.21 - magicast: 0.3.5 - std-env: 3.10.0 - test-exclude: 7.0.2 - tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) - transitivePeerDependencies: - - supports-color + magicast: 0.5.5 + obug: 2.1.1 + std-env: 4.3.0 + tinyrainbow: 3.2.0 + vitest: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) - '@vitest/expect@3.2.4': + '@vitest/expect@4.1.7': dependencies: + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + chai: 6.3.0 + tinyrainbow: 3.2.0 - '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0))': + '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) - '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@24.12.4)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) - '@vitest/pretty-format@3.2.4': + '@vitest/pretty-format@4.1.7': dependencies: - tinyrainbow: 2.0.0 + tinyrainbow: 3.2.0 - '@vitest/runner@3.2.4': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.1.7 pathe: 2.0.3 - strip-literal: 3.1.0 - '@vitest/snapshot@3.2.4': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 3.2.4 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.4 + '@vitest/spy@4.1.7': {} - '@vitest/ui@3.2.4(vitest@3.2.4)': + '@vitest/ui@4.1.7(vitest@4.1.7)': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.1.7 fflate: 0.8.3 flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 tinyglobby: 0.2.16 - tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + tinyrainbow: 3.2.0 + vitest: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) - '@vitest/utils@3.2.4': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.1.7 + convert-source-map: 2.0.0 + tinyrainbow: 3.2.0 '@vscode/l10n@0.0.18': {} @@ -6221,6 +6133,8 @@ snapshots: acorn@8.16.0: {} + acorn@8.19.0: {} + agent-base@7.1.4: {} ajv-draft-04@1.0.0(ajv@8.17.1): @@ -6257,15 +6171,13 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.3: {} - argparse@2.0.1: {} aria-query@5.3.1: {} assertion-error@2.0.1: {} - ast-v8-to-istanbul@0.3.12: + ast-v8-to-istanbul@1.0.7: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -6288,7 +6200,7 @@ snapshots: base64-js@1.5.1: {} - better-auth@1.7.14(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vitest@3.2.4): + better-auth@1.7.14(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7): dependencies: '@better-auth/core': 1.7.14(@better-auth/utils@0.5.1)(@better-fetch/fetch@1.3.3)(better-call@1.4.4(zod@4.6.7))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.5.5) '@better-auth/drizzle-adapter': 1.7.14(@better-auth/core@1.7.14(@better-auth/utils@0.5.1)(@better-fetch/fetch@1.3.3)(better-call@1.4.4(zod@4.6.7))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.5.5))(@better-auth/utils@0.5.1) @@ -6308,9 +6220,9 @@ snapshots: nanostores: 1.5.5 zod: 4.6.7 optionalDependencies: - '@sveltejs/kit': 2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - vitest: 3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0) + '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + vitest: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) transitivePeerDependencies: - '@opentelemetry/api' @@ -6325,15 +6237,15 @@ snapshots: bignumber.js@9.3.1: {} - bits-ui@2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + bits-ui@2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: '@floating-ui/core': 1.7.5 '@floating-ui/dom': 1.7.6 '@internationalized/date': 3.12.1 esm-env: 1.2.2 - runed: 0.35.1(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)) - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - svelte-toolbelt: 0.10.6(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + runed: 0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + svelte-toolbelt: 0.10.6(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) tabbable: 6.4.0 transitivePeerDependencies: - '@sveltejs/kit' @@ -6343,24 +6255,14 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.0: - dependencies: - balanced-match: 1.0.2 - brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - buffer-equal-constant-time@1.0.1: {} byte-counter@0.1.0: {} - cac@6.7.14: {} - cac@7.0.0: {} cacheable-lookup@7.0.0: {} @@ -6379,13 +6281,7 @@ snapshots: ccount@2.0.1: {} - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + chai@6.3.0: {} chalk@4.1.2: dependencies: @@ -6400,8 +6296,6 @@ snapshots: character-reference-invalid@2.0.1: {} - check-error@2.1.3: {} - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -6472,6 +6366,8 @@ snapshots: confbox@0.2.4: {} + convert-source-map@2.0.0: {} + cookie@0.6.0: {} crelt@1.0.6: {} @@ -6508,8 +6404,6 @@ snapshots: dependencies: mimic-response: 4.0.0 - deep-eql@5.0.2: {} - deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -6522,6 +6416,8 @@ snapshots: devalue@5.8.1: {} + devalue@5.9.4: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -6530,16 +6426,12 @@ snapshots: dependencies: type-fest: 4.41.0 - eastasianwidth@0.2.0: {} - ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -6552,36 +6444,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} - - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 + es-module-lexer@2.3.2: {} escalade@3.2.0: {} @@ -6603,7 +6466,7 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 9.1.0(eslint@9.22.0) - eslint-plugin-svelte@3.9.3(eslint@9.22.0)(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + eslint-plugin-svelte@3.18.0(eslint@9.22.0)(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.22.0) '@jridgewell/sourcemap-codec': 1.5.5 @@ -6615,9 +6478,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.15) postcss-safe-parser: 7.0.1(postcss@8.5.15) semver: 7.8.1 - svelte-eslint-parser: 1.6.1(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + svelte-eslint-parser: 1.6.1(svelte@5.57.3(@typescript-eslint/types@8.60.0)) optionalDependencies: - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) transitivePeerDependencies: - ts-node @@ -6630,6 +6493,8 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} + eslint@9.22.0: dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.22.0) @@ -6682,11 +6547,11 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.9(@typescript-eslint/types@8.19.1): + esrap@2.4.0(@typescript-eslint/types@8.60.0): dependencies: '@jridgewell/sourcemap-codec': 1.5.5 optionalDependencies: - '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/types': 8.60.0 esrecurse@4.3.0: dependencies: @@ -6716,14 +6581,6 @@ snapshots: fast-diff@1.3.0: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -6757,10 +6614,6 @@ snapshots: strnum: 2.3.0 xml-naming: 0.1.0 - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fault@2.0.1: dependencies: format: 0.2.2 @@ -6794,10 +6647,6 @@ snapshots: transitivePeerDependencies: - supports-color - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -6810,11 +6659,6 @@ snapshots: flatted@3.4.2: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data-encoder@4.1.0: {} format@0.2.2: {} @@ -6857,23 +6701,10 @@ snapshots: github-slugger@2.0.0: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - globals@14.0.0: {} globals@16.0.0: {} @@ -6906,8 +6737,6 @@ snapshots: responselike: 4.0.2 type-fest: 4.41.0 - graphemer@1.4.0: {} - has-flag@4.0.0: {} hasown@2.0.3: @@ -7028,7 +6857,9 @@ snapshots: ignore@5.3.2: {} - imagetools-core@8.0.0: {} + ignore@7.0.14: {} + + imagetools-core@12.0.0: {} import-fresh@3.3.1: dependencies: @@ -7068,8 +6899,6 @@ snapshots: is-module@1.0.0: {} - is-number@7.0.0: {} - is-plain-obj@4.1.0: {} is-reference@1.2.1: @@ -7092,25 +6921,11 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jju@1.4.0: {} jose@6.2.3: {} @@ -7119,8 +6934,6 @@ snapshots: js-tokens@10.0.0: {} - js-tokens@9.0.1: {} - js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -7182,6 +6995,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + 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 + lilconfig@2.1.0: {} local-pkg@1.2.1: @@ -7204,12 +7066,8 @@ snapshots: longest-streak@3.1.0: {} - loupe@3.2.1: {} - lowercase-keys@3.0.0: {} - lru-cache@10.4.3: {} - luxon@3.7.2: {} lz-string@1.5.0: {} @@ -7218,10 +7076,14 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.3.5: + magic-string@1.4.5: dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.12 + '@babel/types': 7.29.11 source-map-js: 1.2.1 make-dir@4.0.0: @@ -7433,8 +7295,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -7638,11 +7498,6 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - mime-db@1.54.0: {} mime-types@3.0.2: @@ -7661,14 +7516,8 @@ snapshots: dependencies: brace-expansion: 1.1.14 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.0 - minimist@1.2.8: {} - minipass@7.1.3: {} - mlly@1.8.2: dependencies: acorn: 8.16.0 @@ -7742,8 +7591,6 @@ snapshots: '@types/retry': 0.12.0 retry: 0.13.1 - package-json-from-dist@1.0.1: {} - package-manager-detector@1.6.0: {} package-manager-detector@1.8.0: {} @@ -7776,19 +7623,10 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.3 - pathe@2.0.3: {} - pathval@2.0.1: {} - picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.4: {} pino-abstract-transport@2.0.0: @@ -7873,10 +7711,10 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + prettier-plugin-svelte@3.3.2(prettier@3.4.2)(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: prettier: 3.4.2 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) prettier@3.4.2: {} @@ -7909,8 +7747,6 @@ snapshots: quansync@0.2.11: {} - queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} quick-lru@5.1.1: {} @@ -8356,7 +8192,26 @@ snapshots: retry@0.13.1: {} - reusify@1.1.0: {} + rolldown@1.0.2: + dependencies: + '@oxc-project/types': 0.132.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.2 + '@rolldown/binding-darwin-arm64': 1.0.2 + '@rolldown/binding-darwin-x64': 1.0.2 + '@rolldown/binding-freebsd-x64': 1.0.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 + '@rolldown/binding-linux-arm64-gnu': 1.0.2 + '@rolldown/binding-linux-arm64-musl': 1.0.2 + '@rolldown/binding-linux-ppc64-gnu': 1.0.2 + '@rolldown/binding-linux-s390x-gnu': 1.0.2 + '@rolldown/binding-linux-x64-gnu': 1.0.2 + '@rolldown/binding-linux-x64-musl': 1.0.2 + '@rolldown/binding-openharmony-arm64': 1.0.2 + '@rolldown/binding-wasm32-wasi': 1.0.2 + '@rolldown/binding-win32-arm64-msvc': 1.0.2 + '@rolldown/binding-win32-x64-msvc': 1.0.2 rollup@4.60.4: dependencies: @@ -8391,18 +8246,14 @@ snapshots: rou3@0.9.2: {} - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - runed@0.35.1(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + runed@0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: dequal: 2.0.3 esm-env: 1.2.2 lz-string: 1.5.0 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) optionalDependencies: - '@sveltejs/kit': 2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) sade@1.8.1: dependencies: @@ -8416,7 +8267,7 @@ snapshots: semver@7.8.1: {} - set-cookie-parser@2.7.2: {} + semver@7.8.5: {} set-cookie-parser@3.2.1: {} @@ -8446,36 +8297,38 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 - sharp@0.34.5: + sharp@0.35.5(@types/node@24.12.4): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.1 + semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 24.12.4 shebang-command@2.0.0: dependencies: @@ -8498,8 +8351,6 @@ snapshots: siginfo@2.0.0: {} - signal-exit@4.1.0: {} - simple-swizzle@0.2.4: dependencies: is-arrayish: 0.3.4 @@ -8524,7 +8375,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.10.0: {} + std-env@4.3.0: {} string-width@4.2.3: dependencies: @@ -8532,12 +8383,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.2.0 - string-width@8.1.0: dependencies: get-east-asian-width: 1.6.0 @@ -8558,10 +8403,6 @@ snapshots: strip-json-comments@3.1.1: {} - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - strnum@2.3.0: {} strtok3@10.3.5: @@ -8586,19 +8427,20 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.3.2(picomatch@4.0.4)(svelte@5.55.9(@typescript-eslint/types@8.19.1))(typescript@5.7.3): + svelte-check@4.7.7(picomatch@4.0.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.3 chokidar: 4.0.3 fdir: 6.5.0(picomatch@4.0.4) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - typescript: 5.7.3 + svelte: 5.57.3(@typescript-eslint/types@8.60.0) + typescript: 6.0.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.6.1(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + svelte-eslint-parser@1.6.1(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -8608,35 +8450,30 @@ snapshots: postcss-selector-parser: 7.1.1 semver: 7.8.1 optionalDependencies: - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) - svelte-parse-markup@0.1.5(svelte@5.55.9(@typescript-eslint/types@8.19.1)): - dependencies: - svelte: 5.55.9(@typescript-eslint/types@8.19.1) - - svelte-toolbelt@0.10.6(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + svelte-toolbelt@0.10.6(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: clsx: 2.1.1 - runed: 0.35.1(@sveltejs/kit@2.43.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1))(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.55.9(@typescript-eslint/types@8.19.1)) + runed: 0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) style-to-object: 1.0.14 - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) transitivePeerDependencies: - '@sveltejs/kit' - svelte@5.55.9(@typescript-eslint/types@8.19.1): + svelte@5.57.3(@typescript-eslint/types@8.60.0): dependencies: '@jridgewell/remapping': 2.3.5 - '@jridgewell/sourcemap-codec': 1.5.5 - '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0) + '@jridgewell/sourcemap-codec': 1.6.0 + '@sveltejs/acorn-typescript': 1.0.13(acorn@8.19.0) '@types/estree': 1.0.9 - '@types/trusted-types': 2.0.7 - acorn: 8.16.0 + acorn: 8.19.0 aria-query: 5.3.1 axobject-query: 4.1.0 clsx: 2.1.1 - devalue: 5.8.1 + devalue: 5.9.4 esm-env: 1.2.2 - esrap: 2.2.9(@typescript-eslint/types@8.19.1) + esrap: 2.4.0(@typescript-eslint/types@8.60.0) is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 @@ -8660,20 +8497,12 @@ snapshots: - react - react-dom - test-exclude@7.0.2: - dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 10.5.0 - minimatch: 10.2.5 - thread-stream@3.1.0: dependencies: real-require: 0.2.0 tinybench@2.9.0: {} - tinyexec@0.3.2: {} - tinyexec@1.1.2: {} tinyexec@1.3.0: {} @@ -8683,15 +8512,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@4.0.4: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 + tinyrainbow@3.2.0: {} token-types@6.1.2: dependencies: @@ -8705,9 +8526,9 @@ snapshots: trough@2.2.0: {} - ts-api-utils@2.5.0(typescript@5.7.3): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.7.3 + typescript: 6.0.3 tslib@2.8.1: {} @@ -8717,17 +8538,18 @@ snapshots: type-fest@4.41.0: {} - typescript-eslint@8.19.1(eslint@9.22.0)(typescript@5.7.3): + typescript-eslint@8.60.0(eslint@9.22.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.22.0)(typescript@5.7.3))(eslint@9.22.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.19.1(eslint@9.22.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.22.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.60.0(@typescript-eslint/parser@8.60.0(eslint@9.22.0)(typescript@6.0.3))(eslint@9.22.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.60.0(eslint@9.22.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.60.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.60.0(eslint@9.22.0)(typescript@6.0.3) eslint: 9.22.0 - typescript: 5.7.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - typescript@5.7.3: {} + typescript@6.0.3: {} ufo@1.6.4: {} @@ -8786,7 +8608,7 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - unplugin-icons@22.3.0(svelte@5.55.9(@typescript-eslint/types@8.19.1)): + unplugin-icons@22.3.0(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: '@antfu/install-pkg': 1.1.0 '@iconify/utils': 3.1.3 @@ -8794,7 +8616,7 @@ snapshots: local-pkg: 1.2.1 unplugin: 2.3.11 optionalDependencies: - svelte: 5.55.9(@typescript-eslint/types@8.19.1) + svelte: 5.57.3(@typescript-eslint/types@8.60.0) transitivePeerDependencies: - supports-color @@ -8826,171 +8648,101 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-imagetools@8.0.0(rollup@4.60.4): + vite-imagetools@12.0.1(@types/node@24.12.4)(rollup@4.60.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - imagetools-core: 8.0.0 - sharp: 0.34.5 + imagetools-core: 12.0.0 + sharp: 0.35.5(@types/node@24.12.4) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) transitivePeerDependencies: + - '@types/node' - rollup - vite-node@3.2.4(@types/node@24.12.4)(yaml@2.7.0): + vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0): dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite-node@3.2.4(@types/node@24.12.4)(yaml@2.9.0): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) + lightningcss: 1.33.0 picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.60.4 + rolldown: 1.0.2 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 24.12.4 fsevents: 2.3.3 yaml: 2.7.0 - vite@7.1.7(@types/node@24.12.4)(yaml@2.9.0): + vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) + lightningcss: 1.33.0 picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.60.4 + rolldown: 1.0.2 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 24.12.4 fsevents: 2.3.3 yaml: 2.9.0 - vitefu@1.1.3(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)): + vitefu@1.1.3(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)): optionalDependencies: - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) - vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.7.0): + vitest@4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)): dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@24.12.4)(yaml@2.7.0)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + es-module-lexer: 2.3.2 expect-type: 1.3.0 magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.4 - std-env: 3.10.0 + std-env: 4.3.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.3.0 tinyglobby: 0.2.16 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.1.7(@types/node@24.12.4)(yaml@2.7.0) - vite-node: 3.2.4(@types/node@24.12.4)(yaml@2.7.0) + tinyrainbow: 3.2.0 + vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/debug': 4.1.13 '@types/node': 24.12.4 - '@vitest/ui': 3.2.4(vitest@3.2.4) + '@vitest/coverage-v8': 4.1.7(vitest@4.1.7) + '@vitest/ui': 4.1.7(vitest@4.1.7) transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vitest@3.2.4(@types/debug@4.1.13)(@types/node@24.12.4)(@vitest/ui@3.2.4)(yaml@2.9.0): + vitest@4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)): dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@24.12.4)(yaml@2.9.0)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + es-module-lexer: 2.3.2 expect-type: 1.3.0 magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.4 - std-env: 3.10.0 + std-env: 4.3.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.3.0 tinyglobby: 0.2.16 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.1.7(@types/node@24.12.4)(yaml@2.9.0) - vite-node: 3.2.4(@types/node@24.12.4)(yaml@2.9.0) + tinyrainbow: 3.2.0 + vite: 8.0.14(@types/node@24.12.4)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/debug': 4.1.13 '@types/node': 24.12.4 - '@vitest/ui': 3.2.4(vitest@3.2.4) + '@vitest/coverage-v8': 4.1.7(vitest@4.1.7) + '@vitest/ui': 4.1.7(vitest@4.1.7) transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml vscode-jsonrpc@8.2.0: {} @@ -9042,12 +8794,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.2.0 - wrappy@1.0.2: {} ws@8.18.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b51274fc..7ad16ef4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -22,15 +22,16 @@ catalog: '@eslint/js': 9.22.0 '@types/eslint': 9.6.1 '@types/node': 24.12.4 - '@vitest/coverage-v8': 3.2.4 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 eslint: 9.22.0 eslint-config-prettier: 9.1.0 globals: 16.0.0 prettier: 3.4.2 - typescript: 5.7.3 - typescript-eslint: 8.19.1 - vite: 7.1.7 - vitest: 3.2.4 + typescript: 6.0.3 + typescript-eslint: 8.60.0 + vite: 8.0.14 + vitest: 4.1.7 minimumReleaseAgeExclude: - '@takumi-rs/core-darwin-arm64@1.5.0' - '@takumi-rs/core-darwin-x64@1.5.0' -- 2.54.0 From 18761d597d5dbbd461c16ae2b73916e7829fb53a Mon Sep 17 00:00:00 2001 From: eleith Date: Sat, 31 Oct 2026 06:39:44 -0700 Subject: [PATCH 2/4] [web] integrate SvelteKit 3 with runtime configuration --- .changeset/sveltekit-three-integration.md | 7 + apps/web/MIGRATION_TASKS.md | 184 +++++++ apps/web/docker-compose.dev.yml | 1 - apps/web/eslint.config.js | 9 +- apps/web/package.json | 8 +- apps/web/scripts/config-mounts.test.ts | 7 + apps/web/scripts/manager.js | 4 +- apps/web/scripts/manager.test.ts | 103 ++-- apps/web/scripts/runtime-environment.js | 32 +- apps/web/scripts/runtime-environment.test.ts | 121 ++++- apps/web/scripts/start-server.js | 24 +- apps/web/scripts/start-server.test.ts | 175 +++++- apps/web/src/app.d.ts | 4 +- apps/web/src/hooks.server.test.ts | 104 ++-- apps/web/src/hooks.server.ts | 24 +- .../components/editor/GenerationDialog.svelte | 4 +- .../lib/components/editor/PieceToolbar.svelte | 4 +- .../layout/simple/SearchDialog.svelte | 28 +- .../components/layout/simple/SeoHead.svelte | 2 +- .../layout/simple/ThemeToggle.svelte | 2 +- .../content/components/ContentBanner.svelte | 2 +- apps/web/src/lib/feeds/page.svelte | 6 +- apps/web/src/lib/feeds/utils.ts | 4 +- apps/web/src/lib/generation/client.test.ts | 2 +- apps/web/src/lib/generation/client.ts | 4 +- apps/web/src/lib/generation/types.ts | 2 +- apps/web/src/lib/index.ts | 2 +- apps/web/src/lib/pieces/assets.server.test.ts | 4 +- apps/web/src/lib/pieces/assets.server.ts | 4 +- .../web/src/lib/pieces/components/icon.svelte | 2 +- .../src/lib/pieces/components/icon.test.ts | 2 +- .../components/opengraph.catalog.test.ts | 2 +- .../pieces/components/page.catalog.test.ts | 3 +- .../pieces/components/page.default.test.ts | 3 +- .../web/src/lib/pieces/components/page.svelte | 4 +- .../pieces/preview/assemble.server.test.ts | 2 +- apps/web/src/lib/pieces/preview/output-url.ts | 2 +- .../src/lib/pieces/publication.server.test.ts | 16 +- apps/web/src/lib/pieces/publication.server.ts | 2 +- apps/web/src/lib/pieces/types.server.test.ts | 2 +- apps/web/src/lib/pieces/types.server.ts | 2 +- .../src/lib/pieces/unpublish-client.test.ts | 2 +- apps/web/src/lib/pieces/unpublish-client.ts | 2 +- .../lib/pieces/viewer/AssetOutputs.test.ts | 2 +- .../lib/pieces/viewer/PieceOverview.test.ts | 2 +- .../lib/pieces/viewer/ShareViewport.svelte | 2 +- .../lib/pieces/viewer/ShareViewport.test.ts | 2 +- .../lib/pieces/viewer/SourceContent.svelte | 4 +- .../lib/pieces/viewer/SourceContent.test.ts | 4 +- .../src/lib/pieces/viewer/ViewerHeader.svelte | 5 +- .../lib/pieces/viewer/ViewerHeader.test.ts | 2 +- apps/web/src/lib/pieces/viewer/assets.test.ts | 2 +- apps/web/src/lib/pieces/viewer/assets.ts | 2 +- .../pieces/viewer/fixtures/Composition.svelte | 2 +- .../viewer/fixtures/IconRenderer.svelte | 2 +- .../viewer/fixtures/PageRenderer.svelte | 2 +- apps/web/src/lib/server/asset-csp.test.ts | 12 +- apps/web/src/lib/server/asset-csp.ts | 14 +- apps/web/src/lib/server/auth/index.test.ts | 41 +- apps/web/src/lib/server/auth/index.ts | 4 +- .../web/src/lib/server/auth/providers.test.ts | 2 +- .../src/lib/server/configured-styles.test.ts | 18 +- apps/web/src/lib/server/configured-styles.ts | 2 +- apps/web/src/lib/server/database/index.ts | 4 +- apps/web/src/lib/server/formData.ts | 2 +- apps/web/src/lib/server/generation.test.ts | 4 +- apps/web/src/lib/server/generation.ts | 2 +- .../src/lib/server/generation/stream.test.ts | 2 +- apps/web/src/lib/server/generation/stream.ts | 4 +- apps/web/src/lib/server/health/ai.ts | 2 +- .../lib/server/health/connectivity.test.ts | 4 +- .../web/src/lib/server/health/connectivity.ts | 2 +- .../web/src/lib/server/load/dashboard.test.ts | 8 +- apps/web/src/lib/server/load/dashboard.ts | 4 +- apps/web/src/lib/server/load/health.test.ts | 6 +- apps/web/src/lib/server/load/health.ts | 4 +- apps/web/src/lib/server/pages.test.ts | 4 +- apps/web/src/lib/server/pages.ts | 4 +- apps/web/src/lib/server/pieces.test.ts | 4 +- apps/web/src/lib/server/storage.ts | 2 +- .../web/src/lib/server/workflow/index.test.ts | 6 +- apps/web/src/lib/server/workflow/index.ts | 2 +- .../src/lib/server/workflow/stream.test.ts | 2 +- apps/web/src/lib/server/workflow/stream.ts | 2 +- apps/web/src/params.ts | 19 + apps/web/src/params/feedFormat.ts | 3 - apps/web/src/params/openGraphFormat.ts | 3 - apps/web/src/params/variantFormat.ts | 3 - .../src/routes/(auth)/admin/+layout.server.ts | 2 +- .../src/routes/(auth)/admin/+layout.svelte | 4 +- .../src/routes/(auth)/admin/+page.server.ts | 2 +- .../asset/editor/[...asset]/+page.server.ts | 2 +- .../asset/editor/[...asset]/+page.svelte | 178 ++++-- .../admin/asset/viewer/[...asset]/+server.ts | 2 +- .../directory/[...directory]/+page.server.ts | 2 +- .../[...directory]/page.server.test.ts | 2 +- .../(auth)/admin/health/+page.server.ts | 10 +- .../routes/(auth)/admin/health/+page.svelte | 7 +- .../admin/open/[piece]/[slug]/+server.ts | 4 +- .../admin/open/[piece]/[slug]/server.test.ts | 4 +- .../piece/[...path]/preview/+page.server.ts | 8 +- .../preview/[jobId]/+layout.server.ts | 8 +- .../[...path]/preview/[jobId]/+layout.svelte | 6 +- .../[...path]/preview/[jobId]/+page.svelte | 6 +- .../preview/[jobId]/PreviewFacts.svelte | 2 +- .../preview/[jobId]/PreviewFacts.test.ts | 2 +- .../preview/[jobId]/PreviewLogs.svelte | 2 +- .../preview/[jobId]/PreviewLogs.test.ts | 2 +- .../preview/[jobId]/WorkflowConsole.svelte | 2 +- .../preview/[jobId]/WorkflowConsole.test.ts | 2 +- .../preview/[jobId]/duration.test.ts | 2 +- .../[...path]/preview/[jobId]/duration.ts | 2 +- .../preview/[jobId]/icon/+page.svelte | 2 +- .../preview/[jobId]/layout.server.test.ts | 14 +- .../[...path]/preview/[jobId]/layout.test.ts | 4 +- .../[...path]/preview/[jobId]/page.test.ts | 7 +- .../preview/[jobId]/page/+page.svelte | 2 +- .../preview/[jobId]/share/+page.svelte | 2 +- .../preview/[jobId]/share/page.test.ts | 19 +- .../[...path]/preview/page.server.test.ts | 10 +- .../[...path]/published/+layout.server.ts | 12 +- .../piece/[...path]/published/+layout@.svelte | 2 +- .../piece/[...path]/published/+page.svelte | 6 +- .../[...path]/published/icon/+page.svelte | 2 +- .../[...path]/published/layout.server.test.ts | 12 +- .../piece/[...path]/published/page.test.ts | 19 +- .../[...path]/published/page/+page.svelte | 2 +- .../[...path]/published/share/+page.svelte | 2 +- .../piece/[...path]/source/+page.server.ts | 10 +- .../admin/piece/[...path]/source/+page.svelte | 124 +++-- .../[...path]/source/page.server.test.ts | 18 +- .../create/[[directory]]/+page.server.ts | 4 +- .../pieces/create/[[directory]]/+page.svelte | 4 +- .../create/[[directory]]/page.server.test.ts | 8 +- .../[jobId]/asset/[...asset]/+server.ts | 4 +- .../[jobId]/asset/[...asset]/server.test.ts | 4 +- .../(auth)/admin/publish/+page.server.ts | 8 +- .../routes/(auth)/admin/publish/+page.svelte | 2 +- .../(auth)/admin/publish/page.server.test.ts | 8 +- .../src/routes/(auth)/admin/publish/report.ts | 2 +- .../(auth)/api/admin/generate/+server.ts | 2 +- .../api/admin/preview/[id]/stream/+server.ts | 2 +- .../admin/preview/[id]/stream/server.test.ts | 4 +- .../(auth)/api/admin/publish/+server.ts | 4 +- .../api/admin/publish/[id]/stream/+server.ts | 4 +- .../api/admin/publish/admission.test.ts | 6 +- .../(auth)/api/admin/publish/audit/+server.ts | 4 +- .../(auth)/api/admin/unpublish/+server.ts | 4 +- .../(auth)/api/admin/unpublish/server.test.ts | 2 +- .../(auth)/api/admin/validate/+server.ts | 4 +- apps/web/src/routes/(root)/+page.server.ts | 12 +- apps/web/src/routes/(root)/+page.svelte | 6 +- .../web/src/routes/(root)/page.server.test.ts | 10 +- apps/web/src/routes/+error.svelte | 15 +- apps/web/src/routes/+layout.server.ts | 4 +- apps/web/src/routes/api/pieces/+server.ts | 6 +- apps/web/src/routes/api/pieces/server.test.ts | 6 +- apps/web/src/routes/api/search/+server.ts | 4 +- .../src/routes/api/session/verify/+server.ts | 2 +- .../routes/api/session/verify/server.test.ts | 8 +- apps/web/src/routes/layout.server.test.ts | 4 +- apps/web/src/routes/pieces/+layout.svelte | 2 +- .../routes/pieces/[[piece]]/+page.server.ts | 6 +- .../src/routes/pieces/[[piece]]/+page.svelte | 6 +- .../pieces/[[piece]]/page.server.test.ts | 6 +- .../pieces/[piece]/[slug]/+layout@.svelte | 2 +- .../pieces/[piece]/[slug]/+page.server.ts | 10 +- .../routes/pieces/[piece]/[slug]/+page.svelte | 2 +- .../pieces/[piece]/[slug]/page.server.test.ts | 6 +- .../routes/pieces/assets/[...path]/+server.ts | 2 +- apps/web/src/routes/random/+server.ts | 2 +- .../feed.[format=feedFormat]/+server.ts | 6 +- .../feed.[format=feedFormat]/server.test.ts | 4 +- .../[[piece]]/feed.html/+page.server.ts | 8 +- .../pieces/[[piece]]/feed.html/+page.svelte | 2 +- .../[[piece]]/feed.html/page.server.test.ts | 6 +- .../[tag]/feed.[format=feedFormat]/+server.ts | 4 +- .../rss/tags/[tag]/feed.html/+page.server.ts | 6 +- .../rss/tags/[tag]/feed.html/+page.svelte | 2 +- .../tags/[tag]/feed.html/page.server.test.ts | 6 +- apps/web/src/routes/search/+layout.svelte | 2 +- apps/web/src/routes/search/+page.server.ts | 4 +- apps/web/src/routes/search/+page.svelte | 7 +- apps/web/src/routes/signin/+layout.svelte | 2 +- apps/web/src/routes/signin/+page.server.ts | 4 +- apps/web/src/routes/signin/+page.svelte | 4 +- .../web/src/routes/signin/page.server.test.ts | 8 +- apps/web/src/routes/tags/[tag]/+layout.svelte | 2 +- .../web/src/routes/tags/[tag]/+page.server.ts | 4 +- apps/web/src/routes/tags/[tag]/+page.svelte | 2 +- apps/web/svelte.config.js | 48 -- .../fixtures/templates/src/hooks.server.js | 4 +- .../templates/src/routes/+page.svelte | 6 +- .../test/fixtures/templates/svelte.config.js | 19 - .../test/fixtures/templates/vite.config.js | 23 +- apps/web/test/templates/routes.test.ts | 109 +++- apps/web/test/templates/ssr.test.ts | 7 +- apps/web/tsconfig.json | 54 +- apps/web/vite.config.ts | 61 ++- apps/web/vitest.config.ts | 2 +- pnpm-lock.yaml | 509 ++---------------- 201 files changed, 1623 insertions(+), 1186 deletions(-) create mode 100644 .changeset/sveltekit-three-integration.md create mode 100644 apps/web/MIGRATION_TASKS.md create mode 100644 apps/web/src/params.ts delete mode 100644 apps/web/src/params/feedFormat.ts delete mode 100644 apps/web/src/params/openGraphFormat.ts delete mode 100644 apps/web/src/params/variantFormat.ts delete mode 100644 apps/web/svelte.config.js delete mode 100644 apps/web/test/fixtures/templates/svelte.config.js diff --git a/.changeset/sveltekit-three-integration.md b/.changeset/sveltekit-three-integration.md new file mode 100644 index 00000000..4e22c156 --- /dev/null +++ b/.changeset/sveltekit-three-integration.md @@ -0,0 +1,7 @@ +--- +"@luzzle/web": patch +--- + +Integrate SvelteKit 3 with the stock web build, native fixtures and runtime YAML +configuration. Preserve configured public origins before native CSRF checks, +stateless authentication, built-in errors and draft-preserving editor behavior. diff --git a/apps/web/MIGRATION_TASKS.md b/apps/web/MIGRATION_TASKS.md new file mode 100644 index 00000000..2261a4c4 --- /dev/null +++ b/apps/web/MIGRATION_TASKS.md @@ -0,0 +1,184 @@ + + +# SvelteKit 3 migration tasks + +Some migrations are uncommon, context-dependent, or disproportionately difficult to automate safely. This document lists them. + +The findings are intentionally broad and may include files that do not require changes. + +AI agents can work through the migration tasks below one at a time: + +1. Read the linked documentation or pull request before changing code. +2. Inspect every listed file and its surrounding context. +3. Ignore a finding when the file is not actually affected. +4. Make the smallest necessary change. Do not perform unrelated refactors or introduce unnecessary abstractions. +5. Ask the user when application intent cannot be inferred. +6. Run the project's appropriate checks after editing. + +Also search the project for `@migration-task` comments. They may describe additional manual migration work that must be resolved. + +## Migration tasks + +### Migrate remaining `base`, `assets`, and `resolveRoute` from `$app/paths` usages + +The automatic migration can leave `base`, `assets`, and `resolveRoute` usages from `$app/paths`. + +#### What to do + +The deprecated base, assets, and resolveRoute exports have been removed from $app/paths. Use asset and resolve instead:```diff// instead of this... +const pathname = base + resolveRoute('/blog/[slug]', { slug }); +const file = assets + '/foo.png'; + +// ...do this: +const pathname = resolve('/blog/[slug]', { slug }); +const file = asset('foo.png'); + +``` +The `Pathname` and `Asset` types have also been renamed to `Path` and `AssetPath`, and the leading `/` has been removed from those types — so `asset('/foo.png')` should now be `asset('foo.png')`, and pathnames passed to `resolve` no longer start with `/` (e.g. `resolve('blog/hello-world')`). Only route IDs start with `/` now. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-paths-base-assets-and-resolveRoute-removed) + +#### Files to review + +- [ ] `src/lib/pieces/helpers.test.ts` +- [ ] `src/lib/pieces/viewer/ShareViewport.test.ts` +- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts` +- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts` +- [ ] `src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts` +- [ ] `src/routes/(auth)/admin/piece/[...path]/published/page.test.ts` + +### Copy `page.url` before mutating it + +`page.url` and its search parameters are readonly in SvelteKit 3. + +#### What to do + +Create a mutable copy with `new URL(page.url.href)`, mutate that copy, and use it for navigation or serialization. Leave readonly accesses unchanged. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-state-page.url-is-now-readonly) + +#### Files to review + +- [ ] `src/lib/content/components/ContentBanner.svelte` +- [ ] `src/routes/(auth)/admin/+layout.svelte` +- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte` +- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` + +### Review `goto` destinations + +`goto(...)` now rejects destinations that do not resolve to an application route. + +#### What to do + +Confirm each destination is an internal route. Use `window.location.href` for external browser navigation, and handle rejected dynamic destinations where they can fail to resolve. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-goto-rejects-for-URLs-that-don't-resolve-to-a-route) + +#### Files to review + +- [ ] `src/lib/components/layout/simple/SearchDialog.svelte` +- [ ] `src/routes/(auth)/admin/+layout.svelte` +- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` +- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` +- [ ] `src/routes/signin/+page.svelte` + +### Replace `invalidateAll` with `refreshAll` + +`invalidateAll` is deprecated in favor of `refreshAll` + +#### What to do + +Replace `invalidateAll()` with `refreshAll()`. Note that `refreshAll()` will keep `page.state` intact. Check if that has unwanted impact on the application; if yes point this out in a comment with steps to take instead of replacing that instance of `invalidateAll()`. Similarly, replace goto(..., { invalidateAll: true }) with goto(..., { refreshAll: true }). + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-invalidateAll-is-deprecated-in-favour-of-refreshAll) + +#### Files to review + +- [ ] `src/lib/components/layout/simple/SearchDialog.svelte` +- [ ] `src/routes/(auth)/admin/+layout.svelte` +- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` +- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` +- [ ] `src/routes/signin/+page.svelte` + +### Review enhanced forms with explicit actions + +Enhanced forms that submit to another page now navigate to that page. + +#### What to do + +Determine whether each action resolves to another page and confirm navigation is intended. If the form must remain on the current page, implement that behavior explicitly in the enhancement callback. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-forms) + +#### Files to review + +- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` +- [ ] `src/routes/(auth)/admin/health/+page.svelte` +- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` + +### CORS for static assets in development is handled by Vite + +SvelteKit no longer sets `access-control-allow-origin: *` on every static asset request in development. CORS is now delegated to Vite's built-in middleware. + +#### What to do + +If you rely on cross-origin access to static assets in dev, configure it in your Vite config by adding server.cors.origin = "*" to your config. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Security-CORS-for-static-assets-in-development-is-handled-by-Vite) + +### `json` and `text` are deprecated + +The `json(...)` and `text(...)` helpers from `@sveltejs/kit` for generating responses are deprecated. + +#### What to do + +Use `Response.json(...)` and `new Response(text)` instead. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-json-and-text-are-deprecated) + +#### Files to review + +- [ ] `src/hooks.server.ts` +- [ ] `src/routes/(auth)/api/admin/publish/+server.ts` +- [ ] `src/routes/(auth)/api/admin/publish/audit/+server.ts` +- [ ] `src/routes/(auth)/api/admin/unpublish/+server.ts` +- [ ] `src/routes/(auth)/api/admin/validate/+server.ts` +- [ ] `src/routes/api/health/+server.ts` +- [ ] `src/routes/api/pieces/+server.ts` +- [ ] `src/routes/api/search/+server.ts` +- [ ] `src/routes/api/session/verify/+server.ts` + +### Move adapter-node `ORIGIN` to `paths.origin` + +The adapter-node `ORIGIN` environment variable is removed. + +#### What to do + +Find where `ORIGIN` is set (`.env` files, scripts, hosting dashboard, ...) and move the public-facing origin to `paths.origin` in `sveltekit(...)`. Do not delete `ORIGIN`: it is ignored by adapter-node now and may have other uses. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-node) + +## Final verification + +- [ ] Review every migration task and ignore any irrelevant findings. +- [ ] Resolve remaining `@migration-task` comments. +- [ ] Run the project's type checker and tests. +- [ ] Build the project successfully. +- [ ] Delete this file when all migration work is complete. +``` diff --git a/apps/web/docker-compose.dev.yml b/apps/web/docker-compose.dev.yml index 30db624d..e69bed83 100644 --- a/apps/web/docker-compose.dev.yml +++ b/apps/web/docker-compose.dev.yml @@ -30,7 +30,6 @@ services: - ./static:/m/apps/web/static - ./scripts:/m/apps/web/scripts - ./tsconfig.json:/m/apps/web/tsconfig.json - - ./svelte.config.js:/m/apps/web/svelte.config.js - ./vite.config.ts:/m/apps/web/vite.config.ts networks: - luzzle diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index 1d31c9f9..1ae47628 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -3,6 +3,12 @@ import ts from 'typescript-eslint' import svelte from 'eslint-plugin-svelte' import prettier from 'eslint-config-prettier' import globals from 'globals' +import { loadConfig } from '@sveltejs/load-config' + +const loadedConfig = await loadConfig(import.meta.dirname, { traverse: false }) +if (!loadedConfig) throw new Error('Svelte configuration not found') +if ('error' in loadedConfig) throw loadedConfig.error +const svelteConfig = loadedConfig.config /** @type {import('eslint').Linter.Config[]} */ export default [ @@ -36,7 +42,8 @@ export default [ files: ['**/*.svelte'], languageOptions: { parserOptions: { - parser: ts.parser + parser: ts.parser, + svelteConfig } } }, diff --git a/apps/web/package.json b/apps/web/package.json index 9b413b38..bf4e8b9c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -45,9 +45,10 @@ "@luzzle/core": "workspace:*", "@luzzle/web.config": "workspace:*", "@luzzle/web.jobs": "workspace:*", - "@sveltejs/adapter-node": "5.3.2", + "@sveltejs/adapter-node": "6.0.1", "@sveltejs/enhanced-img": "1.0.0", - "@sveltejs/kit": "2.70.3", + "@sveltejs/kit": "3.1.1", + "@sveltejs/load-config": "0.2.2", "@sveltejs/vite-plugin-svelte": "7.1.2", "@types/eslint": "catalog:", "@types/mime-types": "3.0.1", @@ -90,5 +91,8 @@ "kysely": "0.28.17", "micromark": "4.0.2", "openworkflow": "0.11.0" + }, + "imports": { + "#lib/*": "./src/lib/*" } } diff --git a/apps/web/scripts/config-mounts.test.ts b/apps/web/scripts/config-mounts.test.ts index e742e20c..2ee299e1 100644 --- a/apps/web/scripts/config-mounts.test.ts +++ b/apps/web/scripts/config-mounts.test.ts @@ -18,6 +18,13 @@ test.each([ expect(volumes.some((volume) => volume.startsWith('./demo/config.yaml:'))).toBe(false) }) +test('development mounts the Vite configuration without the retired Svelte config', async () => { + const compose = parse(await readFile(path.join(web, 'docker-compose.dev.yml'), 'utf8')) + const volumes: string[] = compose.services['luzzle-dev-web'].volumes + expect(volumes).toContain('./vite.config.ts:/m/apps/web/vite.config.ts') + expect(volumes.some((volume) => volume.startsWith('./svelte.config.js:'))).toBe(false) +}) + test('the demo configuration is a regular file in its mounted directory', async () => { const canonical = path.join(web, 'demo/config/config.yaml') expect((await lstat(canonical)).isFile()).toBe(true) diff --git a/apps/web/scripts/manager.js b/apps/web/scripts/manager.js index 302c0370..03fd5490 100644 --- a/apps/web/scripts/manager.js +++ b/apps/web/scripts/manager.js @@ -2,11 +2,11 @@ import { loadConfig } from '@luzzle/web.templates/config' import { spawn } from 'node:child_process' import { writeFile } from 'node:fs/promises' import path from 'node:path' +import { fileURLToPath } from 'node:url' import { createServerEnvironment } from './runtime-environment.js' const PATHS = { CONFIG: path.resolve('config.yaml'), - LOCAL_BUILD: path.resolve('build'), LOG: path.resolve('data/build.log') } @@ -90,7 +90,7 @@ class ExplorerManager { const command = this.isDev ? 'pnpm' : 'node' const args = this.isDev ? ['run', 'dev', '--', '--host'] - : [path.join(PATHS.LOCAL_BUILD, 'index.js')] + : [fileURLToPath(new URL('./start-server.js', import.meta.url))] this.serverProcess = spawn(command, args, spawnOptions) diff --git a/apps/web/scripts/manager.test.ts b/apps/web/scripts/manager.test.ts index e87bde6d..6d5a59a8 100644 --- a/apps/web/scripts/manager.test.ts +++ b/apps/web/scripts/manager.test.ts @@ -16,6 +16,13 @@ import { promisify } from 'node:util' const exec = promisify(execFile) const manager = fileURLToPath(new URL('./manager.js', import.meta.url)) +const launcher = fileURLToPath(new URL('./start-server.js', import.meta.url)) +const absoluteEnvironment = { + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' +} const cache = fileURLToPath(new URL('../node_modules/.cache/', import.meta.url)) let directory: string @@ -37,7 +44,17 @@ afterEach(async () => { function run(dev = true, environment: NodeJS.ProcessEnv = {}) { const inherited = { ...process.env } - for (const key of ['ORIGIN', 'BETTER_AUTH_URL', 'AUTH_URL', 'NEXTAUTH_URL']) delete inherited[key] + for (const key of [ + 'HOST', + 'ORIGIN', + 'PROTOCOL_HEADER', + 'HOST_HEADER', + 'PORT_HEADER', + 'BETTER_AUTH_URL', + 'AUTH_URL', + 'NEXTAUTH_URL' + ]) + delete inherited[key] return exec(process.execPath, [manager], { cwd: directory, env: { @@ -73,16 +90,20 @@ test('development startup loads config from the mounted working directory withou expect(stdout).not.toContain('Initiating production build sequence') }) -test('production startup uses build and log files in the same working directory', async () => { +test('production delegates to the absolute real launcher with build and log files in the working directory', async () => { await configure() await writeFile( path.join(directory, 'build/index.js'), - 'console.log("manager-server " + JSON.stringify({cwd:process.cwd(),host:process.env.HOST}))' + `import { createServer } from 'node:http' +export const server = createServer() +console.log("manager-server " + JSON.stringify({ + cwd:process.cwd(),host:process.env.HOST,launcher:process.argv[1] +}))` ) const { stdout } = await run(false) expect(stdout).toContain('manager-command run build') expect(stdout).toContain( - `manager-server ${JSON.stringify({ cwd: directory, host: '127.0.0.1' })}` + `manager-server ${JSON.stringify({ cwd: directory, host: '127.0.0.1', launcher })}` ) const log = await readFile(path.join(directory, 'data/build.log'), 'utf8') expect(log).toContain('Config App URL: https://manager.example/garden') @@ -98,7 +119,9 @@ test('fresh production startups follow the compatibility link after atomic confi await symlink('config/config.yaml', filename) await writeFile( path.join(directory, 'build/index.js'), - 'console.log("manager-server " + JSON.stringify({cwd:process.cwd(),host:process.env.HOST}))' + `import { createServer } from 'node:http' +export const server = createServer() +console.log("manager-server " + JSON.stringify({cwd:process.cwd(),host:process.env.HOST}))` ) // Each run is a fresh manager process, not a live-server config reload. @@ -132,21 +155,23 @@ test('startup still fails when the working-directory config is missing', async ( async function captureServerEnvironment() { await writeFile( path.join(directory, 'build/index.js'), - 'console.log("manager-environment " + JSON.stringify({host:process.env.HOST,origin:process.env.ORIGIN}))' + `import { createServer } from 'node:http' +// Keep the native export unbound so this environment snapshot exits naturally. +export const server = createServer() +console.log("manager-environment " + JSON.stringify({ + HOST:process.env.HOST,ORIGIN:process.env.ORIGIN, + PROTOCOL_HEADER:process.env.PROTOCOL_HEADER,HOST_HEADER:process.env.HOST_HEADER, + PORT_HEADER:process.env.PORT_HEADER +}))` ) } -test('production derives the canonical Kit origin from configured url.app', async () => { +test('production selects internal protocol and authority headers from configured url.app', async () => { await configure('https://Manager.EXAMPLE:443/garden/', true) await captureServerEnvironment() const { stdout } = await run(false) expect(stdout).toContain('manager-command run build') - expect(stdout).toContain( - `manager-environment ${JSON.stringify({ - host: '127.0.0.1', - origin: 'https://manager.example' - })}` - ) + expect(stdout).toContain(`manager-environment ${JSON.stringify(absoluteEnvironment)}`) }) test('production uses YAML even when ambient URL variables disagree', async () => { @@ -154,40 +179,52 @@ test('production uses YAML even when ambient URL variables disagree', async () = await captureServerEnvironment() const { stdout } = await run(false, { ORIGIN: 'not-a-url', + PROTOCOL_HEADER: 'x-forwarded-proto', + HOST_HEADER: 'x-forwarded-host', + PORT_HEADER: 'x-forwarded-port', BETTER_AUTH_URL: 'https://unexpected.example', AUTH_URL: '', NEXTAUTH_URL: '' }) - expect(stdout).toContain( - 'manager-environment {"host":"127.0.0.1","origin":"https://manager.example"}' - ) + expect(stdout).toContain(`manager-environment ${JSON.stringify(absoluteEnvironment)}`) }) -test.each(['', '/garden'])('production keeps app URL %s request-derived', async (app) => { - await configure(app, false) - await captureServerEnvironment() - const { stdout } = await run(false) - expect(stdout).toContain('manager-environment {"host":"127.0.0.1"}') -}) +test.each(['', '/garden'])( + 'production keeps app URL %s request-derived and clears ambient ORIGIN', + async (app) => { + await configure(app, false) + await captureServerEnvironment() + const { stdout } = await run(false, { + ORIGIN: 'https://unexpected.example', + PROTOCOL_HEADER: 'x-forwarded-proto' + }) + expect(stdout).toContain( + `manager-environment ${JSON.stringify({ + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-forwarded-proto' + })}` + ) + } +) -test('production derives Kit origin even with authentication disabled', async () => { +test('production selects configured header settings even with authentication disabled', async () => { await configure() await captureServerEnvironment() const { stdout } = await run(false) - expect(stdout).toContain( - 'manager-environment {"host":"127.0.0.1","origin":"https://manager.example"}' - ) + expect(stdout).toContain(`manager-environment ${JSON.stringify(absoluteEnvironment)}`) }) -test('development derives the same internal Kit origin from YAML', async () => { +test('development selects the same internal headers from YAML and clears ambient ORIGIN', async () => { await configure(undefined, true) await writeFile( path.join(directory, 'bin/pnpm'), - '#!/bin/sh\nprintf "manager-environment HOST=%s ORIGIN=%s\\n" "$HOST" "${ORIGIN-unset}"\n', + '#!/bin/sh\nprintf "manager-environment HOST=%s ORIGIN=%s PROTOCOL_HEADER=%s HOST_HEADER=%s PORT_HEADER=%s\\n" "$HOST" "${ORIGIN-unset}" "$PROTOCOL_HEADER" "$HOST_HEADER" "${PORT_HEADER-unset}"\n', { mode: 0o755 } ) const { stdout } = await run(true, { ORIGIN: 'https://unexpected.example' }) - expect(stdout).toContain('manager-environment HOST=127.0.0.1 ORIGIN=https://manager.example') + expect(stdout).toContain( + 'manager-environment HOST=127.0.0.1 ORIGIN=unset PROTOCOL_HEADER=x-luzzle-public-protocol HOST_HEADER=x-luzzle-public-authority PORT_HEADER=\n' + ) expect(stdout).not.toContain('Initiating production build sequence') }) @@ -206,7 +243,13 @@ test('production rejects unsafe configured URLs before logging values, building test('authenticated production rejects relative app URLs instead of using an ambient origin', async () => { await configure('/garden', true) - await expect(run(false, { ORIGIN: 'https://unexpected.example' })).rejects.toMatchObject({ + await expect( + run(false, { + ORIGIN: 'https://unexpected.example', + BETTER_AUTH_URL: 'https://unexpected.example', + AUTH_URL: 'https://legacy.example' + }) + ).rejects.toMatchObject({ code: 1, stdout: 'FATAL ERROR: Authentication requires an absolute url.app\n' }) diff --git a/apps/web/scripts/runtime-environment.js b/apps/web/scripts/runtime-environment.js index 00c9eeb2..8f3ef35a 100644 --- a/apps/web/scripts/runtime-environment.js +++ b/apps/web/scripts/runtime-environment.js @@ -1,3 +1,6 @@ +const protocolHeader = 'x-luzzle-public-protocol' +const authorityHeader = 'x-luzzle-public-authority' + /** @param {string} value */ function parseAppUrl(value) { const invalid = () => new Error('Invalid url.app: expected an HTTP(S) URL without credentials') @@ -16,7 +19,7 @@ function parseAppUrl(value) { } /** @param {string} app */ -function originFromAppUrl(app) { +export function resolveAppOrigin(app) { const schemeCandidate = app.replace(/[\s\p{Cc}]/gu, '') if (!/^[a-z][a-z\d+.-]*:/i.test(schemeCandidate)) return undefined return parseAppUrl(app).origin @@ -24,11 +27,25 @@ function originFromAppUrl(app) { /** @param {string} app */ export function resolveAuthUrl(app) { - const origin = originFromAppUrl(app) + const origin = resolveAppOrigin(app) if (origin) return origin throw new Error('Authentication requires an absolute url.app') } +/** + * @param {import('node:http').Server} server + * @param {string | undefined} origin + */ +export function installRequestOrigin(server, origin) { + if (!origin) return + const url = new URL(origin) + const protocol = url.protocol.slice(0, -1) + server.prependListener('request', (request) => { + request.headers[protocolHeader] = protocol + request.headers[authorityHeader] = url.host + }) +} + /** * @param {Pick} config * @param {NodeJS.ProcessEnv} environment @@ -37,10 +54,13 @@ export function resolveAuthUrl(app) { export function createServerEnvironment(config, environment = process.env) { /** @type {NodeJS.ProcessEnv} */ const env = { ...environment, HOST: config.network?.public?.host || '0.0.0.0' } - const origin = originFromAppUrl(config.url.app) + const origin = resolveAppOrigin(config.url.app) if (config.auth && !origin) throw new Error('Authentication requires an absolute url.app') - - if (origin) env.ORIGIN = origin - else delete env.ORIGIN + delete env.ORIGIN + if (origin) { + env.PROTOCOL_HEADER = protocolHeader + env.HOST_HEADER = authorityHeader + env.PORT_HEADER = '' + } return env } diff --git a/apps/web/scripts/runtime-environment.test.ts b/apps/web/scripts/runtime-environment.test.ts index f11f8339..1b5c5f25 100644 --- a/apps/web/scripts/runtime-environment.test.ts +++ b/apps/web/scripts/runtime-environment.test.ts @@ -1,6 +1,13 @@ import { afterEach, expect, test, vi } from 'vitest' import type { TemplateConfig } from '@luzzle/web.templates/config' -import { createServerEnvironment, resolveAuthUrl } from './runtime-environment.js' +import { createServer, IncomingMessage, ServerResponse } from 'node:http' +import { Socket } from 'node:net' +import { + createServerEnvironment, + installRequestOrigin, + resolveAppOrigin, + resolveAuthUrl +} from './runtime-environment.js' type RuntimeConfig = Pick const auth: NonNullable = { @@ -38,12 +45,29 @@ test('returns a fresh environment without mutating config or supplied environmen expect(result).toEqual({ HOST: '127.0.0.1', UNRELATED: 'keep', - ORIGIN: 'https://configured.example' + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' }) expect(environment).toEqual({ HOST: 'old-host', UNRELATED: 'keep' }) expect(configured.url.app).toBe('https://configured.example/garden/?page=2#top') }) +test('absolute YAML replaces ambient header selection without putting the authority in the environment', () => { + const result = createServerEnvironment(config('https://configured.example:8443/garden'), { + ORIGIN: 'https://unexpected.example', + PROTOCOL_HEADER: 'x-forwarded-proto', + HOST_HEADER: 'x-forwarded-host', + PORT_HEADER: 'x-forwarded-port' + }) + expect(result).toEqual({ + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' + }) +}) + test('falls back to the existing public bind host when the configured host is empty', () => { const configured = config() configured.network.public.host = '' @@ -56,9 +80,15 @@ test.each([ ['https://example.com:8443/garden/', 'https://example.com:8443'], ['http://localhost:8080', 'http://localhost:8080'], ['https://[::1]:443/', 'https://[::1]'] -])('Kit and auth derive the same canonical origin from %s', (app, expected) => { - expect(createServerEnvironment(config(app, true), {}).ORIGIN).toBe(expected) +])('request boundary and auth derive the same canonical origin from %s', (app, expected) => { + expect(resolveAppOrigin(app)).toBe(expected) expect(resolveAuthUrl(app)).toBe(expected) + expect(createServerEnvironment(config(app, true), {})).toEqual({ + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' + }) }) test('ambient URL variables cannot replace the configured public URL', () => { @@ -67,7 +97,12 @@ test('ambient URL variables cannot replace the configured public URL', () => { vi.stubEnv('AUTH_URL', 'https://legacy.example') vi.stubEnv('NEXTAUTH_URL', '') const before = { ...process.env } - expect(createServerEnvironment(config(undefined, true)).ORIGIN).toBe('https://configured.example') + expect(createServerEnvironment(config(undefined, true))).toMatchObject({ + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' + }) + expect(createServerEnvironment(config(undefined, true))).not.toHaveProperty('ORIGIN') expect(resolveAuthUrl('https://configured.example/garden')).toBe('https://configured.example') expect(process.env).toEqual(before) }) @@ -75,8 +110,19 @@ test('ambient URL variables cannot replace the configured public URL', () => { test.each(['', '/', '/garden/', 'garden', './garden', '../garden', '//example.com/garden'])( 'public-only relative app URL %s leaves Kit request-derived, ignoring ambient ORIGIN', (app) => { - const result = createServerEnvironment(config(app), { ORIGIN: 'https://unexpected.example' }) - expect(result).not.toHaveProperty('ORIGIN') + const result = createServerEnvironment(config(app), { + ORIGIN: 'https://unexpected.example', + PROTOCOL_HEADER: 'x-forwarded-proto', + HOST_HEADER: 'x-forwarded-host', + PORT_HEADER: 'x-forwarded-port' + }) + expect(result).toEqual({ + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-forwarded-proto', + HOST_HEADER: 'x-forwarded-host', + PORT_HEADER: 'x-forwarded-port' + }) + expect(resolveAppOrigin(app)).toBeUndefined() expect(() => resolveAuthUrl(app)).toThrow('Authentication requires an absolute url.app') expect(() => createServerEnvironment(config(app, true), {})).toThrow( 'Authentication requires an absolute url.app' @@ -84,6 +130,67 @@ test.each(['', '/', '/garden/', 'garden', './garden', '../garden', '//example.co } ) +test('relative public mode does not introduce header settings when none were supplied', () => { + expect( + createServerEnvironment(config('/garden'), { ORIGIN: 'https://unexpected.example' }) + ).toEqual({ + HOST: '127.0.0.1' + }) +}) + +test('origin listener precedes the native listener on the first and later requests, replacing only internal headers', () => { + const received: Record[] = [] + const nativeListener = vi.fn((request: IncomingMessage) => { + received.push({ method: request.method, url: request.url, headers: { ...request.headers } }) + }) + const server = createServer(nativeListener) + installRequestOrigin(server, resolveAppOrigin('https://Configured.EXAMPLE:8443/garden')) + expect(server.listeners('request')[1]).toBe(nativeListener) + + for (const origin of ['https://configured.example:8443', 'https://browser.example']) { + const request = new IncomingMessage(new Socket()) + request.method = 'POST' + request.url = '/garden/action?next=%2Fprivate&tag=a&tag=b' + request.headers = { + host: 'internal.example:3000', + origin, + cookie: 'session=unchanged', + 'content-type': 'application/x-www-form-urlencoded', + 'x-forwarded-proto': 'http', + 'x-forwarded-host': 'spoofed.example:9999', + 'x-forwarded-port': '9999', + 'x-luzzle-public-protocol': 'http', + 'x-luzzle-public-authority': 'spoofed.example:9999' + } + const original = { ...request.headers } + server.emit('request', request, new ServerResponse(request)) + expect(received.at(-1)).toEqual({ + method: 'POST', + url: '/garden/action?next=%2Fprivate&tag=a&tag=b', + headers: { + ...original, + 'x-luzzle-public-protocol': 'https', + 'x-luzzle-public-authority': 'configured.example:8443' + } + }) + request.destroy() + } + expect(nativeListener).toHaveBeenCalledTimes(2) +}) + +test('relative public origin leaves the native request listener and headers untouched', () => { + const nativeListener = vi.fn() + const server = createServer(nativeListener) + installRequestOrigin(server, resolveAppOrigin('/garden')) + expect(server.listeners('request')).toEqual([nativeListener]) + const request = new IncomingMessage(new Socket()) + request.headers = { host: 'native.example', 'x-forwarded-proto': 'https' } + server.emit('request', request, new ServerResponse(request)) + expect(request.headers).toEqual({ host: 'native.example', 'x-forwarded-proto': 'https' }) + expect(nativeListener).toHaveBeenCalledOnce() + request.destroy() +}) + test.each([ 'ftp://example.com/private-token', 'ws://example.com', diff --git a/apps/web/scripts/start-server.js b/apps/web/scripts/start-server.js index f1f4c105..38313c1f 100644 --- a/apps/web/scripts/start-server.js +++ b/apps/web/scripts/start-server.js @@ -1,19 +1,31 @@ import { loadConfig } from '@luzzle/web.templates/config' import path from 'node:path' import { pathToFileURL } from 'node:url' -import { createServerEnvironment } from './runtime-environment.js' +import { + createServerEnvironment, + installRequestOrigin, + resolveAppOrigin +} from './runtime-environment.js' /** @param {NodeJS.ProcessEnv} environment */ function applyServerEnvironment(environment) { - for (const key of ['HOST', 'ORIGIN']) { + for (const key of ['HOST', 'ORIGIN', 'PROTOCOL_HEADER', 'HOST_HEADER', 'PORT_HEADER']) { const value = environment[key] if (value === undefined) delete process.env[key] else process.env[key] = value } } -const config = loadConfig(path.resolve('config.yaml')) -const environment = createServerEnvironment(config) -applyServerEnvironment(environment) +/** @param {string} entry */ +export async function startServer(entry = path.resolve('build/index.js')) { + const config = loadConfig(path.resolve('config.yaml')) + const origin = resolveAppOrigin(config.url.app) + applyServerEnvironment(createServerEnvironment(config)) + const { server } = await import(pathToFileURL(entry).href) + installRequestOrigin(server, origin) + return server +} -await import(pathToFileURL(path.resolve('build/index.js')).href) +if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url) { + await startServer() +} diff --git a/apps/web/scripts/start-server.test.ts b/apps/web/scripts/start-server.test.ts index c834454f..402d1c2d 100644 --- a/apps/web/scripts/start-server.test.ts +++ b/apps/web/scripts/start-server.test.ts @@ -2,12 +2,18 @@ import { afterEach, beforeEach, expect, test } from 'vitest' import { execFile } from 'node:child_process' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import path from 'node:path' -import { fileURLToPath } from 'node:url' +import { fileURLToPath, pathToFileURL } from 'node:url' import { promisify } from 'node:util' const exec = promisify(execFile) const entry = fileURLToPath(new URL('./start-server.js', import.meta.url)) const cache = fileURLToPath(new URL('../node_modules/.cache/', import.meta.url)) +const absoluteEnvironment = { + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-luzzle-public-protocol', + HOST_HEADER: 'x-luzzle-public-authority', + PORT_HEADER: '' +} let directory: string beforeEach(async () => { @@ -16,10 +22,17 @@ beforeEach(async () => { directory = await mkdtemp(path.join(cache, 'server-startup #-')) await mkdir(path.join(directory, 'build')) // Snapshot at module evaluation: the entry must apply the environment before import. + // Export the native Node server, unbound so this snapshot fixture exits naturally. // Never print the entire environment or any auth secrets in the child fixture. await writeFile( path.join(directory, 'build/index.js'), - 'console.log(JSON.stringify({HOST:process.env.HOST,ORIGIN:process.env.ORIGIN}))' + `import { createServer } from 'node:http' +export const server = createServer() +console.log(JSON.stringify({ + HOST:process.env.HOST,ORIGIN:process.env.ORIGIN, + PROTOCOL_HEADER:process.env.PROTOCOL_HEADER,HOST_HEADER:process.env.HOST_HEADER, + PORT_HEADER:process.env.PORT_HEADER +}))` ) }) @@ -27,11 +40,20 @@ afterEach(async () => { await rm(directory, { recursive: true, force: true }) }) -function run(environment: NodeJS.ProcessEnv = {}) { +function run(environment: NodeJS.ProcessEnv = {}, args = [entry]) { const inherited = { ...process.env } - for (const key of ['HOST', 'ORIGIN', 'BETTER_AUTH_URL', 'AUTH_URL', 'NEXTAUTH_URL']) + for (const key of [ + 'HOST', + 'ORIGIN', + 'PROTOCOL_HEADER', + 'HOST_HEADER', + 'PORT_HEADER', + 'BETTER_AUTH_URL', + 'AUTH_URL', + 'NEXTAUTH_URL' + ]) delete inherited[key] - return exec(process.execPath, [entry], { + return exec(process.execPath, args, { cwd: directory, env: { ...inherited, ...environment }, timeout: 10000 @@ -56,56 +78,161 @@ async function configure( await writeFile(path.join(directory, 'config.yaml'), JSON.stringify(config)) } -test('loads working-directory config and applies the canonical environment before importing the server', async () => { +test('loads working-directory config and applies internal header settings before importing the server', async () => { await configure() const { stdout } = await run({ HOST: 'ignored-host' }) - expect(JSON.parse(stdout)).toEqual({ - HOST: '127.0.0.1', - ORIGIN: 'https://startup.example' - }) + expect(JSON.parse(stdout)).toEqual(absoluteEnvironment) }) -test('configured url.app supplies internal ORIGIN despite ambient URL settings', async () => { +test('configured url.app selects internal headers and clears ORIGIN despite ambient URL settings', async () => { await configure() const { stdout } = await run({ ORIGIN: 'not-a-url', + PROTOCOL_HEADER: 'x-forwarded-proto', + HOST_HEADER: 'x-forwarded-host', + PORT_HEADER: 'x-forwarded-port', BETTER_AUTH_URL: 'https://unexpected.example', AUTH_URL: 'https://legacy.example', NEXTAUTH_URL: '' }) - expect(JSON.parse(stdout)).toEqual({ HOST: '127.0.0.1', ORIGIN: 'https://startup.example' }) + expect(JSON.parse(stdout)).toEqual(absoluteEnvironment) }) test.each(['', '/garden', './garden', '//relative.example/garden'])( 'leaves app URL %s request-derived before importing the server', async (app) => { await configure(app, false) - const { stdout } = await run({ ORIGIN: 'https://unexpected.example' }) - expect(JSON.parse(stdout)).toEqual({ HOST: '127.0.0.1' }) + const { stdout } = await run({ + ORIGIN: 'https://unexpected.example', + PROTOCOL_HEADER: 'x-forwarded-proto' + }) + expect(JSON.parse(stdout)).toEqual({ + HOST: '127.0.0.1', + PROTOCOL_HEADER: 'x-forwarded-proto' + }) } ) -test('does not add an auth URL when authentication is disabled', async () => { +test('uses configured header settings even when authentication is disabled', async () => { await configure(undefined, false) const { stdout } = await run() - expect(JSON.parse(stdout)).toEqual({ - HOST: '127.0.0.1', - ORIGIN: 'https://startup.example' - }) + expect(JSON.parse(stdout)).toEqual(absoluteEnvironment) }) test('uses the existing HOST fallback for an empty configured bind host', async () => { await configure(undefined, true, '') const { stdout } = await run() - expect(JSON.parse(stdout)).toEqual({ - HOST: '0.0.0.0', - ORIGIN: 'https://startup.example' - }) + expect(JSON.parse(stdout)).toEqual({ ...absoluteEnvironment, HOST: '0.0.0.0' }) }) +test('import alone exports startServer without loading config or starting the default entry', async () => { + // No config exists; the default entry would print a different snapshot if imported. + const { stdout, stderr } = await run({}, [ + '--input-type=module', + '--eval', + `const { startServer } = await import(${JSON.stringify(pathToFileURL(entry).href)}) +console.log(typeof startServer)` + ]) + expect(stdout).toBe('function\n') + expect(stderr).toBe('') +}) + +test.each(['https://Public.EXAMPLE:8443/garden', 'http://Other.EXAMPLE:8080/base'])( + 'startServer returns the native server and normalizes its first HTTP request using %s', + async (app) => { + await configure(app) + const compiledEntry = path.join(directory, 'build/probe #.js') + // This is a native HTTP transport probe, not proof of Kit pre-handle CSRF. + // Like the adapter entry, it installs its request listener and starts listening on import. + await writeFile( + compiledEntry, + `import { createServer } from 'node:http' +export const server = createServer(async (request, response) => { + const protocol = request.headers[process.env.PROTOCOL_HEADER] + const authority = request.headers[process.env.HOST_HEADER] + const url = new URL(request.url, protocol + '://' + authority).href + const headers = { ...request.headers } + let body = '' + for await (const chunk of request) body += chunk + response.setHeader('content-type', 'application/json') + response.end(JSON.stringify({ url, method: request.method, headers, body })) +}) +server.listen(0, '127.0.0.1')` + ) + const { stdout } = await run({ ORIGIN: 'https://ambient.example' }, [ + '--input-type=module', + '--eval', + `import { request } from 'node:http' +import { once } from 'node:events' +const { startServer } = await import(${JSON.stringify(pathToFileURL(entry).href)}) +const server = await startServer(${JSON.stringify(compiledEntry)}) +const compiled = await import(${JSON.stringify(pathToFileURL(compiledEntry).href)}) +if (server !== compiled.server) throw new Error('Native server was replaced') +try { + if (!server.listening) await once(server, 'listening') + const results = [] + for (const origin of ['https://browser.example', 'null']) { + results.push(await new Promise((resolve, reject) => { + const outgoing = request({ + hostname: '127.0.0.1', port: server.address().port, agent: false, + method: 'POST', path: '/submit?next=%2Fprivate&tag=a&tag=b', + headers: { + host: 'internal.example:3000', origin, cookie: 'session=unchanged', + 'content-type': 'application/x-www-form-urlencoded', + 'x-forwarded-proto': 'http', 'x-forwarded-host': 'spoofed.example:9999', + 'x-forwarded-port': '9999', 'x-luzzle-public-protocol': 'http', + 'x-luzzle-public-authority': 'spoofed.example:9999' + } + }, async (response) => { + try { + let body = '' + for await (const chunk of response) body += chunk + resolve(JSON.parse(body)) + } catch (error) { reject(error) } + }) + outgoing.on('error', reject) + outgoing.end('title=one%26two&tag=a&tag=b') + })) + } + console.log(JSON.stringify(results)) +} finally { + await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) +}` + ]) + const canonical = new URL(app) + const results = JSON.parse(stdout) + expect(results).toHaveLength(2) + for (const [index, origin] of ['https://browser.example', 'null'].entries()) { + expect(results[index]).toEqual({ + url: `${canonical.origin}/submit?next=%2Fprivate&tag=a&tag=b`, + method: 'POST', + headers: expect.objectContaining({ + host: 'internal.example:3000', + origin, + cookie: 'session=unchanged', + 'content-type': 'application/x-www-form-urlencoded', + 'x-forwarded-proto': 'http', + 'x-forwarded-host': 'spoofed.example:9999', + 'x-forwarded-port': '9999', + 'x-luzzle-public-protocol': canonical.protocol.slice(0, -1), + 'x-luzzle-public-authority': canonical.host + }), + body: 'title=one%26two&tag=a&tag=b' + }) + } + }, + 15000 +) + test('authentication requires an absolute configured app URL before server import', async () => { await configure('/garden') - await expect(run({ ORIGIN: 'https://unexpected.example' })).rejects.toMatchObject({ + await expect( + run({ + ORIGIN: 'https://unexpected.example', + BETTER_AUTH_URL: 'https://unexpected.example', + AUTH_URL: 'https://legacy.example' + }) + ).rejects.toMatchObject({ code: 1, stdout: '', stderr: expect.stringContaining('Authentication requires an absolute url.app') diff --git a/apps/web/src/app.d.ts b/apps/web/src/app.d.ts index d2ea8b3d..da577a33 100644 --- a/apps/web/src/app.d.ts +++ b/apps/web/src/app.d.ts @@ -1,5 +1,5 @@ -import { type AppConfigPublic } from '$lib/server/config' -import type { LuzzleSession as Session } from '$lib/server/auth' +import { type AppConfigPublic } from '#lib/server/config.js' +import type { LuzzleSession as Session } from '#lib/server/auth/index.js' // See https://kit.svelte.dev/docs/types#app // for information about these interfaces diff --git a/apps/web/src/hooks.server.test.ts b/apps/web/src/hooks.server.test.ts index 673245bd..e317ee81 100644 --- a/apps/web/src/hooks.server.test.ts +++ b/apps/web/src/hooks.server.test.ts @@ -1,7 +1,7 @@ +import type { Handle } from '@sveltejs/kit/hooks' import { afterEach, beforeEach, expect, test, vi } from 'vitest' -import type { Handle } from '@sveltejs/kit' import type { Config } from '@luzzle/web.config' -import { credentialsAuth, oidcAuth } from '$lib/server/config.fixture' +import { credentialsAuth, oidcAuth } from '#lib/server/config.fixture.js' const fixture = vi.hoisted(() => ({ building: false, @@ -15,18 +15,18 @@ const fixture = vi.hoisted(() => ({ createAuth: vi.fn(() => ({ id: 'native-auth' })), createAuthHandle: vi.fn(() => (({ event, resolve }) => resolve(event)) satisfies Handle) })) -vi.mock('$app/environment', () => ({ +vi.mock('$app/env', () => ({ get building() { return fixture.building } })) -vi.mock('$lib/server/config', () => ({ config: fixture.config })) -vi.mock('$lib/server/configured-styles', () => ({ +vi.mock('#lib/server/config.js', () => ({ config: fixture.config })) +vi.mock('#lib/server/configured-styles.js', () => ({ prepareConfiguredStyles: fixture.prepareConfiguredStyles, renderConfiguredStyles: ({ event, resolve }: Parameters[0]) => resolve(event) })) -vi.mock('$lib/server/database', () => ({})) -vi.mock('$lib/server/asset-csp', () => ({ +vi.mock('#lib/server/database/index.js', () => ({})) +vi.mock('#lib/server/asset-csp.js', () => ({ createAssetCspHandle: () => ({ event, resolve }: Parameters[0]) => @@ -36,10 +36,11 @@ vi.mock('$lib/server/asset-csp', () => ({ vi.mock('@sveltejs/kit/hooks', () => ({ sequence: (...handles: Handle[]) => handles[handles.length - 1] })) -vi.mock('$lib/server/auth', () => ({ +vi.mock('#lib/server/auth/index.js', () => ({ createAuth: fixture.createAuth, createAuthHandle: fixture.createAuthHandle })) + vi.mock('../scripts/runtime-environment.js', () => ({ resolveAuthUrl: () => fixture.config.url.app })) @@ -49,6 +50,7 @@ beforeEach(() => { fixture.config.auth = credentialsAuth vi.clearAllMocks() }) + afterEach(() => vi.resetModules()) function event( @@ -85,12 +87,12 @@ function event( } test('awaits configured CSS once when the server hooks load, not per request', async () => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') expect(fixture.prepareConfiguredStyles).toHaveBeenCalledExactlyOnceWith(fixture.config) for (let request = 0; request < 2; request++) { await handle({ event: event(null, '/', { routeId: '/(root)' }), - resolve: vi.fn().mockResolvedValue(new Response('public')) + resolve: vi.fn[0]['resolve']>().mockResolvedValue(new Response('public')) }) } expect(fixture.prepareConfiguredStyles).toHaveBeenCalledTimes(1) @@ -98,7 +100,7 @@ test('awaits configured CSS once when the server hooks load, not per request', a test('does not prepare configured CSS during framework building', async () => { fixture.building = true - await import('./hooks.server') + await import('./hooks.server.js') expect(fixture.prepareConfiguredStyles).not.toHaveBeenCalled() expect(fixture.createAuth).not.toHaveBeenCalled() expect(fixture.createAuthHandle).toHaveBeenCalledExactlyOnceWith(undefined, true) @@ -106,19 +108,22 @@ test('does not prepare configured CSS during framework building', async () => { test('failed configured CSS preparation rejects server hook loading', async () => { const failure = new Error('Configured CSS preparation failed') + fixture.prepareConfiguredStyles.mockRejectedValueOnce(failure) - await expect(import('./hooks.server')).rejects.toBe(failure) + await expect(import('./hooks.server.js')).rejects.toBe(failure) }) test.each(['/admin', '/admin/pieces', '/api/admin', '/api/admin/generate', '/admin/lsp'])( 'the protected route %s requires a session before resolving its handler', async (pathname) => { - const { handle } = await import('./hooks.server') - const resolve = vi.fn() + const { handle } = await import('./hooks.server.js') + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) + await expect(handle({ event: event(null, pathname), resolve })).rejects.toMatchObject({ status: 302, location: `/signin?${new URLSearchParams({ redirectTo: pathname })}` }) + expect(resolve).not.toHaveBeenCalled() } ) @@ -127,9 +132,11 @@ test.each(['/admin', '/admin/lsp', '/api/admin/generate'])( '%s remains inaccessible without configured auth, even with a session', async (pathname) => { fixture.config.auth = undefined - const { handle } = await import('./hooks.server') - const resolve = vi.fn() + + const { handle } = await import('./hooks.server.js') + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) const request = event({ user: { name: 'test' } }, pathname) + await expect(handle({ event: request, resolve })).rejects.toMatchObject({ status: 302, location: '/' @@ -142,12 +149,14 @@ test.each(['/admin', '/admin/lsp', '/api/admin/generate'])( test('public-only requests resolve without constructing native auth or reading a session', async () => { fixture.config.auth = undefined - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const request = event(null, '/', { routeId: '/(root)' }) const response = new Response('public', { headers: { 'Cache-Control': 'public, max-age=31536000, immutable' } }) - const resolve = vi.fn().mockResolvedValue(response) + + const resolve = vi.fn[0]['resolve']>().mockResolvedValue(response) + await expect(handle({ event: request, resolve })).resolves.toBe(response) expect(request.locals.auth).not.toHaveBeenCalled() expect(request.setHeaders).not.toHaveBeenCalled() @@ -158,21 +167,26 @@ test('public-only requests resolve without constructing native auth or reading a test.each([false, true])( 'signin preserves an encoded path and its query as one return parameter (data=%s)', async (isDataRequest) => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const pathname = '/garden/%61dmin/piece/books/%E6%BA%90%20%23%3F%25.md/published' const search = '?tag=a%26b&filter=a+b&redirectTo=https%3A%2F%2Fexternal.example' - const resolve = vi.fn() + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) + const request = event(null, pathname, { routeId: '/(auth)/admin/piece/[...path]/published', isDataRequest, search, headers: { accept: isDataRequest ? 'application/json' : 'text/html' } }) + const failure = await Promise.resolve(handle({ event: request, resolve })).catch( (error) => error ) + expect(failure).toMatchObject({ status: 302, location: expect.any(String) }) + const signin = new URL(failure.location, request.url.origin) + expect(signin.pathname).toBe('/signin') expect([...signin.searchParams]).toEqual([['redirectTo', pathname + search]]) expect(resolve).not.toHaveBeenCalled() @@ -182,23 +196,27 @@ test.each([false, true])( test.each(['/admin', '/admin/preview/job/asset/private.png', '/api/admin/generate'])( '%s sets the framework cache policy before resolving protected content', async (pathname) => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const request = event({ user: { name: 'test' } }, pathname) - const resolve = vi.fn(async () => { + + const resolve = vi.fn[0]['resolve']>(async () => { expect(request.setHeaders).toHaveBeenCalledExactlyOnceWith({ 'Cache-Control': 'private, no-store' }) + return new Response('private') }) + await handle({ event: request, resolve }) expect(resolve).toHaveBeenCalledOnce() } ) test('an authenticated generation request reaches its handler', async () => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const response = new Response('resolved') - const resolve = vi.fn().mockResolvedValue(response) + const resolve = vi.fn[0]['resolve']>().mockResolvedValue(response) + await expect(handle({ event: event({ user: { name: 'test' } }), resolve })).resolves.toBe( response ) @@ -229,8 +247,8 @@ const encodedRequests = [ test.each(encodedRequests)( '$method $pathname (data=$isDataRequest) requires a session by matched route identity', async ({ pathname, ...options }) => { - const { handle } = await import('./hooks.server') - const resolve = vi.fn() + const { handle } = await import('./hooks.server.js') + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) const request = event(null, pathname, options) if (options.method === 'GET') { await expect(handle({ event: request, resolve })).rejects.toMatchObject({ @@ -243,6 +261,7 @@ test.each(encodedRequests)( expect(response.headers.has('location')).toBe(false) expect(await response.json()).toEqual({ message: 'Unauthorized' }) } + expect(request.locals.auth).toHaveBeenCalledOnce() expect(resolve).not.toHaveBeenCalled() } @@ -252,9 +271,11 @@ test.each(encodedRequests)( '$pathname remains inaccessible with auth disabled, even with a session (data=$isDataRequest)', async ({ pathname, ...options }) => { fixture.config.auth = undefined - const { handle } = await import('./hooks.server') - const resolve = vi.fn() + + const { handle } = await import('./hooks.server.js') + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) const request = event({ user: { name: 'test' } }, pathname, options) + if (options.method === 'GET') { await expect(handle({ event: request, resolve })).rejects.toMatchObject({ status: 302, @@ -262,10 +283,12 @@ test.each(encodedRequests)( }) } else { const response = await handle({ event: request, resolve }) + expect(response.status).toBe(403) expect(response.headers.has('location')).toBe(false) expect(await response.json()).toEqual({ message: 'Forbidden' }) } + expect(request.locals.auth).not.toHaveBeenCalled() expect(resolve).not.toHaveBeenCalled() } @@ -274,10 +297,11 @@ test.each(encodedRequests)( test.each(encodedRequests)( 'authenticated $pathname resolves normally (data=$isDataRequest)', async ({ pathname, ...options }) => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const response = new Response('resolved') - const resolve = vi.fn().mockResolvedValue(response) + const resolve = vi.fn[0]['resolve']>().mockResolvedValue(response) const request = event({ user: { name: 'test' } }, pathname, options) + await expect(handle({ event: request, resolve })).resolves.toBe(response) expect(request.locals.auth).toHaveBeenCalledOnce() expect(resolve).toHaveBeenCalledExactlyOnceWith(request) @@ -295,10 +319,11 @@ test.each([ { pathname: '/admin-looking-group', routeId: '/(auth)extra/admin' }, { pathname: '/admin/not-a-route', routeId: null } ])('public/unmatched $routeId is not classified by URL spelling', async ({ pathname, routeId }) => { - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const response = new Response('public or not found') - const resolve = vi.fn().mockResolvedValue(response) + const resolve = vi.fn[0]['resolve']>().mockResolvedValue(response) const request = event(null, pathname, { routeId }) + await expect(handle({ event: request, resolve })).resolves.toBe(response) expect(request.locals.auth).not.toHaveBeenCalled() expect(resolve).toHaveBeenCalledExactlyOnceWith(request) @@ -308,7 +333,9 @@ test.each([true, false])( 'non-navigation requests get generic errors (auth=%s)', async (enabled) => { fixture.config.auth = enabled ? credentialsAuth : undefined - const { handle } = await import('./hooks.server') + + const { handle } = await import('./hooks.server.js') + for (const pathname of ['/admin/tools', '/api/admin/generate', '/future-tool']) { for (const [method, accept] of [ ['POST', 'application/json'], @@ -318,7 +345,9 @@ test.each([true, false])( ['HEAD', 'text/html'] ]) { const request = event(null, pathname, { method, headers: { accept } }) - const resolve = vi.fn() + const resolve = vi.fn[0]['resolve']>( + async () => new Response('resolved') + ) const response = await handle({ event: request, resolve }) expect(response.status).toBe(enabled ? 401 : 403) expect(response.headers.get('Cache-Control')).toBe('private, no-store') @@ -335,17 +364,18 @@ test.each([true, false])( 'enhanced forms keep their existing redirects (auth=%s)', async (enabled) => { fixture.config.auth = enabled ? credentialsAuth : undefined - const { handle } = await import('./hooks.server') + const { handle } = await import('./hooks.server.js') const pathname = '/admin/piece/books/book.books.md/source' const request = event(null, pathname, { method: 'POST', headers: { accept: 'application/json', 'x-sveltekit-action': 'true' } }) - const resolve = vi.fn() + const resolve = vi.fn[0]['resolve']>(async () => new Response('resolved')) await expect(handle({ event: request, resolve })).rejects.toMatchObject({ status: 302, location: enabled ? `/signin?${new URLSearchParams({ redirectTo: pathname })}` : '/' }) + expect(resolve).not.toHaveBeenCalled() } ) @@ -355,6 +385,6 @@ test.each([ { auth: oidcAuth, provider: 'oidc' } ])('initializes the native core with the configured $provider provider', async ({ auth }) => { fixture.config.auth = auth - await import('./hooks.server') + await import('./hooks.server.js') expect(fixture.createAuth).toHaveBeenCalledExactlyOnceWith(auth, fixture.config.url.app) }) diff --git a/apps/web/src/hooks.server.ts b/apps/web/src/hooks.server.ts index edace10c..9854f2dc 100644 --- a/apps/web/src/hooks.server.ts +++ b/apps/web/src/hooks.server.ts @@ -1,12 +1,12 @@ -import { building } from '$app/environment' -import { config } from '$lib/server/config' -import { createAuth, createAuthHandle } from '$lib/server/auth' +import { json, redirect } from '@sveltejs/kit' +import { building } from '$app/env' +import { config } from '#lib/server/config.js' +import { createAuth, createAuthHandle } from '#lib/server/auth/index.js' import { resolveAuthUrl } from '../scripts/runtime-environment.js' -import { createAssetCspHandle } from '$lib/server/asset-csp' -import { prepareConfiguredStyles, renderConfiguredStyles } from '$lib/server/configured-styles' -import '$lib/server/database' -import { sequence } from '@sveltejs/kit/hooks' -import { json, redirect, type Handle } from '@sveltejs/kit' +import { createAssetCspHandle } from '#lib/server/asset-csp.js' +import { prepareConfiguredStyles, renderConfiguredStyles } from '#lib/server/configured-styles.js' +import '#lib/server/database/index.js' +import { sequence, type Handle } from '@sveltejs/kit/hooks' async function prepareStartupStyles() { if (building) return '' @@ -24,8 +24,8 @@ function createAssetPolicy(): Handle { const configuredStyles = await prepareStartupStyles() const handleAssetCsp = createAssetPolicy() - const auth = config.auth + function initializeAuth() { if (building || !auth) return undefined return createAuth(auth, resolveAuthUrl(config.url.app)) @@ -40,7 +40,9 @@ const guardHandle: Handle = async ({ event, resolve }) => { if (!isProtectedRoute(event.route.id)) return resolve(event) const headers = { 'Cache-Control': 'private, no-store' } + event.setHeaders(headers) + const { request } = event const browserGet = request.method === 'GET' && @@ -55,10 +57,12 @@ const guardHandle: Handle = async ({ event, resolve }) => { } const session = await event.locals.auth() - if (session) return resolve(event) + if (session) return resolve(event) if (!shouldRedirect) return json({ message: 'Unauthorized' }, { status: 401, headers }) + const params = new URLSearchParams({ redirectTo: event.url.pathname + event.url.search }) + throw redirect(302, `/signin?${params}`) } diff --git a/apps/web/src/lib/components/editor/GenerationDialog.svelte b/apps/web/src/lib/components/editor/GenerationDialog.svelte index 7863ef1f..d94f02df 100644 --- a/apps/web/src/lib/components/editor/GenerationDialog.svelte +++ b/apps/web/src/lib/components/editor/GenerationDialog.svelte @@ -3,9 +3,9 @@ import { onDestroy, tick, untrack } from 'svelte' import { fade, fly } from 'svelte/transition' import CircleNotch from 'virtual:icons/ph/circle-notch-bold' - import Button from '$lib/components/ui/Button.svelte' + import Button from '#lib/components/ui/Button.svelte' import GenerationFields from './GenerationFields.svelte' - import { startGeneration } from '$lib/generation/client' + import { startGeneration } from '#lib/generation/client.js' type Props = { open: boolean diff --git a/apps/web/src/lib/components/editor/PieceToolbar.svelte b/apps/web/src/lib/components/editor/PieceToolbar.svelte index c3264448..ad2f2e9d 100644 --- a/apps/web/src/lib/components/editor/PieceToolbar.svelte +++ b/apps/web/src/lib/components/editor/PieceToolbar.svelte @@ -1,7 +1,7 @@ diff --git a/apps/web/src/lib/components/layout/simple/ThemeToggle.svelte b/apps/web/src/lib/components/layout/simple/ThemeToggle.svelte index e14217bc..8851ac73 100644 --- a/apps/web/src/lib/components/layout/simple/ThemeToggle.svelte +++ b/apps/web/src/lib/components/layout/simple/ThemeToggle.svelte @@ -2,7 +2,7 @@ import SunIcon from 'virtual:icons/ph/sun-dim' import MoonIcon from 'virtual:icons/ph/moon' import FaderIcon from 'virtual:icons/ph/faders' - import themes, { type Theme } from '$lib/ui/styles/themes' + import themes, { type Theme } from '#lib/ui/styles/themes.js' import { onMount } from 'svelte' function getThemePreference(): Theme { diff --git a/apps/web/src/lib/content/components/ContentBanner.svelte b/apps/web/src/lib/content/components/ContentBanner.svelte index 4ee4a4e2..70b5a5e6 100644 --- a/apps/web/src/lib/content/components/ContentBanner.svelte +++ b/apps/web/src/lib/content/components/ContentBanner.svelte @@ -1,6 +1,6 @@ diff --git a/apps/web/src/lib/pieces/viewer/fixtures/IconRenderer.svelte b/apps/web/src/lib/pieces/viewer/fixtures/IconRenderer.svelte index ac5b2a38..653212f2 100644 --- a/apps/web/src/lib/pieces/viewer/fixtures/IconRenderer.svelte +++ b/apps/web/src/lib/pieces/viewer/fixtures/IconRenderer.svelte @@ -1,6 +1,6 @@ "' + config.theme.markdown.sidenote['sidenote-callout-before-content'] = content config.theme.globals['font-sans-name'] = '"Custom < Sans"' + const html = await renderChunks(await prepareConfiguredStyles(config), [document]) expect(html.match(/<\/style/gi)).toHaveLength(1) @@ -142,7 +155,8 @@ test('leaves non-HTML bodies and framework cache/CSP headers unchanged', async ( 'content-security-policy': "script-src 'self' 'nonce-framework'" } }) - const resolve = vi.fn().mockResolvedValue(response) + + const resolve = vi.fn[0]['resolve']>().mockResolvedValue(response) expect(await renderConfiguredStyles({ event, resolve }, styles)).toBe(response) expect(typeof event.locals.inlineConfiguredStyles).toBe('function') diff --git a/apps/web/src/lib/server/configured-styles.ts b/apps/web/src/lib/server/configured-styles.ts index bfdad8b3..7d8219aa 100644 --- a/apps/web/src/lib/server/configured-styles.ts +++ b/apps/web/src/lib/server/configured-styles.ts @@ -1,6 +1,6 @@ +import type { Handle } from '@sveltejs/kit/hooks' import { generateThemeCss } from '@luzzle/web.theme' import { generateEditorThemeCss } from '@luzzle/web.theme/editor' -import type { Handle } from '@sveltejs/kit' import type { AppConfig } from './config' export async function prepareConfiguredStyles(config: AppConfig): Promise { diff --git a/apps/web/src/lib/server/database/index.ts b/apps/web/src/lib/server/database/index.ts index 4921595d..7b638638 100644 --- a/apps/web/src/lib/server/database/index.ts +++ b/apps/web/src/lib/server/database/index.ts @@ -1,5 +1,5 @@ -import { building } from '$app/environment' -import { config } from '$lib/server/config' +import { building } from '$app/env' +import { config } from '#lib/server/config.js' import { sql } from '@luzzle/core' import { runWebMigrations, createAppDb } from '@luzzle/web.db' import type { WebDatabase } from '@luzzle/web.db' diff --git a/apps/web/src/lib/server/formData.ts b/apps/web/src/lib/server/formData.ts index 2c78ce0c..e82a2084 100644 --- a/apps/web/src/lib/server/formData.ts +++ b/apps/web/src/lib/server/formData.ts @@ -6,7 +6,7 @@ import { type PieceMarkdown, findFrontmatterField } from '@luzzle/core' -import { normalizeMarkdown } from '$lib/server/markdown' +import { normalizeMarkdown } from '#lib/server/markdown.js' type FormOperations = { removes: Set diff --git a/apps/web/src/lib/server/generation.test.ts b/apps/web/src/lib/server/generation.test.ts index bf597e28..7c5354a2 100644 --- a/apps/web/src/lib/server/generation.test.ts +++ b/apps/web/src/lib/server/generation.test.ts @@ -6,7 +6,7 @@ import { type PieceFrontmatter, type PieceFrontmatterSchema } from '@luzzle/core' -import type { GenerationEvent } from '$lib/generation/types' +import type { GenerationEvent } from '#lib/generation/types.js' import { POST } from '../../routes/(auth)/api/admin/generate/+server' const fixture = vi.hoisted(() => { @@ -58,7 +58,7 @@ vi.mock('../../../../core/node_modules/@google/genai', async (original) => ({ } }) })) -vi.mock('$lib/server/database', () => { +vi.mock('#lib/server/database/index.js', () => { throw new Error('Generation must not import the database') }) vi.mock('@luzzle/web.jobs', () => { diff --git a/apps/web/src/lib/server/generation.ts b/apps/web/src/lib/server/generation.ts index 756dae2b..2201b9e0 100644 --- a/apps/web/src/lib/server/generation.ts +++ b/apps/web/src/lib/server/generation.ts @@ -8,7 +8,7 @@ import { type PieceFrontmatter, type PieceFrontmatterSchema } from '@luzzle/core' -import type { GenerationResult } from '$lib/generation/types' +import type { GenerationResult } from '#lib/generation/types.js' import { config } from './config' import { getPieces } from './pieces' import { diff --git a/apps/web/src/lib/server/generation/stream.test.ts b/apps/web/src/lib/server/generation/stream.test.ts index 6e80ac42..1b673563 100644 --- a/apps/web/src/lib/server/generation/stream.test.ts +++ b/apps/web/src/lib/server/generation/stream.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { DEFAULT_GENERATION_LIMITS, GenerationValidationError } from '@luzzle/core' -import type { GenerationEvent, GenerationResult } from '$lib/generation/types' +import type { GenerationEvent, GenerationResult } from '#lib/generation/types.js' import { createGenerationStream } from './stream' const result: GenerationResult = { markdown: 'café\n🧩 日本語' } diff --git a/apps/web/src/lib/server/generation/stream.ts b/apps/web/src/lib/server/generation/stream.ts index dc3910d2..9680c554 100644 --- a/apps/web/src/lib/server/generation/stream.ts +++ b/apps/web/src/lib/server/generation/stream.ts @@ -3,8 +3,8 @@ import { GenerationValidationError, type GenerationProgress } from '@luzzle/core' -import type { ProgressPhase } from '$lib/components/progress' -import type { GenerationEvent, GenerationResult } from '$lib/generation/types' +import type { ProgressPhase } from '#lib/components/progress.js' +import type { GenerationEvent, GenerationResult } from '#lib/generation/types.js' import { createEventStream } from '../sse' export function createGenerationStream(request: Request) { diff --git a/apps/web/src/lib/server/health/ai.ts b/apps/web/src/lib/server/health/ai.ts index 41a36705..efd28384 100644 --- a/apps/web/src/lib/server/health/ai.ts +++ b/apps/web/src/lib/server/health/ai.ts @@ -1,5 +1,5 @@ import { validateApiKey } from '@luzzle/core' -import type { AppConfig } from '$lib/server/config' +import type { AppConfig } from '#lib/server/config.js' import type { ProbeResult } from './probes.js' export async function validateAiKeyIfConfigured(config: AppConfig): Promise { diff --git a/apps/web/src/lib/server/health/connectivity.test.ts b/apps/web/src/lib/server/health/connectivity.test.ts index 413136df..3ac42500 100644 --- a/apps/web/src/lib/server/health/connectivity.test.ts +++ b/apps/web/src/lib/server/health/connectivity.test.ts @@ -1,9 +1,9 @@ import { describe, test, expect, vi, beforeEach } from 'vitest' -import { getOpenWorkflow, getOpenWorkflowBackend } from '$lib/server/workflow/index.js' +import { getOpenWorkflow, getOpenWorkflowBackend } from '#lib/server/workflow/index.js' import { getWorkflowRun } from '@luzzle/web.jobs' import { testConnectivity } from './connectivity.js' -vi.mock('$lib/server/workflow/index.js', () => ({ +vi.mock('#lib/server/workflow/index.js', () => ({ getOpenWorkflow: vi.fn(), getOpenWorkflowBackend: vi.fn() })) diff --git a/apps/web/src/lib/server/health/connectivity.ts b/apps/web/src/lib/server/health/connectivity.ts index 65d07a9a..dec5c48f 100644 --- a/apps/web/src/lib/server/health/connectivity.ts +++ b/apps/web/src/lib/server/health/connectivity.ts @@ -1,4 +1,4 @@ -import { getOpenWorkflow, getOpenWorkflowBackend } from '$lib/server/workflow/index.js' +import { getOpenWorkflow, getOpenWorkflowBackend } from '#lib/server/workflow/index.js' import { getWorkflowRun, type TestConnectivityResult } from '@luzzle/web.jobs' import { testConnectivitySpec } from '@luzzle/web.jobs/specs' diff --git a/apps/web/src/lib/server/load/dashboard.test.ts b/apps/web/src/lib/server/load/dashboard.test.ts index 6e590d1a..3f5827f8 100644 --- a/apps/web/src/lib/server/load/dashboard.test.ts +++ b/apps/web/src/lib/server/load/dashboard.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect, vi, beforeEach } from 'vitest' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { getPieceStats, getAssetStats } from '../dashboardStats.js' import { getRecentlyEditedPieces } from '../pieces.js' import { getOpenWorkflowBackend } from '../workflow/index.js' @@ -7,10 +7,10 @@ import { getLatestWorkflowRun, type WorkflowRunRow } from '@luzzle/web.jobs' import { makeConfig, credentialsAuth, oidcAuth } from '../config.fixture' import { loadDashboardPage } from './dashboard.js' -vi.mock('$lib/server/database', () => ({ db: {} })) +vi.mock('#lib/server/database/index.js', () => ({ db: {} })) -vi.mock('$lib/server/config', async () => { - const { makeConfig, oidcAuth } = await import('../config.fixture') +vi.mock('#lib/server/config.js', async () => { + const { makeConfig, oidcAuth } = await import('../config.fixture.js') const defaults = makeConfig() return { config: makeConfig({ diff --git a/apps/web/src/lib/server/load/dashboard.ts b/apps/web/src/lib/server/load/dashboard.ts index a41fdb7a..b4559a9d 100644 --- a/apps/web/src/lib/server/load/dashboard.ts +++ b/apps/web/src/lib/server/load/dashboard.ts @@ -1,5 +1,5 @@ -import { db } from '$lib/server/database' -import { config } from '$lib/server/config' +import { db } from '#lib/server/database/index.js' +import { config } from '#lib/server/config.js' import { getPieceStats, getAssetStats, type PieceStats } from '../dashboardStats.js' import { getRecentlyEditedPieces } from '../pieces.js' import { getOpenWorkflowBackend } from '../workflow/index.js' diff --git a/apps/web/src/lib/server/load/health.test.ts b/apps/web/src/lib/server/load/health.test.ts index b4a70802..18c1bc09 100644 --- a/apps/web/src/lib/server/load/health.test.ts +++ b/apps/web/src/lib/server/load/health.test.ts @@ -1,13 +1,13 @@ import { describe, test, expect, vi, beforeEach } from 'vitest' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { makeConfig, credentialsAuth, oidcAuth } from '../config.fixture' import { buildHealthConfigSummary } from '../health.js' import type * as Health from '../health.js' import { probeStorage, probeWorker, probeOidcIssuer } from '../health/probes.js' import { loadHealthPage, probeWorkerIfConfigured, probeOidcIssuerIfConfigured } from './health.js' -vi.mock('$lib/server/config', async () => { - const { makeConfig } = await import('../config.fixture') +vi.mock('#lib/server/config.js', async () => { + const { makeConfig } = await import('../config.fixture.js') return { config: makeConfig() } }) diff --git a/apps/web/src/lib/server/load/health.ts b/apps/web/src/lib/server/load/health.ts index 849cc56e..76da5f32 100644 --- a/apps/web/src/lib/server/load/health.ts +++ b/apps/web/src/lib/server/load/health.ts @@ -1,5 +1,5 @@ -import type { AppConfig } from '$lib/server/config' -import { config } from '$lib/server/config' +import type { AppConfig } from '#lib/server/config.js' +import { config } from '#lib/server/config.js' import { buildHealthConfigSummary, type HealthConfigSummary } from '../health.js' import { probeStorage, probeWorker, probeOidcIssuer, type ProbeResult } from '../health/probes.js' diff --git a/apps/web/src/lib/server/pages.test.ts b/apps/web/src/lib/server/pages.test.ts index bdb70f89..1535e9e4 100644 --- a/apps/web/src/lib/server/pages.test.ts +++ b/apps/web/src/lib/server/pages.test.ts @@ -5,8 +5,8 @@ const { config, loadMarkdownFile } = vi.hoisted(() => ({ config: { paths: { config: '/deployment/config/config.yaml' } }, loadMarkdownFile: vi.fn() })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/content/markdown/files.server.js', () => ({ loadMarkdownFile })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/content/markdown/files.server.js', () => ({ loadMarkdownFile })) beforeEach(() => { vi.clearAllMocks() diff --git a/apps/web/src/lib/server/pages.ts b/apps/web/src/lib/server/pages.ts index 288c091f..9f14271e 100644 --- a/apps/web/src/lib/server/pages.ts +++ b/apps/web/src/lib/server/pages.ts @@ -1,6 +1,6 @@ import path from 'node:path' -import { config } from '$lib/server/config' -import { loadMarkdownFile } from '$lib/content/markdown/files.server.js' +import { config } from '#lib/server/config.js' +import { loadMarkdownFile } from '#lib/content/markdown/files.server.js' export function loadPageMarkdown(file?: string): Promise { if (file === undefined) return Promise.resolve(undefined) diff --git a/apps/web/src/lib/server/pieces.test.ts b/apps/web/src/lib/server/pieces.test.ts index b31bf36c..b7ac7a99 100644 --- a/apps/web/src/lib/server/pieces.test.ts +++ b/apps/web/src/lib/server/pieces.test.ts @@ -3,8 +3,8 @@ import { createAppDb, runWebMigrations } from '@luzzle/web.db' import { getWebPiece, getRecentlyEditedPieces, getRecentPieces, getRandomPieces } from './pieces.js' // pieces.js pulls in getPieces()'s module-level dependencies on import -vi.mock('$lib/server/config', () => ({ config: {} })) -vi.mock('$lib/server/storage', () => ({ getStorage: vi.fn() })) +vi.mock('#lib/server/config.js', () => ({ config: {} })) +vi.mock('#lib/server/storage.js', () => ({ getStorage: vi.fn() })) async function createTestDb() { const db = createAppDb(':memory:') diff --git a/apps/web/src/lib/server/storage.ts b/apps/web/src/lib/server/storage.ts index 2d4ed67e..b222c319 100644 --- a/apps/web/src/lib/server/storage.ts +++ b/apps/web/src/lib/server/storage.ts @@ -1,4 +1,4 @@ -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { StorageFileSystem, type LuzzleStorage } from '@luzzle/core' let storage: LuzzleStorage | null = null diff --git a/apps/web/src/lib/server/workflow/index.test.ts b/apps/web/src/lib/server/workflow/index.test.ts index 8dd202cf..14654e7d 100644 --- a/apps/web/src/lib/server/workflow/index.test.ts +++ b/apps/web/src/lib/server/workflow/index.test.ts @@ -1,11 +1,11 @@ import { expect, test, vi } from 'vitest' import { publishSpec } from '@luzzle/web.jobs/specs' -import { config } from '$lib/server/config.js' +import { config } from '#lib/server/config.js' import { getOpenWorkflow, getOpenWorkflowBackend } from './index.js' import { createWorkflowQueue, closeWorkflowQueue } from './publish.fixture.js' -vi.mock('$lib/server/config.js', async () => { - const { makeConfig } = await import('$lib/server/config.fixture.js') +vi.mock('#lib/server/config.js', async () => { + const { makeConfig } = await import('#lib/server/config.fixture.js') return { config: makeConfig() } }) diff --git a/apps/web/src/lib/server/workflow/index.ts b/apps/web/src/lib/server/workflow/index.ts index 3848786f..542912c8 100644 --- a/apps/web/src/lib/server/workflow/index.ts +++ b/apps/web/src/lib/server/workflow/index.ts @@ -1,7 +1,7 @@ import path from 'node:path' import { OpenWorkflow } from 'openworkflow' import { BackendSqlite } from 'openworkflow/sqlite' -import { config } from '$lib/server/config.js' +import { config } from '#lib/server/config.js' let openWorkflowInstance: OpenWorkflow | null = null let openWorkflowBackendInstance: BackendSqlite | null = null diff --git a/apps/web/src/lib/server/workflow/stream.test.ts b/apps/web/src/lib/server/workflow/stream.test.ts index b0f6ce63..50689063 100644 --- a/apps/web/src/lib/server/workflow/stream.test.ts +++ b/apps/web/src/lib/server/workflow/stream.test.ts @@ -19,7 +19,7 @@ const mocks = vi.hoisted(() => { } }) -vi.mock('$lib/server/database/index.js', () => ({ +vi.mock('#lib/server/database/index.js', () => ({ db: { selectFrom: mocks.selectFrom } })) vi.mock('./index.js', () => ({ diff --git a/apps/web/src/lib/server/workflow/stream.ts b/apps/web/src/lib/server/workflow/stream.ts index fe4aa6dd..20ac34ef 100644 --- a/apps/web/src/lib/server/workflow/stream.ts +++ b/apps/web/src/lib/server/workflow/stream.ts @@ -1,4 +1,4 @@ -import { db, type JobProgressRow } from '$lib/server/database/index.js' +import { db, type JobProgressRow } from '#lib/server/database/index.js' import { createEventStream } from '../sse.js' import { WORKFLOW_POLL_INTERVAL_MS } from '../constants.js' import { getOpenWorkflowBackend } from './index.js' diff --git a/apps/web/src/params.ts b/apps/web/src/params.ts new file mode 100644 index 00000000..9aee2840 --- /dev/null +++ b/apps/web/src/params.ts @@ -0,0 +1,19 @@ +import { defineParams } from '@sveltejs/kit/params' + +function matchFeedFormat(param: string): param is 'json' | 'xml' | 'md' { + return /(json|xml|md)/.test(param) +} + +function matchOpenGraphFormat(param: string): param is 'svg' | 'png' { + return /(svg|png)/.test(param) +} + +function matchVariantFormat(param: string): param is 'jpg' | 'avif' | 'webp' | 'png' { + return /(avif|jpg|webp|png)/.test(param) +} + +export const params = defineParams({ + feedFormat: (param) => (matchFeedFormat(param) ? param : undefined), + openGraphFormat: (param) => (matchOpenGraphFormat(param) ? param : undefined), + variantFormat: (param) => (matchVariantFormat(param) ? param : undefined) +}) diff --git a/apps/web/src/params/feedFormat.ts b/apps/web/src/params/feedFormat.ts deleted file mode 100644 index c14b8189..00000000 --- a/apps/web/src/params/feedFormat.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function match(param: string): param is 'json' | 'xml' | 'md' { - return /(json|xml|md)/.test(param) -} diff --git a/apps/web/src/params/openGraphFormat.ts b/apps/web/src/params/openGraphFormat.ts deleted file mode 100644 index 81e02756..00000000 --- a/apps/web/src/params/openGraphFormat.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function match(param: string): param is 'svg' | 'png' { - return /(svg|png)/.test(param) -} diff --git a/apps/web/src/params/variantFormat.ts b/apps/web/src/params/variantFormat.ts deleted file mode 100644 index 5f556a15..00000000 --- a/apps/web/src/params/variantFormat.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function match(param: string): param is 'jpg' | 'avif' | 'webp' | 'png' { - return /(avif|jpg|webp|png)/.test(param) -} diff --git a/apps/web/src/routes/(auth)/admin/+layout.server.ts b/apps/web/src/routes/(auth)/admin/+layout.server.ts index cdd26ab8..42b1f5f6 100644 --- a/apps/web/src/routes/(auth)/admin/+layout.server.ts +++ b/apps/web/src/routes/(auth)/admin/+layout.server.ts @@ -1,4 +1,4 @@ -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import type { LayoutServerLoad } from './$types' export const load: LayoutServerLoad = async () => { diff --git a/apps/web/src/routes/(auth)/admin/+layout.svelte b/apps/web/src/routes/(auth)/admin/+layout.svelte index 3ed0a1bf..ba111867 100644 --- a/apps/web/src/routes/(auth)/admin/+layout.svelte +++ b/apps/web/src/routes/(auth)/admin/+layout.svelte @@ -1,9 +1,9 @@ diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/+page.svelte index 7c0fb3c8..e65f5a40 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/+page.svelte @@ -1,8 +1,8 @@ diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts index f5368b10..34fe4ba9 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts @@ -1,5 +1,5 @@ import { beforeEach, expect, test, vi } from 'vitest' -import { MarkdownPublicationStatus } from '$lib/pieces/types' +import { MarkdownPublicationStatus } from '#lib/pieces/types.js' import { load } from './+layout.server' const { @@ -46,11 +46,11 @@ vi.mock('$app/paths', () => ({ throw new Error('Could not get the request store. This is an internal error.') } })) -vi.mock('$lib/server/database', () => ({ db })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/server/pieces', () => ({ getWebPiece, getPieces })) -vi.mock('$lib/server/storage', () => ({ getStorage })) -vi.mock('$lib/server/workflow/index.js', () => ({ getOpenWorkflow: () => ({ runWorkflow }) })) +vi.mock('#lib/server/database/index.js', () => ({ db })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/server/pieces.js', () => ({ getWebPiece, getPieces })) +vi.mock('#lib/server/storage.js', () => ({ getStorage })) +vi.mock('#lib/server/workflow/index.js', () => ({ getOpenWorkflow: () => ({ runWorkflow }) })) const file = 'old type/源 #?%.books.md' const serving = { diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts index f8ca7ec4..042ae901 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts @@ -2,8 +2,11 @@ import { readFileSync } from 'node:fs' import { beforeEach, expect, test, vi } from 'vitest' import { render } from 'svelte/server' import type { ComponentProps } from 'svelte' -import { MarkdownPublicationStatus } from '$lib/pieces/types' -import { publishedPiece as rawPublishedPiece, asset } from '$lib/pieces/viewer/fixtures/published' +import { MarkdownPublicationStatus } from '#lib/pieces/types.js' +import { + publishedPiece as rawPublishedPiece, + asset +} from '#lib/pieces/viewer/fixtures/published.js' import Overview from './+page.svelte' import PublishedTree from './fixtures/PublishedTree.svelte' @@ -30,14 +33,14 @@ vi.mock('$app/paths', () => ({ })) // These synchronous renderer stubs establish route props, URLs and landmarks; // they do not exercise the wrappers' static catalogs or client prop updates. -vi.mock('$lib/pieces/components/page.svelte', async () => ({ - default: (await import('$lib/pieces/viewer/fixtures/PageRenderer.svelte')).default +vi.mock('#lib/pieces/components/page.svelte', async () => ({ + default: (await import('#lib/pieces/viewer/fixtures/PageRenderer.svelte')).default })) -vi.mock('$lib/pieces/components/icon.svelte', async () => ({ - default: (await import('$lib/pieces/viewer/fixtures/IconRenderer.svelte')).default +vi.mock('#lib/pieces/components/icon.svelte', async () => ({ + default: (await import('#lib/pieces/viewer/fixtures/IconRenderer.svelte')).default })) -vi.mock('$lib/pieces/components/opengraph.svelte', async () => ({ - default: (await import('$lib/pieces/viewer/fixtures/Composition.svelte')).default +vi.mock('#lib/pieces/components/opengraph.svelte', async () => ({ + default: (await import('#lib/pieces/viewer/fixtures/Composition.svelte')).default })) function data( status: MarkdownPublicationStatus = MarkdownPublicationStatus.MATCHING, diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page/+page.svelte index 93e18fae..30faa710 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page/+page.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/share/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/share/+page.svelte index afe95221..3a76f930 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/share/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/share/+page.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.server.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.server.ts index e30c3439..ff3ebb80 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.server.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.server.ts @@ -1,10 +1,10 @@ import { error, fail, redirect } from '@sveltejs/kit' import type { Actions, PageServerLoad } from './$types' -import { getPieces, getWebPiece } from '$lib/server/pieces' -import { getStorage } from '$lib/server/storage' -import { db } from '$lib/server/database' +import { getPieces, getWebPiece } from '#lib/server/pieces.js' +import { getStorage } from '#lib/server/storage.js' +import { db } from '#lib/server/database/index.js' import path from 'path' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { asCoreDatabase, extractFullMarkdown, @@ -18,7 +18,7 @@ import { savePieceAsset } from '@luzzle/core' import { Readable } from 'stream' -import { normalizeLineEndings, normalizeMarkdown } from '$lib/server/markdown' +import { normalizeLineEndings, normalizeMarkdown } from '#lib/server/markdown.js' async function resolveAssetUrls( piece: Piece, diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte index 5b0ec152..3a9e65fb 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte @@ -1,43 +1,52 @@ -{#if page.status === 404} - +{#if error.status === 404} + {:else} - + {/if} diff --git a/apps/web/src/routes/+layout.server.ts b/apps/web/src/routes/+layout.server.ts index 55c39d30..2493edca 100644 --- a/apps/web/src/routes/+layout.server.ts +++ b/apps/web/src/routes/+layout.server.ts @@ -1,5 +1,5 @@ -import { config, type AppConfigPublic } from '$lib/server/config' -import { getConfiguredPieceTypes } from '$lib/pieces/types.server.js' +import { config, type AppConfigPublic } from '#lib/server/config.js' +import { getConfiguredPieceTypes } from '#lib/pieces/types.server.js' import type { LayoutServerLoad } from './$types' export const load: LayoutServerLoad = async ({ locals }) => { diff --git a/apps/web/src/routes/api/pieces/+server.ts b/apps/web/src/routes/api/pieces/+server.ts index b07df073..ce5265b1 100644 --- a/apps/web/src/routes/api/pieces/+server.ts +++ b/apps/web/src/routes/api/pieces/+server.ts @@ -1,7 +1,7 @@ import { json, type RequestHandler } from '@sveltejs/kit' -import { db, sql } from '$lib/server/database' -import { isConfiguredPieceType } from '$lib/pieces/types.server.js' -import { hydrateWithAssets } from '$lib/pieces/assets.server' +import { db, sql } from '#lib/server/database/index.js' +import { isConfiguredPieceType } from '#lib/pieces/types.server.js' +import { hydrateWithAssets } from '#lib/pieces/assets.server.js' import type { WebPieces } from '@luzzle/web.db' const TAKE_DEFAULT = 50 diff --git a/apps/web/src/routes/api/pieces/server.test.ts b/apps/web/src/routes/api/pieces/server.test.ts index 67228433..a72774df 100644 --- a/apps/web/src/routes/api/pieces/server.test.ts +++ b/apps/web/src/routes/api/pieces/server.test.ts @@ -15,9 +15,9 @@ const { config, selectFrom, query, hydrateWithAssets, sql } = vi.hoisted(() => ( hydrateWithAssets: vi.fn(), sql: vi.fn() })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/server/database', () => ({ db: { selectFrom }, sql })) -vi.mock('$lib/pieces/assets.server', () => ({ hydrateWithAssets })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/server/database/index.js', () => ({ db: { selectFrom }, sql })) +vi.mock('#lib/pieces/assets.server.js', () => ({ hydrateWithAssets })) function event(search = '') { return { diff --git a/apps/web/src/routes/api/search/+server.ts b/apps/web/src/routes/api/search/+server.ts index 4285fec2..98105eb6 100644 --- a/apps/web/src/routes/api/search/+server.ts +++ b/apps/web/src/routes/api/search/+server.ts @@ -1,6 +1,6 @@ import { json, type RequestHandler } from '@sveltejs/kit' -import { db, sql } from '$lib/server/database' -import { hydrateWithAssets } from '$lib/pieces/assets.server' +import { db, sql } from '#lib/server/database/index.js' +import { hydrateWithAssets } from '#lib/pieces/assets.server.js' const MAX_RESULTS = 20 diff --git a/apps/web/src/routes/api/session/verify/+server.ts b/apps/web/src/routes/api/session/verify/+server.ts index dcb60ef8..f1f97efd 100644 --- a/apps/web/src/routes/api/session/verify/+server.ts +++ b/apps/web/src/routes/api/session/verify/+server.ts @@ -1,5 +1,5 @@ import { json } from '@sveltejs/kit' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import type { RequestEvent } from './$types' export async function GET(event: RequestEvent) { diff --git a/apps/web/src/routes/api/session/verify/server.test.ts b/apps/web/src/routes/api/session/verify/server.test.ts index 47f05fae..c41821e6 100644 --- a/apps/web/src/routes/api/session/verify/server.test.ts +++ b/apps/web/src/routes/api/session/verify/server.test.ts @@ -1,10 +1,10 @@ import { beforeEach, expect, test, vi } from 'vitest' -import { config } from '$lib/server/config' -import { credentialsAuth, oidcAuth } from '$lib/server/config.fixture' +import { config } from '#lib/server/config.js' +import { credentialsAuth, oidcAuth } from '#lib/server/config.fixture.js' import { GET } from './+server' -vi.mock('$lib/server/config', async () => { - const { makeConfig } = await import('$lib/server/config.fixture') +vi.mock('#lib/server/config.js', async () => { + const { makeConfig } = await import('#lib/server/config.fixture.js') return { config: makeConfig() } }) diff --git a/apps/web/src/routes/layout.server.test.ts b/apps/web/src/routes/layout.server.test.ts index 154b0d50..6c56bae9 100644 --- a/apps/web/src/routes/layout.server.test.ts +++ b/apps/web/src/routes/layout.server.test.ts @@ -1,8 +1,8 @@ import { expect, test, vi } from 'vitest' import { load } from './+layout.server' -vi.mock('$lib/server/config', async () => { - const { makeConfig: baseConfig, oidcAuth } = await import('$lib/server/config.fixture') +vi.mock('#lib/server/config.js', async () => { + const { makeConfig: baseConfig, oidcAuth } = await import('#lib/server/config.fixture.js') return { config: { ...baseConfig(), diff --git a/apps/web/src/routes/pieces/+layout.svelte b/apps/web/src/routes/pieces/+layout.svelte index e8208688..6fa12ff5 100644 --- a/apps/web/src/routes/pieces/+layout.svelte +++ b/apps/web/src/routes/pieces/+layout.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/pieces/[piece]/[slug]/page.server.test.ts b/apps/web/src/routes/pieces/[piece]/[slug]/page.server.test.ts index 15d1d35c..4b2a5952 100644 --- a/apps/web/src/routes/pieces/[piece]/[slug]/page.server.test.ts +++ b/apps/web/src/routes/pieces/[piece]/[slug]/page.server.test.ts @@ -17,9 +17,9 @@ const { query, hydrateWithAssets, config } = vi.hoisted(() => ({ content: { text: { title: 'Garden' } } } })) -vi.mock('$lib/server/database', () => ({ db: { selectFrom: () => query } })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/pieces/assets.server', () => ({ hydrateWithAssets })) +vi.mock('#lib/server/database/index.js', () => ({ db: { selectFrom: () => query } })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/pieces/assets.server.js', () => ({ hydrateWithAssets })) function event() { return { diff --git a/apps/web/src/routes/pieces/assets/[...path]/+server.ts b/apps/web/src/routes/pieces/assets/[...path]/+server.ts index 09f75408..bc4b728a 100644 --- a/apps/web/src/routes/pieces/assets/[...path]/+server.ts +++ b/apps/web/src/routes/pieces/assets/[...path]/+server.ts @@ -3,7 +3,7 @@ import fs from 'node:fs' import { Readable } from 'node:stream' import mime from 'mime-types' import type { RequestHandler } from '@sveltejs/kit' -import { isSafeRelativePath, isWithinDirectory } from '$lib/server/asset-paths' +import { isSafeRelativePath, isWithinDirectory } from '#lib/server/asset-paths.js' export const GET: RequestHandler = async ({ params }) => { const { path: assetPath } = params diff --git a/apps/web/src/routes/random/+server.ts b/apps/web/src/routes/random/+server.ts index 5d10afae..d508ebfb 100644 --- a/apps/web/src/routes/random/+server.ts +++ b/apps/web/src/routes/random/+server.ts @@ -1,5 +1,5 @@ import { redirect } from '@sveltejs/kit' -import { db, sql } from '$lib/server/database' +import { db, sql } from '#lib/server/database/index.js' import type { RequestHandler } from './$types' export const GET: RequestHandler = async ({ url }) => { diff --git a/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/+server.ts b/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/+server.ts index df1fd875..021ef24c 100644 --- a/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/+server.ts +++ b/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/+server.ts @@ -1,10 +1,10 @@ import type { PublicWebPiece } from '@luzzle/web.pieces' import type { RequestHandler } from './$types' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { generateRssFeed, generateJsonFeed, type RssFeed, type JsonFeed } from 'feedsmith' -import { getPiecesForFeed } from '$lib/feeds/utils' +import { getPiecesForFeed } from '#lib/feeds/utils.js' import { error } from '@sveltejs/kit' -import { isConfiguredPieceType } from '$lib/pieces/types.server.js' +import { isConfiguredPieceType } from '#lib/pieces/types.server.js' import YAML from 'yaml' type FeedRss = RssFeed diff --git a/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/server.test.ts b/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/server.test.ts index 819f7073..2847a918 100644 --- a/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/server.test.ts +++ b/apps/web/src/routes/rss/pieces/[[piece]]/feed.[format=feedFormat]/server.test.ts @@ -9,8 +9,8 @@ const { config, getPiecesForFeed } = vi.hoisted(() => ({ }, getPiecesForFeed: vi.fn() })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/feeds/utils', () => ({ getPiecesForFeed })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/feeds/utils.js', () => ({ getPiecesForFeed })) function event(type: string | undefined, format: string) { return { params: { piece: type, format } } as Parameters[0] diff --git a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.server.ts b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.server.ts index 2bfdcb28..d47b5f6d 100644 --- a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.server.ts +++ b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.server.ts @@ -1,8 +1,8 @@ -import { getPiecesForFeed } from '$lib/feeds/utils' -import { loadPageMarkdown } from '$lib/server/pages.js' -import { config } from '$lib/server/config' +import { getPiecesForFeed } from '#lib/feeds/utils.js' +import { loadPageMarkdown } from '#lib/server/pages.js' +import { config } from '#lib/server/config.js' import { error } from '@sveltejs/kit' -import { isConfiguredPieceType } from '$lib/pieces/types.server.js' +import { isConfiguredPieceType } from '#lib/pieces/types.server.js' import type { PageServerLoad } from './$types' export const load: PageServerLoad = async ({ params }) => { diff --git a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.svelte b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.svelte index 199c62fd..b29ca5ea 100644 --- a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.svelte +++ b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/+page.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/page.server.test.ts b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/page.server.test.ts index 42b2d3ab..ffda6f2d 100644 --- a/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/page.server.test.ts +++ b/apps/web/src/routes/rss/pieces/[[piece]]/feed.html/page.server.test.ts @@ -10,9 +10,9 @@ const { config, getPiecesForFeed, loadPageMarkdown } = vi.hoisted(() => ({ getPiecesForFeed: vi.fn(), loadPageMarkdown: vi.fn() })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/feeds/utils', () => ({ getPiecesForFeed })) -vi.mock('$lib/server/pages.js', () => ({ loadPageMarkdown })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/feeds/utils.js', () => ({ getPiecesForFeed })) +vi.mock('#lib/server/pages.js', () => ({ loadPageMarkdown })) const pieces = [{ id: 'note', type: 'field-notes', title: 'A note', assets: [] }] function event(type?: string) { diff --git a/apps/web/src/routes/rss/tags/[tag]/feed.[format=feedFormat]/+server.ts b/apps/web/src/routes/rss/tags/[tag]/feed.[format=feedFormat]/+server.ts index 203ec3d6..95985b66 100644 --- a/apps/web/src/routes/rss/tags/[tag]/feed.[format=feedFormat]/+server.ts +++ b/apps/web/src/routes/rss/tags/[tag]/feed.[format=feedFormat]/+server.ts @@ -1,8 +1,8 @@ import type { PublicWebPiece } from '@luzzle/web.pieces' import type { RequestHandler } from './$types' -import { config } from '$lib/server/config' +import { config } from '#lib/server/config.js' import { generateRssFeed, generateJsonFeed, type RssFeed, type JsonFeed } from 'feedsmith' -import { getPiecesForTagFeed } from '$lib/feeds/utils' +import { getPiecesForTagFeed } from '#lib/feeds/utils.js' import YAML from 'yaml' type FeedRss = RssFeed diff --git a/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.server.ts b/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.server.ts index 091e541e..13c801b9 100644 --- a/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.server.ts +++ b/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.server.ts @@ -1,6 +1,6 @@ -import { getPiecesForTagFeed } from '$lib/feeds/utils' -import { loadPageMarkdown } from '$lib/server/pages.js' -import { config } from '$lib/server/config' +import { getPiecesForTagFeed } from '#lib/feeds/utils.js' +import { loadPageMarkdown } from '#lib/server/pages.js' +import { config } from '#lib/server/config.js' import type { PageServerLoad } from './$types' export const load: PageServerLoad = async ({ params }) => { diff --git a/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.svelte b/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.svelte index 7319aa3f..3d4984c6 100644 --- a/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.svelte +++ b/apps/web/src/routes/rss/tags/[tag]/feed.html/+page.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/rss/tags/[tag]/feed.html/page.server.test.ts b/apps/web/src/routes/rss/tags/[tag]/feed.html/page.server.test.ts index 9ff49592..aecbd112 100644 --- a/apps/web/src/routes/rss/tags/[tag]/feed.html/page.server.test.ts +++ b/apps/web/src/routes/rss/tags/[tag]/feed.html/page.server.test.ts @@ -9,9 +9,9 @@ const { config, getPiecesForTagFeed, loadPageMarkdown } = vi.hoisted(() => ({ getPiecesForTagFeed: vi.fn(), loadPageMarkdown: vi.fn() })) -vi.mock('$lib/server/config', () => ({ config })) -vi.mock('$lib/feeds/utils', () => ({ getPiecesForTagFeed })) -vi.mock('$lib/server/pages.js', () => ({ loadPageMarkdown })) +vi.mock('#lib/server/config.js', () => ({ config })) +vi.mock('#lib/feeds/utils.js', () => ({ getPiecesForTagFeed })) +vi.mock('#lib/server/pages.js', () => ({ loadPageMarkdown })) const pieces = [{ id: 'note', type: 'field-notes', title: 'A note', assets: [] }] function event(tag = 'field-notes') { return { params: { tag } } as Parameters[0] diff --git a/apps/web/src/routes/search/+layout.svelte b/apps/web/src/routes/search/+layout.svelte index dc9a9d50..2c7a09d8 100644 --- a/apps/web/src/routes/search/+layout.svelte +++ b/apps/web/src/routes/search/+layout.svelte @@ -1,5 +1,5 @@ diff --git a/apps/web/src/routes/search/+page.server.ts b/apps/web/src/routes/search/+page.server.ts index 4c0bda60..8533f0cf 100644 --- a/apps/web/src/routes/search/+page.server.ts +++ b/apps/web/src/routes/search/+page.server.ts @@ -1,7 +1,7 @@ -import { db, sql } from '$lib/server/database' +import { db, sql } from '#lib/server/database/index.js' import { redirect } from '@sveltejs/kit' import type { PageServerLoad } from './$types' -import { hydrateWithAssets } from '$lib/pieces/assets.server' +import { hydrateWithAssets } from '#lib/pieces/assets.server.js' const MAX_RESULTS = 20 diff --git a/apps/web/src/routes/search/+page.svelte b/apps/web/src/routes/search/+page.svelte index 7a9c1fd4..0adcb796 100644 --- a/apps/web/src/routes/search/+page.svelte +++ b/apps/web/src/routes/search/+page.svelte @@ -1,13 +1,14 @@ diff --git a/apps/web/src/routes/signin/+page.server.ts b/apps/web/src/routes/signin/+page.server.ts index 07059645..c2652598 100644 --- a/apps/web/src/routes/signin/+page.server.ts +++ b/apps/web/src/routes/signin/+page.server.ts @@ -1,7 +1,7 @@ import { redirect } from '@sveltejs/kit' import type { PageServerLoad } from './$types' -import { config } from '$lib/server/config' -import { localRedirect } from '$lib/server/redirect' +import { config } from '#lib/server/config.js' +import { localRedirect } from '#lib/server/redirect.js' export const load: PageServerLoad = async ({ locals, url }) => { const auth = config.auth diff --git a/apps/web/src/routes/signin/+page.svelte b/apps/web/src/routes/signin/+page.svelte index d260f81d..2172f806 100644 --- a/apps/web/src/routes/signin/+page.svelte +++ b/apps/web/src/routes/signin/+page.svelte @@ -1,6 +1,6 @@ diff --git a/apps/web/test/fixtures/templates/svelte.config.js b/apps/web/test/fixtures/templates/svelte.config.js deleted file mode 100644 index b78680f6..00000000 --- a/apps/web/test/fixtures/templates/svelte.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import adapter from '@sveltejs/adapter-node' - -export default { - compilerOptions: { experimental: { async: true } }, - kit: { - adapter: adapter({ out: process.env.TEMPLATE_BUILD_DIR }), - outDir: process.env.TEMPLATE_KIT_DIR, - files: { lib: '../../../src/lib' }, - csp: { - mode: 'auto', - directives: { - 'default-src': ['self'], - 'script-src': ['self'], - 'style-src': ['self', 'unsafe-inline'], - 'object-src': ['none'] - } - } - } -} diff --git a/apps/web/test/fixtures/templates/vite.config.js b/apps/web/test/fixtures/templates/vite.config.js index 87c5dedb..9a6906c2 100644 --- a/apps/web/test/fixtures/templates/vite.config.js +++ b/apps/web/test/fixtures/templates/vite.config.js @@ -1,9 +1,30 @@ +import adapter from '@sveltejs/adapter-node' import { defineConfig } from 'vite' import { sveltekit } from '@sveltejs/kit/vite' +import { fileURLToPath } from 'node:url' import Icons from 'unplugin-icons/vite' +const appLibrary = fileURLToPath(new URL('../../../src/lib/', import.meta.url)) + export default defineConfig({ - plugins: [sveltekit(), Icons({ compiler: 'svelte' })], + resolve: { alias: { '#lib': appLibrary } }, + plugins: [ + sveltekit({ + compilerOptions: { experimental: { async: true } }, + adapter: adapter({ out: process.env.TEMPLATE_BUILD_DIR }), + outDir: process.env.TEMPLATE_KIT_DIR, + csp: { + mode: 'auto', + directives: { + 'default-src': ['self'], + 'script-src': ['self'], + 'style-src': ['self', 'unsafe-inline'], + 'object-src': ['none'] + } + } + }), + Icons({ compiler: 'svelte' }) + ], // Preserve Node integrations and package-relative computed theme imports at runtime. ssr: { external: ['@luzzle/core', '@luzzle/web.theme/editor'], noExternal: ['bits-ui'] }, logLevel: 'error' diff --git a/apps/web/test/templates/routes.test.ts b/apps/web/test/templates/routes.test.ts index dbf595bc..b6f4bdf1 100644 --- a/apps/web/test/templates/routes.test.ts +++ b/apps/web/test/templates/routes.test.ts @@ -6,7 +6,7 @@ import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promis import path from 'node:path' import { fileURLToPath } from 'node:url' import { promisify } from 'node:util' -import { createServerEnvironment } from '../../scripts/runtime-environment.js' +import { resolveAuthUrl } from '../../scripts/runtime-environment.js' const exec = promisify(execFile) const webRoot = fileURLToPath(new URL('../../', import.meta.url)) @@ -59,17 +59,21 @@ let configFilename: string let origin: string let server: ChildProcess | undefined let serverErrors = '' +let nativeShutdownObserved = false let originalBuild: Record let originalMachineFeeds: MachineFeeds let originalPublicPiece: unknown let childEnv: NodeJS.ProcessEnv +// Keep argv[1] as the built entry so importing the bootstrap does not trigger its CLI guard. const startServer = ` import { once } from 'node:events'; import { pathToFileURL } from 'node:url'; -const { server } = await import(pathToFileURL(process.argv[1]).href); -if (!server.server.listening) await once(server.server, 'listening'); -process.send({ port: server.server.address().port }); +const { startServer } = await import(pathToFileURL(process.argv[2]).href); +const server = await startServer(process.argv[1]); +process.once('sveltekit:shutdown', () => process.send({ nativeShutdown: true })); +if (!server.listening) await once(server, 'listening'); +process.send({ port: server.address().port }); ` const seedDatabase = ` @@ -176,7 +180,7 @@ async function buildHashes() { async function request(pathname = piecePath, accept = 'text/html', init: RequestInit = {}) { const headers = new Headers(init.headers) headers.set('accept', accept) - headers.set('x-forwarded-proto', 'http') + if (!headers.has('x-forwarded-proto')) headers.set('x-forwarded-proto', 'http') return fetch(`${origin}${pathname}`, { ...init, headers, @@ -456,7 +460,9 @@ async function stopBuiltApp() { server.kill('SIGTERM') const timeout = setTimeout(() => server?.kill('SIGKILL'), 5000) try { - await exited + const [, signal] = await exited + expect(signal).not.toBe('SIGKILL') + expect(nativeShutdownObserved).toBe(true) } finally { clearTimeout(timeout) } @@ -465,14 +471,19 @@ async function stopBuiltApp() { async function startBuiltApp(envOverrides: NodeJS.ProcessEnv = {}): Promise { const child = spawn( process.execPath, - ['--input-type=module', '-e', startServer, path.join(buildDirectory, 'index.js')], + [ + '--input-type=module', + '-e', + startServer, + path.join(buildDirectory, 'index.js'), + path.join(webRoot, 'scripts/start-server.js') + ], { cwd: directory, env: { ...childEnv, HOST: '127.0.0.1', PORT: '0', - ORIGIN: '', PROTOCOL_HEADER: 'x-forwarded-proto', SOCKET_PATH: '', LISTEN_PID: '0', @@ -483,6 +494,12 @@ async function startBuiltApp(envOverrides: NodeJS.ProcessEnv = {}): Promise { + if (typeof message === 'object' && message !== null && 'nativeShutdown' in message) { + nativeShutdownObserved = message.nativeShutdown === true + } + }) child.stderr?.on('data', (chunk) => { serverErrors += String(chunk) }) @@ -546,6 +563,7 @@ beforeAll(async () => { configFilename, JSON.stringify({ url: { app: '' }, + network: { public: { host: '127.0.0.1' } }, paths: { database: path.join(directory, 'luzzle.sqlite'), static: path.join(directory, 'owner-static') @@ -708,10 +726,10 @@ describe('stock pieces and configured site sections through production HTTP', () }, { label: 'B', - app: 'https://garden-b.example.test:9443/published-b/', + app: 'http://garden-b.example.test:9443/published-b/', app_assets: 'https://app-assets-b.example.test/site/v2/', luzzle_assets: 'https://piece-assets-b.example.test/archive/v2/', - requestOrigin: 'https://garden-b.example.test:9443', + requestOrigin: 'http://garden-b.example.test:9443', environment: { ORIGIN: 'https://public-b.example.test:10443/' } } ] @@ -720,14 +738,22 @@ describe('stock pieces and configured site sections through production HTTP', () const { label, app, app_assets, luzzle_assets, requestOrigin, environment } = deployment const config = JSON.parse(original.toString('utf8')) config.url = { app, app_assets, luzzle_assets } - const runtimeEnvironment = createServerEnvironment(config, environment) - expect(runtimeEnvironment.ORIGIN, label).toBe(requestOrigin) + expect(resolveAuthUrl(config.url.app), label).toBe(requestOrigin) await stopBuiltApp() await writeFile(configFilename, JSON.stringify(config)) - origin = await startBuiltApp({ ORIGIN: runtimeEnvironment.ORIGIN }) + origin = await startBuiltApp(environment) - // Only the local Node listener is contacted; CDN URLs are inspected, never fetched. - const response = await request() + // First request after the real bootstrap returns: incoming proxy values must not + // override YAML. Only the local listener is contacted; CDNs are never fetched. + const proxyProtocol = requestOrigin.startsWith('https:') ? 'http' : 'https' + const response = await request(piecePath, 'text/html', { + headers: { + host: 'incoming.example.test:12443', + 'x-forwarded-host': 'forwarded.example.test:11443', + 'x-forwarded-proto': proxyProtocol, + 'x-forwarded-port': '10443' + } + }) expect(response.status, label).toBe(200) const html = await response.text() expectInlineConfiguredCss(html) @@ -760,6 +786,7 @@ describe('stock pieces and configured site sections through production HTTP', () expect(api.headers.has('content-security-policy')).toBe(false) expect(api.headers.has('content-security-policy-report-only')).toBe(false) expect((await api.json()).pieces).toEqual([originalPublicPiece]) + expect(await buildHashes(), label).toEqual(originalBuild) } } finally { await stopBuiltApp() @@ -772,14 +799,13 @@ describe('stock pieces and configured site sections through production HTTP', () const original = await readFile(configFilename) const config = JSON.parse(original.toString('utf8')) config.url.app = 'https://configured-csrf.example.test:8443/garden/' - const requestOrigin = new URL(config.url.app).origin + const requestOrigin = resolveAuthUrl(config.url.app) const ignoredOrigin = 'https://request-csrf.example.test:9443' - const runtimeEnvironment = createServerEnvironment(config, { ORIGIN: ignoredOrigin }) - expect(runtimeEnvironment.ORIGIN).toBe(requestOrigin) + expect(requestOrigin).toBe('https://configured-csrf.example.test:8443') try { await stopBuiltApp() await writeFile(configFilename, JSON.stringify(config)) - origin = await startBuiltApp({ ORIGIN: runtimeEnvironment.ORIGIN }) + origin = await startBuiltApp({ ORIGIN: ignoredOrigin }) const get = await request('/api/pieces?type=cutover-kind', 'application/json') expect(get.status).toBe(200) expect((await get.json()).pieces).toEqual([originalPublicPiece]) @@ -804,11 +830,20 @@ describe('stock pieces and configured site sections through production HTTP', () requestOrigin: ignoredOrigin, status: 403 }, - { label: 'listener is not the canonical origin', requestOrigin: origin, status: 403 } + { label: 'listener is not the canonical origin', requestOrigin: origin, status: 403 }, + { + label: 'browser origin matching spoofed forwarded host is not trusted', + requestOrigin: 'http://spoofed.example.test:11443', + status: 403 + } ] for (const entry of cases) { const headers: Record = { - 'content-type': 'application/x-www-form-urlencoded' + 'content-type': 'application/x-www-form-urlencoded', + host: 'incoming.example.test:12443', + 'x-forwarded-host': 'spoofed.example.test:11443', + 'x-forwarded-proto': 'http', + 'x-forwarded-port': '10443' } if (entry.requestOrigin !== undefined) headers.origin = entry.requestOrigin const response = await request('/api/pieces', 'application/json', { @@ -850,10 +885,14 @@ describe('stock pieces and configured site sections through production HTTP', () }) test('also gates unsafe PUT, PATCH and DELETE forms before GET-only endpoint dispatch', async () => { - const requestOrigin = 'https://method-csrf.example.test:9443' + const original = await readFile(configFilename) + const config = JSON.parse(original.toString('utf8')) + config.url.app = 'https://method-csrf.example.test:9443/garden/' + const requestOrigin = resolveAuthUrl(config.url.app) try { await stopBuiltApp() - origin = await startBuiltApp({ ORIGIN: requestOrigin }) + await writeFile(configFilename, JSON.stringify(config)) + origin = await startBuiltApp({ ORIGIN: 'http://ignored.example.test:3000' }) for (const method of ['PUT', 'PATCH', 'DELETE']) { for (const entry of [ { label: 'same origin reaches method dispatch', origin: requestOrigin, status: 405 }, @@ -861,14 +900,29 @@ describe('stock pieces and configured site sections through production HTTP', () label: 'cross origin rejected before handle', origin: 'https://cross.example.test', status: 403 + }, + { label: 'missing origin', origin: undefined, status: 403 }, + { label: 'literal null origin', origin: 'null', status: 403 }, + { + label: 'browser origin matching spoofed forwarded host is not trusted', + origin: 'http://spoofed.example.test:11443', + status: 403 } ]) { + const headers: Record = { + 'content-type': 'application/x-www-form-urlencoded', + host: 'incoming.example.test:12443', + 'x-forwarded-host': 'spoofed.example.test:11443', + 'x-forwarded-port': '10443' + } + if (entry.origin !== undefined) headers.origin = entry.origin const response = await request('/api/pieces', 'application/json', { method, - headers: { origin: entry.origin, 'content-type': 'application/x-www-form-urlencoded' }, + headers, body: 'title=not-a-write' }) expect(response.status, `${method}: ${entry.label}`).toBe(entry.status) + expect(response.headers.has('content-security-policy')).toBe(false) if (entry.status === 403) expect(await response.json()).toEqual({ message: `Cross-site ${method} form submissions are forbidden` @@ -877,6 +931,7 @@ describe('stock pieces and configured site sections through production HTTP', () } } finally { await stopBuiltApp() + await writeFile(configFilename, original) origin = await startBuiltApp() } }) @@ -1147,6 +1202,8 @@ describe('stock pieces and configured site sections through production HTTP', () const failedHome = await request('/') expect(failedHome.status).toBe(500) const html = await failedHome.text() + expect(html).toContain('error 500') + expect(html).not.toContain('sections/missing.md') expect(html).not.toContain(directory) expect(expectInlineConfiguredCss(html)).toContain('--color-primary: #135790;') }) @@ -1156,7 +1213,9 @@ describe('stock pieces and configured site sections through production HTTP', () await writeFile(path.join(directory, 'sections/neighbor.png'), 'not a public asset') await restartWithPages({ feed: { about: 'sections/image.md' } }) expect(await pageHtml('/rss/pieces/feed.html')).toContain('src="neighbor.png"') - expect((await request('/rss/pieces/neighbor.png')).status).toBe(404) + const missing = await request('/rss/pieces/neighbor.png') + expect(missing.status).toBe(404) + expect(await missing.text()).toContain('page not found') }) test('site section settings preserve machine-feed metadata and item data', async () => { diff --git a/apps/web/test/templates/ssr.test.ts b/apps/web/test/templates/ssr.test.ts index a1eaf409..07c30e59 100644 --- a/apps/web/test/templates/ssr.test.ts +++ b/apps/web/test/templates/ssr.test.ts @@ -81,12 +81,14 @@ const ogSelections = [ } ] +// Isolated compiler fixture only: adapter-node6 exports the native HTTP server directly. +// Production origin/CSRF coverage lives in routes.test.ts and uses the real bootstrap. const startServer = ` import { once } from 'node:events'; import { pathToFileURL } from 'node:url'; const { server } = await import(pathToFileURL(process.argv[1]).href); -if (!server.server.listening) await once(server.server, 'listening'); -process.send({ port: server.server.address().port }); +if (!server.listening) await once(server, 'listening'); +process.send({ port: server.address().port }); ` async function buildHashes() { @@ -316,7 +318,6 @@ beforeAll(async () => { ...env, HOST: '127.0.0.1', PORT: '0', - ORIGIN: '', PROTOCOL_HEADER: 'x-forwarded-proto', SOCKET_PATH: '', LISTEN_PID: '0', diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index fb52011a..ef193d5c 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,49 +1,25 @@ { - "extends": "./.svelte-kit/tsconfig.json", + "extends": "$app/tsconfig", "compilerOptions": { - "verbatimModuleSyntax": true, - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "bundler", - "types": [ - "node", - "unplugin-icons/types/svelte" - ] + "types": ["$app/types", "node", "unplugin-icons/types/svelte"] }, "include": [ - ".svelte-kit/ambient.d.ts", - ".svelte-kit/non-ambient.d.ts", - "./types/**/$types.d.ts", - "./vite.config.js", - "./vite.config.ts", - "./src/**/*.js", - "./src/**/*.ts", - "./src/**/*.svelte", - "./tests/**/*.js", - "./tests/**/*.ts", - "./tests/**/*.svelte", - "./test/templates/**/*.ts", + "src", + "scripts/**/*.test.ts", + "test/templates/**/*.ts", + "vite.config.ts", + "vitest*.config.ts" ], "exclude": [ - "./node_modules/**", - "./src/lib/pieces/components/custom/", - "./src/lib/content/components/custom/", - "./src/service-worker.js", - "./src/service-worker/**/*.js", - "./src/service-worker.ts", - "./src/service-worker/**/*.ts", - "./src/service-worker.d.ts", - "./src/service-worker/**/*.d.ts" + "node_modules", + "build", + "src/lib/pieces/components/custom", + "src/lib/content/components/custom", + "src/service-worker.js", + "src/service-worker.ts", + "src/service-worker.d.ts", + "src/service-worker" ] - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // except $lib which is handled by https://kit.svelte.dev/docs/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 } diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 0820e052..90750c8b 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,8 +1,13 @@ +import adapter from '@sveltejs/adapter-node' +import type { Adapter } from '@sveltejs/kit' +import { getRequest } from '@sveltejs/kit/node' import { sveltekit } from '@sveltejs/kit/vite' +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' import { defineConfig } from 'vite' import Icons from 'unplugin-icons/vite' import { enhancedImages } from '@sveltejs/enhanced-img' import { loadConfig } from '@luzzle/web.templates/config' +import { resolveAppOrigin } from './scripts/runtime-environment.js' function extractHostname(url: string | undefined): string | undefined { if (!url) return undefined @@ -13,8 +18,25 @@ function extractHostname(url: string | undefined): string | undefined { } } +function createAdapter(origin: string | undefined): Adapter { + const native = adapter() + if (!origin) return native + const nativeVite = native.vite + return { + ...native, + vite: (context) => { + const settings = typeof nativeVite === 'function' ? nativeVite(context) : nativeVite + return { + ...settings, + getRequest: (options) => getRequest({ ...options, base: origin }) + } + } + } +} + export default defineConfig(({ command }) => { const config = command === 'serve' ? loadConfig('./config.yaml') : undefined + const appOrigin = config ? resolveAppOrigin(config.url.app) : undefined const host = config?.network?.public?.host const hmrPort = config?.network?.public?.hmr_port const allowedHosts = Array.from( @@ -23,21 +45,50 @@ export default defineConfig(({ command }) => { 'localhost', extractHostname(config?.url.app), extractHostname(config?.network.internal.explorer) - ].filter((h): h is string => !!h) + ].filter((value): value is string => !!value) ) ) const sourceFirst = command === 'serve' && !process.env.VITEST return { - plugins: [enhancedImages(), sveltekit(), Icons({ compiler: 'svelte' })], + plugins: [ + enhancedImages(), + sveltekit({ + compilerOptions: { experimental: { async: true } }, + preprocess: [vitePreprocess()], + adapter: createAdapter(appOrigin), + csrf: { trustedOrigins: ['http://localhost:3000', 'http://localhost:5173'] }, + csp: { + mode: 'auto', + directives: { + 'default-src': ['self'], + 'script-src': [ + 'self', + 'unsafe-hashes', + 'sha256-7dQwUgLau1NFCCGjfn9FsYptB6ZtWxJin6VohGIu20I=', + 'sha256-8oa0zHl7YAxAhiYNg8fadxmsU54ezUB+oZ/NjM03xVk=' + ], + 'style-src': ['self', 'unsafe-inline'], + 'img-src': ['self', 'data:'], + 'connect-src': ['self'], + 'font-src': ['self'], + 'object-src': ['none'], + 'base-uri': ['self'], + 'frame-ancestors': ['none'] + } + }, + inlineStyleThreshold: 3072, + paths: { assets: '' }, + prerender: { concurrency: 8, handleHttpError: 'warn', crawl: false } + }), + Icons({ compiler: 'svelte' }) + ], // Keep computed Shiki imports relative to their owning runtime package. ssr: sourceFirst ? { noExternal: [/^@luzzle\//] } : { external: ['@luzzle/web.theme/editor'] }, server: { host: host || false, allowedHosts, - hmr: { - clientPort: hmrPort ? Number(hmrPort) : undefined - } + hmr: { clientPort: hmrPort ? Number(hmrPort) : undefined } } } }) diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index a9111ed5..043ae4f0 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url' export default defineConfig({ plugins: [svelte({ configFile: false }), Icons({ compiler: 'svelte' })], server: { hmr: false }, - resolve: { alias: { $lib: fileURLToPath(new URL('./src/lib', import.meta.url)) } }, + resolve: { alias: { '#lib': fileURLToPath(new URL('./src/lib', import.meta.url)) } }, test: { include: ['src/**/*.{test,spec}.{ts,js}', 'scripts/**/*.{test,spec}.{ts,js}'], exclude: ['build/**', '.svelte-kit/**', 'node_modules/**'] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 863c2237..9dffbc06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -286,7 +286,7 @@ importers: version: 6.4.0(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0) better-auth: specifier: 1.7.14 - version: 1.7.14(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7) + version: 1.7.14(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7) eventsource-parser: specifier: 4.1.1 version: 4.1.1 @@ -355,14 +355,17 @@ importers: specifier: workspace:* version: link:../../packages/web/jobs '@sveltejs/adapter-node': - specifier: 5.3.2 - version: 5.3.2(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))) + specifier: 6.0.1 + version: 6.0.1(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))) '@sveltejs/enhanced-img': specifier: 1.0.0 - version: 1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(rollup@4.60.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + version: 1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) '@sveltejs/kit': - specifier: 2.70.3 - version: 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + specifier: 3.1.1 + version: 3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/load-config': + specifier: 0.2.2 + version: 0.2.2 '@sveltejs/vite-plugin-svelte': specifier: 7.1.2 version: 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) @@ -377,7 +380,7 @@ importers: version: 24.12.4 bits-ui: specifier: 2.19.2 - version: 2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) + version: 2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) codemirror: specifier: 6.0.2 version: 6.0.2 @@ -1867,33 +1870,6 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rollup/plugin-commonjs@28.0.9': - resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-json@6.1.0': - resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-node-resolve@16.0.3': - resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -1903,144 +1879,6 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} - cpu: [x64] - os: [win32] - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2083,20 +1921,15 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@sveltejs/acorn-typescript@1.0.10': - resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==} - peerDependencies: - acorn: ^8.9.0 - '@sveltejs/acorn-typescript@1.0.13': resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} peerDependencies: acorn: ^8.9.0 - '@sveltejs/adapter-node@5.3.2': - resolution: {integrity: sha512-nBJSipMb1KLjnAM7uzb+YpnA1VWKb+WdR+0mXEnXI6K1A3XYWbjkcjnW20ubg07sicK8XaGY/FAX3PItw39qBQ==} + '@sveltejs/adapter-node@6.0.1': + resolution: {integrity: sha512-Pt/YcMakEkTjdQXaAvxrjrDnoGX4zU7pAZICnoLfpI+CawLGigq5upjFW9rVK2QY7yfpR1xeRG/kgI+syzPB3w==} peerDependencies: - '@sveltejs/kit': ^2.4.0 + '@sveltejs/kit': ^3.0.0-next.0 '@sveltejs/enhanced-img@1.0.0': resolution: {integrity: sha512-MVY2SNj1NE/DW7yrAePFfOeEnj/PfkHPQvWfeL23h/a0i7r1qEF5qj3iIyxqCF5Vu+akskQ/h5eEEM7hjvFmrQ==} @@ -2106,22 +1939,26 @@ packages: svelte: ^5.0.0 vite: '>=8.0.12' - '@sveltejs/kit@2.70.3': - resolution: {integrity: sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==} - engines: {node: '>=18.13'} + '@sveltejs/kit@3.1.1': + resolution: {integrity: sha512-BiEMvekYXE9Gu/YhWJ/yJ0WDLH1PX/dmjuAeFp5rrtdmgdzkjt7DvimlSP+/U4tgposl7pkE20luHpcnc6BgRw==} + engines: {node: '>=22.17'} hasBin: true 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 + '@sveltejs/vite-plugin-svelte': ^7.0.0 + svelte: ^5.57.1 + typescript: ^6.0.0 + vite: ^8.0.12 peerDependenciesMeta: '@opentelemetry/api': optional: true typescript: optional: true + '@sveltejs/load-config@0.2.2': + resolution: {integrity: sha512-K7dsJDQxBOF+f+epuhMactcjK2VP4MRkLKtwSykNtEI+cKVEyzrmmhQ1pmoxI800m4JKcyJ05L2M4yPwAGiBNw==} + engines: {node: '>= 18.0.0'} + '@sveltejs/load-config@0.2.3': resolution: {integrity: sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==} engines: {node: '>= 18.0.0'} @@ -2225,9 +2062,6 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} @@ -2240,9 +2074,6 @@ packages: '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -2276,9 +2107,6 @@ packages: '@types/node@24.12.4': resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} - '@types/resolve@1.20.2': - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} @@ -2678,9 +2506,6 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2697,9 +2522,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -2762,9 +2587,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - devalue@5.8.1: - resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} - devalue@5.9.4: resolution: {integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==} @@ -2796,10 +2618,6 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} @@ -3022,9 +2840,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gaxios@7.1.4: resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} engines: {node: '>=18'} @@ -3079,10 +2894,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} - engines: {node: '>= 0.4'} - hast-util-from-html@2.0.3: resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} @@ -3178,10 +2989,6 @@ packages: is-arrayish@0.3.4: resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} - is-core-module@2.16.2: - resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} - engines: {node: '>= 0.4'} - is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -3200,16 +3007,10 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} @@ -3290,10 +3091,6 @@ packages: keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} @@ -3734,9 +3531,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -4024,11 +3818,6 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} - engines: {node: '>= 0.4'} - hasBin: true - responselike@4.0.2: resolution: {integrity: sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==} engines: {node: '>=20'} @@ -4042,11 +3831,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rou3@0.9.2: resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} @@ -4193,10 +3977,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - svelte-check@4.7.7: resolution: {integrity: sha512-Ge2IMdeTpbJuzNFGRiiMg0RWgbScysKQu7xwcyFJ22+DU9F5nPX51+Q3gHmGqRxYQMx2knreVWt5oCtfe0vf2g==} engines: {node: '>= 18.0.0'} @@ -5600,116 +5380,11 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-commonjs@28.0.9(rollup@4.60.4)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.4) - is-reference: 1.2.1 - magic-string: 0.30.21 - picomatch: 4.0.4 - optionalDependencies: - rollup: 4.60.4 - - '@rollup/plugin-json@6.1.0(rollup@4.60.4)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - optionalDependencies: - rollup: 4.60.4 - - '@rollup/plugin-node-resolve@16.0.3(rollup@4.60.4)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-module: 1.0.0 - resolve: 1.22.12 - optionalDependencies: - rollup: 4.60.4 - - '@rollup/pluginutils@5.3.0(rollup@4.60.4)': + '@rollup/pluginutils@5.3.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.4 - optionalDependencies: - rollup: 4.60.4 - - '@rollup/rollup-android-arm-eabi@4.60.4': - optional: true - - '@rollup/rollup-android-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-x64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-arm64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-x64@4.60.4': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-x64-musl@4.60.4': - optional: true - - '@rollup/rollup-openbsd-x64@4.60.4': - optional: true - - '@rollup/rollup-openharmony-arm64@4.60.4': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.60.4': - optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -5759,55 +5434,45 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@sveltejs/acorn-typescript@1.0.10(acorn@8.16.0)': - dependencies: - acorn: 8.16.0 - '@sveltejs/acorn-typescript@1.0.13(acorn@8.19.0)': dependencies: acorn: 8.19.0 - '@sveltejs/adapter-node@5.3.2(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))': + '@sveltejs/adapter-node@6.0.1(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))': dependencies: - '@rollup/plugin-commonjs': 28.0.9(rollup@4.60.4) - '@rollup/plugin-json': 6.1.0(rollup@4.60.4) - '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.4) - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) - rollup: 4.60.4 + '@sveltejs/kit': 3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) - '@sveltejs/enhanced-img@1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(rollup@4.60.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': + '@sveltejs/enhanced-img@1.0.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(@types/node@24.12.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) magic-string: 1.4.5 sharp: 0.35.5(@types/node@24.12.4) svelte: 5.57.3(@typescript-eslint/types@8.60.0) vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) - vite-imagetools: 12.0.1(@types/node@24.12.4)(rollup@4.60.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + vite-imagetools: 12.0.1(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) zimmerframe: 1.1.4 transitivePeerDependencies: - '@types/node' - rollup - '@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': + '@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': dependencies: + '@rolldown/pluginutils': 1.0.1 '@standard-schema/spec': 1.1.0 - '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0) '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) - '@types/cookie': 0.6.0 - acorn: 8.16.0 - cookie: 0.6.0 - devalue: 5.8.1 + cookie: 2.0.1 + devalue: 5.9.4 esm-env: 1.2.2 - kleur: 4.1.5 - magic-string: 0.30.21 + magic-string: 1.4.5 mrmime: 2.0.1 - set-cookie-parser: 3.2.1 sirv: 3.0.2 svelte: 5.57.3(@typescript-eslint/types@8.60.0) vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) optionalDependencies: typescript: 6.0.3 + '@sveltejs/load-config@0.2.2': {} + '@sveltejs/load-config@0.2.3': {} '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0))': @@ -5895,8 +5560,6 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/cookie@0.6.0': {} - '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -5912,8 +5575,6 @@ snapshots: dependencies: '@types/estree': 1.0.9 - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/hast@3.0.4': @@ -5944,8 +5605,6 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/resolve@1.20.2': {} - '@types/retry@0.12.0': {} '@types/unist@2.0.11': {} @@ -6200,7 +5859,7 @@ snapshots: base64-js@1.5.1: {} - better-auth@1.7.14(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7): + better-auth@1.7.14(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vitest@4.1.7): dependencies: '@better-auth/core': 1.7.14(@better-auth/utils@0.5.1)(@better-fetch/fetch@1.3.3)(better-call@1.4.4(zod@4.6.7))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.5.5) '@better-auth/drizzle-adapter': 1.7.14(@better-auth/core@1.7.14(@better-auth/utils@0.5.1)(@better-fetch/fetch@1.3.3)(better-call@1.4.4(zod@4.6.7))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.5.5))(@better-auth/utils@0.5.1) @@ -6220,7 +5879,7 @@ snapshots: nanostores: 1.5.5 zod: 4.6.7 optionalDependencies: - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/kit': 3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) svelte: 5.57.3(@typescript-eslint/types@8.60.0) vitest: 4.1.7(@types/node@24.12.4)(@vitest/coverage-v8@4.1.7)(@vitest/ui@4.1.7)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) transitivePeerDependencies: @@ -6237,15 +5896,15 @@ snapshots: bignumber.js@9.3.1: {} - bits-ui@2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): + bits-ui@2.19.2(@internationalized/date@3.12.1)(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: '@floating-ui/core': 1.7.5 '@floating-ui/dom': 1.7.6 '@internationalized/date': 3.12.1 esm-env: 1.2.2 - runed: 0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) + runed: 0.35.1(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) svelte: 5.57.3(@typescript-eslint/types@8.60.0) - svelte-toolbelt: 0.10.6(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) + svelte-toolbelt: 0.10.6(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) tabbable: 6.4.0 transitivePeerDependencies: - '@sveltejs/kit' @@ -6346,8 +6005,6 @@ snapshots: commander@8.3.0: {} - commondir@1.0.1: {} - concat-map@0.0.1: {} conf@13.1.0: @@ -6368,7 +6025,7 @@ snapshots: convert-source-map@2.0.0: {} - cookie@0.6.0: {} + cookie@2.0.1: {} crelt@1.0.6: {} @@ -6414,8 +6071,6 @@ snapshots: detect-libc@2.1.2: {} - devalue@5.8.1: {} - devalue@5.9.4: {} devlop@1.1.0: @@ -6442,8 +6097,6 @@ snapshots: env-paths@3.0.0: {} - es-errors@1.3.0: {} - es-module-lexer@2.3.2: {} escalade@3.2.0: {} @@ -6670,8 +6323,6 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - gaxios@7.1.4: dependencies: extend: 3.0.2 @@ -6739,10 +6390,6 @@ snapshots: has-flag@4.0.0: {} - hasown@2.0.3: - dependencies: - function-bind: 1.1.2 - hast-util-from-html@2.0.3: dependencies: '@types/hast': 3.0.4 @@ -6881,10 +6528,6 @@ snapshots: is-arrayish@0.3.4: {} - is-core-module@2.16.2: - dependencies: - hasown: 2.0.3 - is-decimal@2.0.1: {} is-extglob@2.1.1: {} @@ -6897,14 +6540,8 @@ snapshots: is-hexadecimal@2.0.1: {} - is-module@1.0.0: {} - is-plain-obj@4.1.0: {} - is-reference@1.2.1: - dependencies: - '@types/estree': 1.0.9 - is-reference@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -6979,8 +6616,6 @@ snapshots: dependencies: '@keyv/serialize': 1.1.1 - kleur@4.1.5: {} - known-css-properties@0.37.0: {} kysely@0.28.17: {} @@ -7621,8 +7256,6 @@ snapshots: path-key@3.1.1: {} - path-parse@1.0.7: {} - pathe@2.0.3: {} picocolors@1.1.1: {} @@ -8179,13 +7812,6 @@ snapshots: resolve-from@4.0.0: {} - resolve@1.22.12: - dependencies: - es-errors: 1.3.0 - is-core-module: 2.16.2 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - responselike@4.0.2: dependencies: lowercase-keys: 3.0.0 @@ -8213,47 +7839,16 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.2 '@rolldown/binding-win32-x64-msvc': 1.0.2 - rollup@4.60.4: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 - fsevents: 2.3.3 - rou3@0.9.2: {} - runed@0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): + runed@0.35.1(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: dequal: 2.0.3 esm-env: 1.2.2 lz-string: 1.5.0 svelte: 5.57.3(@typescript-eslint/types@8.60.0) optionalDependencies: - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) + '@sveltejs/kit': 3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)) sade@1.8.1: dependencies: @@ -8425,8 +8020,6 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.7.7(picomatch@4.0.4)(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -8452,10 +8045,10 @@ snapshots: optionalDependencies: svelte: 5.57.3(@typescript-eslint/types@8.60.0) - svelte-toolbelt@0.10.6(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): + svelte-toolbelt@0.10.6(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)): dependencies: clsx: 2.1.1 - runed: 0.35.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) + runed: 0.35.1(@sveltejs/kit@3.1.1(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.57.3(@typescript-eslint/types@8.60.0))(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)))(svelte@5.57.3(@typescript-eslint/types@8.60.0)) style-to-object: 1.0.14 svelte: 5.57.3(@typescript-eslint/types@8.60.0) transitivePeerDependencies: @@ -8648,9 +8241,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-imagetools@12.0.1(@types/node@24.12.4)(rollup@4.60.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)): + vite-imagetools@12.0.1(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(yaml@2.7.0)): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + '@rollup/pluginutils': 5.3.0 imagetools-core: 12.0.0 sharp: 0.35.5(@types/node@24.12.4) vite: 8.0.14(@types/node@24.12.4)(yaml@2.7.0) -- 2.54.0 From a6910c9c1253c835c3cfa3945704f79a02689096 Mon Sep 17 00:00:00 2001 From: eleith Date: Sat, 31 Oct 2026 06:58:32 -0700 Subject: [PATCH 3/4] [web] finish SvelteKit 3 API cleanup --- .changeset/kit3-api-cleanup.md | 6 + apps/web/MIGRATION_TASKS.md | 184 ------------------ apps/web/src/hooks.server.ts | 6 +- .../src/routes/(auth)/admin/+layout.svelte | 2 +- .../admin/piece/[...path]/source/+page.svelte | 4 +- .../routes/(auth)/admin/publish/+page.svelte | 4 +- .../routes/(auth)/admin/publish/page.test.ts | 2 +- .../(auth)/api/admin/publish/+server.ts | 17 +- .../(auth)/api/admin/publish/audit/+server.ts | 5 +- .../(auth)/api/admin/unpublish/+server.ts | 9 +- .../(auth)/api/admin/validate/+server.ts | 9 +- apps/web/src/routes/api/health/+server.ts | 4 +- apps/web/src/routes/api/pieces/+server.ts | 6 +- apps/web/src/routes/api/search/+server.ts | 4 +- .../src/routes/api/session/verify/+server.ts | 3 +- .../routes/api/session/verify/server.test.ts | 1 + apps/web/src/routes/signin/+page.svelte | 2 +- 17 files changed, 46 insertions(+), 222 deletions(-) create mode 100644 .changeset/kit3-api-cleanup.md delete mode 100644 apps/web/MIGRATION_TASKS.md diff --git a/.changeset/kit3-api-cleanup.md b/.changeset/kit3-api-cleanup.md new file mode 100644 index 00000000..f2b71a87 --- /dev/null +++ b/.changeset/kit3-api-cleanup.md @@ -0,0 +1,6 @@ +--- +"@luzzle/web": patch +--- + +Finish the SvelteKit 3 API migration with native JSON responses and refresh APIs, +retaining response statuses/cache headers, local navigation and editor draft guards. diff --git a/apps/web/MIGRATION_TASKS.md b/apps/web/MIGRATION_TASKS.md deleted file mode 100644 index 2261a4c4..00000000 --- a/apps/web/MIGRATION_TASKS.md +++ /dev/null @@ -1,184 +0,0 @@ - - -# SvelteKit 3 migration tasks - -Some migrations are uncommon, context-dependent, or disproportionately difficult to automate safely. This document lists them. - -The findings are intentionally broad and may include files that do not require changes. - -AI agents can work through the migration tasks below one at a time: - -1. Read the linked documentation or pull request before changing code. -2. Inspect every listed file and its surrounding context. -3. Ignore a finding when the file is not actually affected. -4. Make the smallest necessary change. Do not perform unrelated refactors or introduce unnecessary abstractions. -5. Ask the user when application intent cannot be inferred. -6. Run the project's appropriate checks after editing. - -Also search the project for `@migration-task` comments. They may describe additional manual migration work that must be resolved. - -## Migration tasks - -### Migrate remaining `base`, `assets`, and `resolveRoute` from `$app/paths` usages - -The automatic migration can leave `base`, `assets`, and `resolveRoute` usages from `$app/paths`. - -#### What to do - -The deprecated base, assets, and resolveRoute exports have been removed from $app/paths. Use asset and resolve instead:```diff// instead of this... -const pathname = base + resolveRoute('/blog/[slug]', { slug }); -const file = assets + '/foo.png'; - -// ...do this: -const pathname = resolve('/blog/[slug]', { slug }); -const file = asset('foo.png'); - -``` -The `Pathname` and `Asset` types have also been renamed to `Path` and `AssetPath`, and the leading `/` has been removed from those types — so `asset('/foo.png')` should now be `asset('foo.png')`, and pathnames passed to `resolve` no longer start with `/` (e.g. `resolve('blog/hello-world')`). Only route IDs start with `/` now. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-paths-base-assets-and-resolveRoute-removed) - -#### Files to review - -- [ ] `src/lib/pieces/helpers.test.ts` -- [ ] `src/lib/pieces/viewer/ShareViewport.test.ts` -- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts` -- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts` -- [ ] `src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts` -- [ ] `src/routes/(auth)/admin/piece/[...path]/published/page.test.ts` - -### Copy `page.url` before mutating it - -`page.url` and its search parameters are readonly in SvelteKit 3. - -#### What to do - -Create a mutable copy with `new URL(page.url.href)`, mutate that copy, and use it for navigation or serialization. Leave readonly accesses unchanged. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-state-page.url-is-now-readonly) - -#### Files to review - -- [ ] `src/lib/content/components/ContentBanner.svelte` -- [ ] `src/routes/(auth)/admin/+layout.svelte` -- [ ] `src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte` -- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` - -### Review `goto` destinations - -`goto(...)` now rejects destinations that do not resolve to an application route. - -#### What to do - -Confirm each destination is an internal route. Use `window.location.href` for external browser navigation, and handle rejected dynamic destinations where they can fail to resolve. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-goto-rejects-for-URLs-that-don't-resolve-to-a-route) - -#### Files to review - -- [ ] `src/lib/components/layout/simple/SearchDialog.svelte` -- [ ] `src/routes/(auth)/admin/+layout.svelte` -- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` -- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` -- [ ] `src/routes/signin/+page.svelte` - -### Replace `invalidateAll` with `refreshAll` - -`invalidateAll` is deprecated in favor of `refreshAll` - -#### What to do - -Replace `invalidateAll()` with `refreshAll()`. Note that `refreshAll()` will keep `page.state` intact. Check if that has unwanted impact on the application; if yes point this out in a comment with steps to take instead of replacing that instance of `invalidateAll()`. Similarly, replace goto(..., { invalidateAll: true }) with goto(..., { refreshAll: true }). - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-navigation-invalidateAll-is-deprecated-in-favour-of-refreshAll) - -#### Files to review - -- [ ] `src/lib/components/layout/simple/SearchDialog.svelte` -- [ ] `src/routes/(auth)/admin/+layout.svelte` -- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` -- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` -- [ ] `src/routes/signin/+page.svelte` - -### Review enhanced forms with explicit actions - -Enhanced forms that submit to another page now navigate to that page. - -#### What to do - -Determine whether each action resolves to another page and confirm navigation is intended. If the form must remain on the current page, implement that behavior explicitly in the enhancement callback. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#$app-forms) - -#### Files to review - -- [ ] `src/routes/(auth)/admin/asset/editor/[...asset]/+page.svelte` -- [ ] `src/routes/(auth)/admin/health/+page.svelte` -- [ ] `src/routes/(auth)/admin/piece/[...path]/source/+page.svelte` - -### CORS for static assets in development is handled by Vite - -SvelteKit no longer sets `access-control-allow-origin: *` on every static asset request in development. CORS is now delegated to Vite's built-in middleware. - -#### What to do - -If you rely on cross-origin access to static assets in dev, configure it in your Vite config by adding server.cors.origin = "*" to your config. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Security-CORS-for-static-assets-in-development-is-handled-by-Vite) - -### `json` and `text` are deprecated - -The `json(...)` and `text(...)` helpers from `@sveltejs/kit` for generating responses are deprecated. - -#### What to do - -Use `Response.json(...)` and `new Response(text)` instead. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#sveltejs-kit-json-and-text-are-deprecated) - -#### Files to review - -- [ ] `src/hooks.server.ts` -- [ ] `src/routes/(auth)/api/admin/publish/+server.ts` -- [ ] `src/routes/(auth)/api/admin/publish/audit/+server.ts` -- [ ] `src/routes/(auth)/api/admin/unpublish/+server.ts` -- [ ] `src/routes/(auth)/api/admin/validate/+server.ts` -- [ ] `src/routes/api/health/+server.ts` -- [ ] `src/routes/api/pieces/+server.ts` -- [ ] `src/routes/api/search/+server.ts` -- [ ] `src/routes/api/session/verify/+server.ts` - -### Move adapter-node `ORIGIN` to `paths.origin` - -The adapter-node `ORIGIN` environment variable is removed. - -#### What to do - -Find where `ORIGIN` is set (`.env` files, scripts, hosting dashboard, ...) and move the public-facing origin to `paths.origin` in `sveltekit(...)`. Do not delete `ORIGIN`: it is ignored by adapter-node now and may have other uses. - -#### References - -- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Adapters-adapter-node) - -## Final verification - -- [ ] Review every migration task and ignore any irrelevant findings. -- [ ] Resolve remaining `@migration-task` comments. -- [ ] Run the project's type checker and tests. -- [ ] Build the project successfully. -- [ ] Delete this file when all migration work is complete. -``` diff --git a/apps/web/src/hooks.server.ts b/apps/web/src/hooks.server.ts index 9854f2dc..427821f5 100644 --- a/apps/web/src/hooks.server.ts +++ b/apps/web/src/hooks.server.ts @@ -1,4 +1,4 @@ -import { json, redirect } from '@sveltejs/kit' +import { redirect } from '@sveltejs/kit' import { building } from '$app/env' import { config } from '#lib/server/config.js' import { createAuth, createAuthHandle } from '#lib/server/auth/index.js' @@ -53,13 +53,13 @@ const guardHandle: Handle = async ({ event, resolve }) => { if (!auth) { if (shouldRedirect) throw redirect(302, '/') - return json({ message: 'Forbidden' }, { status: 403, headers }) + return Response.json({ message: 'Forbidden' }, { status: 403, headers }) } const session = await event.locals.auth() if (session) return resolve(event) - if (!shouldRedirect) return json({ message: 'Unauthorized' }, { status: 401, headers }) + if (!shouldRedirect) return Response.json({ message: 'Unauthorized' }, { status: 401, headers }) const params = new URLSearchParams({ redirectTo: event.url.pathname + event.url.search }) diff --git a/apps/web/src/routes/(auth)/admin/+layout.svelte b/apps/web/src/routes/(auth)/admin/+layout.svelte index ba111867..e9e007ed 100644 --- a/apps/web/src/routes/(auth)/admin/+layout.svelte +++ b/apps/web/src/routes/(auth)/admin/+layout.svelte @@ -24,7 +24,7 @@ } localStorage.removeItem('luzzle.admin') try { - await goto('/', { invalidateAll: true }) + await goto('/', { refreshAll: true }) } catch { signOutError = 'navigation' } finally { diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte index 3a9e65fb..5f9cb14c 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/source/+page.svelte @@ -16,7 +16,7 @@ import { Dialog } from 'bits-ui' import { fade, fly } from 'svelte/transition' import { navigating, page } from '$app/state' - import { beforeNavigate, goto, invalidateAll } from '$app/navigation' + import { beforeNavigate, goto, refreshAll } from '$app/navigation' import { onMount, onDestroy, tick, untrack } from 'svelte' let { data, form }: PageProps = $props() @@ -175,7 +175,7 @@ if (result.state === 'failed' && result.reason === 'sign-in') return if (navigating.to) return try { - await invalidateAll() + await refreshAll() } catch { if (isCurrent()) unpublishRefreshFailed = true } diff --git a/apps/web/src/routes/(auth)/admin/publish/+page.svelte b/apps/web/src/routes/(auth)/admin/publish/+page.svelte index 82b76b79..f2e93c8f 100644 --- a/apps/web/src/routes/(auth)/admin/publish/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/publish/+page.svelte @@ -1,6 +1,6 @@
@@ -38,7 +40,11 @@ {:else if isDirty} Unsaved {:else} - + Saved {/if} @@ -47,7 +53,7 @@ {#if isUnpublishing} Unpublishing… {:else if hasPublishedVersion} - Published + Published {:else} Unpublished {/if} diff --git a/apps/web/src/lib/components/editor/PieceToolbar.test.ts b/apps/web/src/lib/components/editor/PieceToolbar.test.ts index 6995a254..253743b1 100644 --- a/apps/web/src/lib/components/editor/PieceToolbar.test.ts +++ b/apps/web/src/lib/components/editor/PieceToolbar.test.ts @@ -1,8 +1,21 @@ import { createRawSnippet, type ComponentProps } from 'svelte' import { render } from 'svelte/server' -import { expect, test } from 'vitest' +import { beforeEach, expect, test, vi } from 'vitest' +import { resolve } from '$app/paths' +import { + encodeArchivePath, + previewHref as piecePreviewHref, + publishedHref as piecePublishedHref +} from '#lib/pieces/navigation.js' import PieceToolbar from './PieceToolbar.svelte' +vi.mock('$app/paths', () => ({ + resolve: vi.fn((route: string, params: { path: string }) => + route.replace('/(auth)', '').replace('[...path]', () => params.path) + ) +})) +beforeEach(() => vi.clearAllMocks()) + const file = 'books/源 #?%.books.md' const previewHref = '/admin/piece/books/%E6%BA%90%20%23%3F%25.books.md/preview' const publishedHref = '/admin/piece/books/%E6%BA%90%20%23%3F%25.books.md/published' @@ -107,4 +120,20 @@ test('editor actions do not duplicate archive navigation', () => { expect(output).not.toContain('Back to archive') }) -// SSR verifies labels, links and disabled/form semantics, not clicks or responsive layout. +test('encoded file paths enter the app resolver unchanged', () => { + const file = 'books/literal%2F 源 #?%.books.md' + const preview = piecePreviewHref(file) + const published = piecePublishedHref(file) + const output = html({ file }) + + expect(preview).toContain('literal%252F%20%E6%BA%90%20%23%3F%25.books.md') + expect(vi.mocked(resolve).mock.calls).toEqual([ + ['/(auth)/admin/piece/[...path]/preview', { path: encodeArchivePath(file) }], + ['/(auth)/admin/piece/[...path]/published', { path: encodeArchivePath(file) }] + ]) + expect(output).toContain(`href="${preview}"`) + expect(output).toContain(`href="${published}"`) +}) + +// Mocked SSR verifies resolver inputs, labels, links and disabled/form semantics, +// not browser/native resolution, clicks or responsive layout. diff --git a/apps/web/src/lib/components/layout/simple/NavBanner.svelte b/apps/web/src/lib/components/layout/simple/NavBanner.svelte index 5b97459d..d510ba2f 100644 --- a/apps/web/src/lib/components/layout/simple/NavBanner.svelte +++ b/apps/web/src/lib/components/layout/simple/NavBanner.svelte @@ -1,4 +1,5 @@
- + Published
diff --git a/apps/web/src/lib/pieces/viewer/ViewerHeader.test.ts b/apps/web/src/lib/pieces/viewer/ViewerHeader.test.ts index e42df9ea..a7230b18 100644 --- a/apps/web/src/lib/pieces/viewer/ViewerHeader.test.ts +++ b/apps/web/src/lib/pieces/viewer/ViewerHeader.test.ts @@ -1,19 +1,33 @@ import { readFileSync } from 'node:fs' import { beforeEach, expect, test, vi } from 'vitest' import { render } from 'svelte/server' -import type { PieceLinks } from '#lib/pieces/navigation.js' import ViewerHeader from './ViewerHeader.svelte' -const { page, resolve } = vi.hoisted(() => ({ - page: { url: new URL('https://app.test/') }, - resolve: vi.fn(() => { - throw new Error('Could not get the request store. This is an internal error.') - }) -})) +const { page, mount, resolve } = vi.hoisted(() => { + const mount = { prefix: '' } + return { + mount, + page: { + url: new URL('https://app.test/'), + route: { id: '/(auth)/admin/piece/[...path]/published' } + }, + resolve: vi.fn( + (route: string, params: { path: string }) => + `${mount.prefix}${route.replace('/(auth)', '').replace('[...path]', params.path)}` + ) + } +}) vi.mock('$app/state', () => ({ page })) vi.mock('$app/paths', () => ({ resolve })) -function links(root = '/admin/piece/books/%E6%BA%90%20%23%3F%25.books.md'): PieceLinks { +// This resolver double tests delegation and encoded params, not native Kit/browser routing. +const file = 'books/源 #?%.books.md' +const encodedPath = 'books/%E6%BA%90%20%23%3F%25.books.md' +const publishedRoute = '/(auth)/admin/piece/[...path]/published' +const tabSuffixes = { overview: '', page: '/page', icon: '/icon', share: '/share' } + +function links(path = encodedPath, prefix = '') { + const root = `${prefix}/admin/piece/${path}` return { source: `${root}/source`, overview: `${root}/published`, @@ -23,17 +37,29 @@ function links(root = '/admin/piece/books/%E6%BA%90%20%23%3F%25.books.md'): Piec } } +function expectNavigation(path = encodedPath) { + const routes = [ + '/(auth)/admin/piece/[...path]/source', + ...Object.values(tabSuffixes).map((suffix) => publishedRoute + suffix) + ] + expect(resolve).toHaveBeenCalledTimes(routes.length) + for (const route of routes) expect(resolve).toHaveBeenCalledWith(route, { path }) +} + beforeEach(() => { page.url = new URL('https://app.test/') + page.route.id = publishedRoute + mount.prefix = '' vi.clearAllMocks() }) test.each(['overview', 'page', 'icon', 'share'] as const)( - '%s is active using serialized loader links without a render request store', + '%s is active by route ID and delegates navigation to resolve', (tab) => { const navigation = links() page.url = new URL(navigation[tab], 'https://app.test') - const html = render(ViewerHeader, { props: { links: navigation } }).body + page.route.id = publishedRoute + tabSuffixes[tab] + const html = render(ViewerHeader, { props: { file } }).body expect(html).toContain(`href="${navigation.source}"`) expect(html).toContain('aria-label="Back to editor"') expect(html).toContain('title="Back to editor"') @@ -44,42 +70,69 @@ test.each(['overview', 'page', 'icon', 'share'] as const)( expect(html).not.toContain('Logs') expect(html).not.toContain(' { - const navigation = links('/garden/admin/piece/books/book.md') - page.url = new URL(navigation.icon, 'https://app.test') - const html = render(ViewerHeader, { props: { links: navigation } }).body - expect(html).toContain(`href="${navigation.icon}" aria-current="page"`) - expect(html).toContain(`href="${navigation.source}"`) - expect(resolve).not.toHaveBeenCalled() +test.each([ + [file, encodedPath], + ['folder with spaces/a book.books.md', 'folder%20with%20spaces/a%20book.books.md'], + ['nested/literal%2F.books.md', 'nested/literal%252F.books.md'], + ['books/📚.books.md', 'books/%F0%9F%93%9A.books.md'], + ['published/page/icon/share/source/file.md', 'published/page/icon/share/source/file.md'] +])('raw file %s becomes an encoded-once path param for all five routes', (raw, encoded) => { + // A different URL identity must not be used to derive the file or its mount. + page.url = new URL('https://app.test/garden/admin/piece/other.md/published/share?x=1#old') + page.route.id = publishedRoute + '/share' + const html = render(ViewerHeader, { props: { file: raw } }).body + for (const href of Object.values(links(encoded))) expect(html).toContain(`href="${href}"`) + expectNavigation(encoded) + expect(html).not.toContain('/garden/') + expect(html).not.toContain('other.md') }) -test('trailing slash still identifies the active view', () => { - const navigation = links() - page.url = new URL(navigation.overview + '/', 'https://app.test') - const html = render(ViewerHeader, { props: { links: navigation } }).body - expect(html).toContain(`href="${navigation.overview}" aria-current="page"`) +test.each(['overview', 'page', 'icon', 'share'] as const)( + 'controlled resolver prefix and trailing slash leave %s active by route ID', + (tab) => { + mount.prefix = '/garden' + const navigation = links(encodedPath, mount.prefix) + page.url = new URL(`${navigation[tab]}/?x=1#fragment`, 'https://app.test') + page.route.id = publishedRoute + tabSuffixes[tab] + const html = render(ViewerHeader, { props: { file } }).body + expect(html).toContain(`href="${navigation[tab]}" aria-current="page"`) + expect(html.match(/aria-current="page"/g)).toHaveLength(1) + expect(html).toContain(`href="${navigation.source}"`) + // Prefix handling belongs to resolve, not the raw file or its encoded param. + expectNavigation() + } +) + +test('active tab follows route ID rather than a coincidentally matching URL pathname', () => { + page.url = new URL(links().icon, 'https://app.test') + page.route.id = publishedRoute + '/page' + const html = render(ViewerHeader, { props: { file } }).body + expect(html).toContain(`href="${links().page}" aria-current="page"`) + expect(html).not.toContain(`href="${links().icon}" aria-current="page"`) + expect(html.match(/aria-current="page"/g)).toHaveLength(1) }) -test('replacement loader links do not retain a previous source identity', () => { - render(ViewerHeader, { props: { links: links('/admin/piece/first.md') } }) - const navigation = links('/admin/piece/second.md') - page.url = new URL(navigation.share, 'https://app.test') - const html = render(ViewerHeader, { props: { links: navigation } }).body +test('replacement file renders do not retain a previous source identity', () => { + render(ViewerHeader, { props: { file: 'first.md' } }) + vi.clearAllMocks() + page.url = new URL(links('second.md').share, 'https://app.test') + page.route.id = publishedRoute + '/share' + const html = render(ViewerHeader, { props: { file: 'second.md' } }).body expect(html).toContain('/second.md/published/share') expect(html).toContain('aria-current="page"') expect(html).not.toContain('first') + expectNavigation('second.md') }) test('back link uses the same decorative arrow icon as Preview, not a visible text label', () => { - const navigation = links() - const html = render(ViewerHeader, { props: { links: navigation } }).body + const html = render(ViewerHeader, { props: { file } }).body const anchors = html.match(/"']|"[^"]*"|'[^']*')*>[\s\S]*?<\/a>/g) ?? [] const back = anchors.find((anchor) => anchor.includes('aria-label="Back to editor"'))! - expect(back).toContain(`href="${navigation.source}"`) + expect(back).toContain(`href="${links().source}"`) expect(back).toContain('title="Back to editor"') expect(back).toMatch(/]*aria-hidden="true"/) expect(back).toMatch(/]*focusable="false"/) @@ -90,7 +143,7 @@ test('back link uses the same decorative arrow icon as Preview, not a visible te .trim() ).toBe('') expect(back).not.toContain('target=') - expect(resolve).not.toHaveBeenCalled() + expectNavigation() }) test('Published header consumes the narrowly scoped shared bar stylesheet', () => { @@ -141,7 +194,7 @@ test('shared bar stacks mode/back above full-width wrapping tabs on mobile, with expect(css).toContain('min-height: 44px') expect(mobile).not.toContain('display: none') expect(mobile).not.toContain('position: absolute') - const html = render(ViewerHeader, { props: { links: links() } }).body + const html = render(ViewerHeader, { props: { file } }).body expect(html).toContain('
@@ -20,7 +26,7 @@

assets:

{#each files.assets as asset (asset.path)} - {asset.name} + {asset.name} {/each}
@@ -31,7 +37,7 @@

directories

{#each files.directories as dir (dir.path)} - {dir.name} + {dir.name} {/each}
diff --git a/apps/web/src/routes/(auth)/admin/directory/[...directory]/page.test.ts b/apps/web/src/routes/(auth)/admin/directory/[...directory]/page.test.ts new file mode 100644 index 00000000..8fba52bb --- /dev/null +++ b/apps/web/src/routes/(auth)/admin/directory/[...directory]/page.test.ts @@ -0,0 +1,62 @@ +import { expect, test, vi } from 'vitest' +import { render } from 'svelte/server' +import type { ComponentProps } from 'svelte' +import { makeConfig } from '#lib/server/config.fixture.js' +import Page from './+page.svelte' + +const { resolve } = vi.hoisted(() => ({ + resolve: vi.fn((route: string, params?: { path: string }) => { + const path = route.replace('/(auth)', '').replace('[...path]', () => params?.path ?? '') + return path.startsWith('/') ? path : `/${path}` + }) +})) +vi.mock('$app/paths', () => ({ resolve })) + +// Mocked SSR proves native route params, encoded application paths and rendered +// targets, not native Kit resolution or browser navigation. +test('directory links encode segments once and retain nested separators and trailing slash', () => { + const props = { + data: { + config: makeConfig(), + meta: { title: 'Directory' }, + pieceTypes: ['books'], + directory: { parent: '棚 #?%', current: '棚 #?%/literal%2F' }, + files: { + pieces: [ + { + file: '棚 #?%/literal%2F/源 #?%.books.md', + slug: 'Piece', + type: 'books', + format: '.md' + } + ], + assets: [{ path: '棚 #?%/literal%2F/.assets/源 #?%.png', name: 'Asset' }], + directories: [{ path: '棚 #?%/literal%2F/子 #?%/', name: 'Directory' }] + } + } + } satisfies ComponentProps + const targets = [ + 'admin/piece/%E6%A3%9A%20%23%3F%25/literal%252F/%E6%BA%90%20%23%3F%25.books.md/source', + 'admin/asset/editor/%E6%A3%9A%20%23%3F%25/literal%252F/.assets/%E6%BA%90%20%23%3F%25.png', + 'admin/directory/%E6%A3%9A%20%23%3F%25/literal%252F/%E5%AD%90%20%23%3F%25/' + ] + const html = render(Page, { props }).body.replace(//g, '') + const links = html.match(/]*>[\s\S]*?<\/a>/g) ?? [] + expect(links).toHaveLength(3) + for (const [index, label] of ['Piece', 'Asset', 'Directory'].entries()) { + expect(links[index]).toContain(`href="/${targets[index]}"`) + expect(links[index]).toContain(`>${label}`) + } + // Native piece params and asset/directory pathstrings are already encoded; + // separators and the directory's final slash are left intact. + expect(resolve.mock.calls).toEqual([ + [ + '/(auth)/admin/piece/[...path]/source', + { + path: '%E6%A3%9A%20%23%3F%25/literal%252F/%E6%BA%90%20%23%3F%25.books.md' + } + ], + [targets[1]], + [targets[2]] + ]) +}) diff --git a/apps/web/src/routes/(auth)/admin/page.test.ts b/apps/web/src/routes/(auth)/admin/page.test.ts index 380bec3e..7153c249 100644 --- a/apps/web/src/routes/(auth)/admin/page.test.ts +++ b/apps/web/src/routes/(auth)/admin/page.test.ts @@ -2,8 +2,16 @@ import { afterEach, beforeEach, expect, test, vi } from 'vitest' import { render } from 'svelte/server' import type { ComponentProps } from 'svelte' import type { WebPieces } from '@luzzle/web.db' +import { resolve } from '$app/paths' import Page from './+page.svelte' +vi.mock('$app/paths', () => ({ + resolve: vi.fn((route: string, params?: { path: string }) => { + const path = route.replace('/(auth)', '').replace('[...path]', () => params?.path ?? '') + return path.startsWith('/') ? path : `/${path}` + }) +})) + const now = Date.parse('2026-01-02T12:00:00Z') const piece: WebPieces = { id: 'book-1', @@ -18,6 +26,7 @@ const piece: WebPieces = { } beforeEach(() => { + vi.clearAllMocks() vi.useFakeTimers() vi.setSystemTime(now) }) @@ -75,3 +84,12 @@ test('recent rows retain the slug and added-time fallbacks', () => { test('the recently edited section stays hidden when empty', () => { expect(html([])).not.toContain('recent-list') }) + +// The native resolver receives already-encoded params, not an erased pathstring. +test('recent files preserve encoded archive segments in native route params', () => { + const file = 'books/literal%2F/源 #?%.books.md' + const path = 'books/literal%252F/%E6%BA%90%20%23%3F%25.books.md' + const output = html([{ ...piece, file_path: file }]) + expect(resolve).toHaveBeenCalledWith('/(auth)/admin/piece/[...path]/source', { path }) + expect(output).toContain(`href="/admin/piece/${path}/source"`) +}) diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte index 1120d1fc..b1c1e4b6 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte @@ -2,8 +2,9 @@ import '#lib/pieces/viewer/ViewerBar.css' import { onMount, onDestroy } from 'svelte' import { page } from '$app/state' + import { resolve } from '$app/paths' import type { LayoutProps } from './$types' - import { editorHref, previewHref } from '#lib/pieces/navigation.js' + import { encodeArchivePath } from '#lib/pieces/navigation.js' import ArrowLeftIcon from 'virtual:icons/ph/arrow-left' import PieceOverview from '#lib/pieces/viewer/PieceOverview.svelte' @@ -50,12 +51,14 @@ let eventSource: EventSource | null = null - const editorUrl = editorHref(data.file) - - const logsUrl = previewHref(data.file, data.job) - const pageUrl = `${logsUrl}/page` - const iconUrl = `${logsUrl}/icon` - const shareUrl = `${logsUrl}/share` + const path = encodeArchivePath(data.file) + const jobId = encodeURIComponent(data.job) + const previewRoute = '/(auth)/admin/piece/[...path]/preview/[jobId]' + const editorUrl = resolve('/(auth)/admin/piece/[...path]/source', { path }) + const logsUrl = resolve(previewRoute, { path, jobId }) + const pageUrl = resolve(`${previewRoute}/page`, { path, jobId }) + const iconUrl = resolve(`${previewRoute}/icon`, { path, jobId }) + const shareUrl = resolve(`${previewRoute}/share`, { path, jobId }) function startWatching() { if (eventSource) eventSource.close() @@ -125,14 +128,14 @@ overviewoverview @@ -141,8 +144,8 @@ @@ -151,8 +154,8 @@ diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/layout.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/layout.test.ts index ce7da332..0d735489 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/layout.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/layout.test.ts @@ -17,14 +17,31 @@ const publishedPiece = { } } -const { page, EventSource } = vi.hoisted(() => ({ - page: { url: new URL('https://app.test/') }, - EventSource: vi.fn() -})) +const { page, EventSource, resolver, resolve } = vi.hoisted(() => { + const resolver = { prefix: '' } + return { + page: { + url: new URL('https://app.test/'), + route: { id: '/(auth)/admin/piece/[...path]/preview/[jobId]' } + }, + EventSource: vi.fn(), + resolver, + resolve: vi.fn( + (route: string, params: { path: string; jobId?: string }) => + resolver.prefix + + route + .replace('/(auth)', '') + .replace('[...path]', () => params.path) + .replace('[jobId]', () => params.jobId ?? '') + ) + } +}) vi.mock('$app/state', () => ({ page })) +vi.mock('$app/paths', () => ({ resolve })) const file = 'books/book.books.md' const root = `/admin/piece/${file}/preview/preview-id` +const previewRoute = '/(auth)/admin/piece/[...path]/preview/[jobId]' const children = createRawSnippet(() => ({ render: () => '

Preview child fixture

' })) type Status = 'waiting' | 'running' | 'completed' | 'failed' | 'expired' @@ -60,8 +77,21 @@ function anchors(html: string) { return html.match(/"']|"[^"]*"|'[^']*')*>[\s\S]*?<\/a>/g) ?? [] } +function expectAppNavigation(path = file, jobId = 'preview-id', renders = 1) { + const calls = [ + ['/(auth)/admin/piece/[...path]/source', { path }], + [previewRoute, { path, jobId }], + [`${previewRoute}/page`, { path, jobId }], + [`${previewRoute}/icon`, { path, jobId }], + [`${previewRoute}/share`, { path, jobId }] + ] + expect(resolve.mock.calls).toEqual(Array.from({ length: renders }, () => calls).flat()) +} + beforeEach(() => { page.url = new URL(root, 'https://app.test') + page.route.id = previewRoute + resolver.prefix = '' vi.clearAllMocks() vi.stubGlobal('EventSource', EventSource) }) @@ -83,6 +113,7 @@ test.each(['/static', 'https://cdn.test/site-assets', ''])( expect(result.head).not.toContain('name="description"') expect(result.head).not.toContain('name="robots"') expect(result.body).toContain('Preview child fixture') + expect(resolve).not.toHaveBeenCalled() expect(EventSource).not.toHaveBeenCalled() } } @@ -128,7 +159,8 @@ test('Preview registers Share without retaining an OG route or redirect alias', expect(existsSync(new URL('./og/', import.meta.url))).toBe(false) }) -// SSR establishes markup/state contracts, not mount/SSE, CSS layout or client replacement. +// Mocked SSR establishes resolver inputs and markup/state contracts, not native Kit +// resolution, mount/SSE, CSS layout or client replacement. test.each(['waiting', 'running', 'completed', 'failed', 'expired'] as const)( 'Preview %s retains destinations and availability inside the shared responsive header', (status) => { @@ -167,6 +199,7 @@ test.each(['waiting', 'running', 'completed', 'failed', 'expired'] as const)( expect(header).not.toMatch(/>\s*og(?: \(html\))?\s*<\/a>/) expect(header).not.toContain('share (html)') expect(html.includes('Preview child fixture')).toBe(status === 'completed') + expectAppNavigation() expect(EventSource).not.toHaveBeenCalled() } ) @@ -175,22 +208,65 @@ test.each(['', '/page', '/icon', '/share'])( 'completed Preview %s uses the existing active-view conditions with native current-page semantics', (leaf) => { page.url = new URL(root + leaf, 'https://app.test') + page.route.id = previewRoute + leaf const links = anchors(body()) - const selected = links.filter((link) => link.includes('aria-current="page"')) - expect(selected).toHaveLength(1) - expect(selected[0]).toContain(`href="${root}${leaf}"`) - expect(selected[0]).toContain('active') + for (const [index, tab] of ['', '/page', '/icon', '/share'].entries()) { + const link = links[index + 1] + expect(link).toContain(`href="${root}${tab}"`) + expect(link.includes('aria-current="page"')).toBe(tab === leaf) + expect( + link + .match(/class="([^"]*)"/)?.[1] + .split(/\s+/) + .includes('active') + ).toBe(tab === leaf) + } + expectAppNavigation() + expect(EventSource).not.toHaveBeenCalled() + } +) + +// Deliberately controlled unit output, not native Kit/browser resolution proof. +// A full pathname must not be compared to the resolver's SSR-relative hrefs. +test.each(['', '/page', '/icon', '/share'])( + 'Preview %s stays active with SSR-relative resolver output in every job state', + (leaf) => { + resolver.prefix = '../../../../..' + page.url = new URL(root + leaf, 'https://app.test') + page.route.id = previewRoute + leaf + for (const status of ['waiting', 'running', 'completed', 'failed', 'expired'] as const) { + const html = body(status) + const links = anchors(html.slice(0, html.indexOf(''))) + expect(links).toHaveLength(5) + expect(links[0]).toContain(`href="${resolver.prefix}/admin/piece/${file}/source"`) + for (const [index, tab] of ['', '/page', '/icon', '/share'].entries()) { + const link = links[index + 1] + const disabled = index > 0 && status !== 'completed' + const classes = link.match(/class="([^"]*)"/)?.[1].split(/\s+/) ?? [] + expect(link).toContain(`href="${disabled ? '#' : resolver.prefix + root + tab}"`) + expect(classes.includes('active')).toBe(tab === leaf) + expect(link.includes('aria-current="page"')).toBe(tab === leaf) + expect(classes.includes('disabled')).toBe(disabled) + if (index > 0) expect(link).toContain(`aria-disabled="${disabled}"`) + else expect(link).not.toContain('aria-disabled') + } + expect(html.includes('Preview child fixture')).toBe(status === 'completed') + } + expectAppNavigation(file, 'preview-id', 5) expect(EventSource).not.toHaveBeenCalled() } ) test('Preview header retains encoded file and job identity reached through View', () => { const props = data('completed') - props.file = 'books/源 #?%.books.md' - props.job = 'job #?%' - const encodedRoot = '/admin/piece/books/%E6%BA%90%20%23%3F%25.books.md' - const preview = `${encodedRoot}/preview/job%20%23%3F%25` + props.file = 'books/literal%2F/源 #?%.books.md' + props.job = 'job /#?%2F' + const path = 'books/literal%252F/%E6%BA%90%20%23%3F%25.books.md' + const jobId = 'job%20%2F%23%3F%252F' + const encodedRoot = `/admin/piece/${path}` + const preview = `${encodedRoot}/preview/${jobId}` page.url = new URL(`${preview}/share?html`, 'https://app.test') + page.route.id = `${previewRoute}/share` const html = render(PreviewLayout, { props: { data: props, params: { path: props.file, jobId: props.job }, children } }).body @@ -201,11 +277,13 @@ test('Preview header retains encoded file and job identity reached through View' expect(links[index + 1]).toContain(`href="${preview}${leaf}"`) expect(links.filter((link) => link.includes('aria-current="page"'))).toHaveLength(1) expect(links.at(-1)).toContain('aria-current="page"') + expectAppNavigation(path, jobId) expect(EventSource).not.toHaveBeenCalled() }) test('obsolete html query does not create a mode, extra tab or different active view', () => { page.url = new URL(`${root}/share?html`, 'https://app.test') + page.route.id = `${previewRoute}/share` const withQuery = body() const links = anchors(withQuery) expect(links).toHaveLength(5) @@ -216,6 +294,8 @@ test('obsolete html query does not create a mode, extra tab or different active expect(withQuery).not.toContain('?html') expect(withQuery).not.toContain('share (html)') page.url = new URL(`${root}/share`, 'https://app.test') + page.route.id = `${previewRoute}/share` expect(body()).toBe(withQuery) + expectAppNavigation(file, 'preview-id', 2) expect(EventSource).not.toHaveBeenCalled() }) diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts index 578a05c4..d2e51a44 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/page.test.ts @@ -19,14 +19,39 @@ const publishedPiece = { } const { page, resolve } = vi.hoisted(() => ({ - page: { url: new URL('https://app.test/admin/piece/books/book.md/preview/job') }, - resolve: vi.fn(() => { - throw new Error('Could not get the request store. This is an internal error.') - }) + page: { + url: new URL('https://app.test/admin/piece/books/book.md/preview/job'), + route: { id: '/(auth)/admin/piece/[...path]/preview/[jobId]' } + }, + resolve: vi.fn((route: string, params: { path: string; jobId?: string }) => + route + .replace('/(auth)', '') + .replace('[...path]', () => params.path) + .replace('[jobId]', () => params.jobId ?? '') + ) })) vi.mock('$app/state', () => ({ page })) vi.mock('$app/paths', () => ({ resolve })) +// Only the layout's five app-navigation destinations may use this mocked resolver. +// Resource/helper URLs must remain independent; this is not native Kit resolution evidence. +function expectAppNavigation(jobs = ['job']) { + expect(resolve.mock.calls).toEqual( + jobs.flatMap((job) => { + const route = '/(auth)/admin/piece/[...path]/preview/[jobId]' + const path = 'books/book.md' + const jobId = encodeURIComponent(job) + return [ + ['/(auth)/admin/piece/[...path]/source', { path }], + [route, { path, jobId }], + [`${route}/page`, { path, jobId }], + [`${route}/icon`, { path, jobId }], + [`${route}/share`, { path, jobId }] + ] + }) + ) +} + type Data = ComponentProps['data'] type Status = 'waiting' | 'running' | 'completed' | 'failed' | 'expired' function data(status: Status = 'completed'): Data { @@ -115,6 +140,7 @@ function tree(props: Data) { beforeEach(() => { page.url = new URL('https://app.test/admin/piece/books/book.md/preview/job') + page.route.id = '/(auth)/admin/piece/[...path]/preview/[jobId]' vi.clearAllMocks() }) @@ -181,7 +207,7 @@ test('completed Preview reads title, consolidated facts, Preview logs, then asse 'background-color: #abc' ]) expect(html).not.toContain(absent) - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation() }) test.each(['waiting', 'running', 'failed', 'expired'] as const)( @@ -209,7 +235,7 @@ test.each(['waiting', 'running', 'failed', 'expired'] as const)( expect(html).not.toContain('Publication') expect(html).not.toContain('Preview <Draft>') expect(html).not.toContain('/asset/generated/') - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation() } ) @@ -229,7 +255,7 @@ test('completed workflow without an assembled result keeps facts/logs and omits expect(html).not.toContain('Preview assets') expect(html).not.toContain('None recorded') expect(html).not.toContain('/asset/generated/') - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation() }) test('recorded generated Share PNG remains a View link in Overview', () => { @@ -246,10 +272,10 @@ test('recorded generated Share PNG remains a View link in Overview', () => { expect(png).toBeDefined() expect(png).toContain('aria-label="View opengraph: generated/share.png"') expect(png).toContain('target="_blank"') - expect(png).toContain('rel="noopener noreferrer"') + expect(png).toContain('rel="external noopener noreferrer"') expect(png).not.toMatch(/\bdownload(?:=|\s|>)/) expect(html.match(/ { @@ -262,9 +288,10 @@ test('empty generated outputs do not turn SDK passthroughs into downloadable pre expect(html).not.toContain('Image') expect(html).not.toContain('archive/manual.pdf') expect(html).not.toContain('/asset/generated/') + expectAppNavigation() }) -test('replacement completed snapshots use only the new job namespace and metadata without a request store', () => { +test('replacement completed snapshots use only the new job namespace and metadata with mocked app navigation', () => { tree(data()) const props = data() if (props.status !== 'completed' || !props.piece) throw new Error('Expected assembled fixture') @@ -272,10 +299,11 @@ test('replacement completed snapshots use only the new job namespace and metadat props.piece = { ...props.piece, title: 'New result' } props.outputs = [asset('custom', 'new.txt', 'new', 'text/plain')] page.url = new URL('https://app.test/admin/piece/books/book.md/preview/new-job') + page.route.id = '/(auth)/admin/piece/[...path]/preview/[jobId]' const html = tree(props) expect(html).toContain('New result') expect(html).toContain('href="/admin/preview/new-job/asset/new.txt"') expect(html).not.toContain('/preview/job/asset/') expect(html).not.toContain('generated/cover.jpg') - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation(['job', 'new-job']) }) diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts index a4effa7c..9b28784e 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/share/page.test.ts @@ -23,18 +23,44 @@ const publishedPiece = { const { page, resolve } = vi.hoisted(() => ({ page: { url: new URL('https://app.test/admin/piece/books/book.md/preview/job/share'), + route: { id: '/(auth)/admin/piece/[...path]/preview/[jobId]/share' }, data: { preview: true, job: 'job', config: { url: { app: 'https://public.test/site', luzzle_assets: 'https://cdn.test' } } } }, - resolve: vi.fn(() => { - throw new Error('Could not get the request store. This is an internal error.') + resolve: vi.fn((route: string, params: { tag: string } | { path: string; jobId?: string }) => { + if (route === '/tags/[tag]' && 'tag' in params) return `/tags/${params.tag}` + if (!('path' in params)) throw new Error(`Unexpected route: ${route}`) + return route + .replace('/(auth)', '') + .replace('[...path]', () => params.path) + .replace('[jobId]', () => params.jobId ?? '') }) })) vi.mock('$app/state', () => ({ page })) vi.mock('$app/paths', () => ({ resolve })) + +// Only the layout's five destinations and explicit page tag links may use this mock. +// Resource/helper URLs must remain independent; this is not native Kit resolution evidence. +function expectAppNavigation(jobs = ['job'], tags: string[] = []) { + expect(resolve.mock.calls).toEqual([ + ...jobs.flatMap((job) => { + const route = '/(auth)/admin/piece/[...path]/preview/[jobId]' + const path = 'books/book.md' + const jobId = encodeURIComponent(job) + return [ + ['/(auth)/admin/piece/[...path]/source', { path }], + [route, { path, jobId }], + [`${route}/page`, { path, jobId }], + [`${route}/icon`, { path, jobId }], + [`${route}/share`, { path, jobId }] + ] + }), + ...tags.map((tag) => ['/tags/[tag]', { tag: encodeURIComponent(tag) }]) + ]) +} // Only renderer wrappers are replaced: the real routes, layout, ShareViewport // and getPieceHelpers run. SSR cannot prove reactive prop updates, client SSE, // or instance ResizeObserver mounting/cleanup and responsive scaling behavior. @@ -110,6 +136,7 @@ function tree(props: Data, view: keyof typeof routes = 'share', query = '') { page.url = new URL( `https://app.test/admin/piece/${props.file}/preview/${props.job}/${view}${query}` ) + page.route.id = `/(auth)/admin/piece/[...path]/preview/[jobId]/${view}` const params = { path: props.file, jobId: props.job } const children = createRawSnippet(() => ({ render: () => render(routes[view], { props: { data: props, params } }).body @@ -137,7 +164,7 @@ test.each([true, false])('Share reconstructs composition with recorded PNG=%s', expect(html).not.toMatch( /https:\/\/(?:public|cdn)\.test|\/pieces\/assets\/|\/admin\/asset\/|published\/cover\.jpg/ ) - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation() }) test('?html is ignored: identical composition and four tabs plus Back, only Share active', () => { @@ -153,6 +180,7 @@ test('?html is ignored: identical composition and four tabs plus Back, only Shar expect(header).toMatch(/]*href="[^"]*\/share"[^>]*aria-current="page"[^>]*>\s*share\s*<\/a>/) expect(header).not.toContain('?html') expect(header).not.toContain('share (html)') + expectAppNavigation(['job', 'job']) }) test.each(['share', 'page', 'icon'] as const)( @@ -163,7 +191,7 @@ test.each(['share', 'page', 'icon'] as const)( expect(html.match(/id="main-content"/g)).toHaveLength(1) expect(html).toContain(`data-renderer="${view === 'share' ? 'composition' : view}"`) expect(html).toContain('src="/admin/preview/job/asset/generated/cover.jpg"') - expect(resolve).not.toHaveBeenCalled() + expectAppNavigation() } ) @@ -181,7 +209,7 @@ test.each(['waiting', 'running', 'failed', 'expired'] as const)( expect(html).not.toMatch( /data-renderer="composition"| { + const props = data() + if (props.status !== 'completed') throw new Error('Expected completed fixture') + props.tags = [{ slug: '源 #?%', tag: 'Tag
{data.publishedUrl} + diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts index 34fe4ba9..004f70fa 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/layout.server.test.ts @@ -73,8 +73,7 @@ function event() { locals: { inlineConfiguredStyles: vi.fn() }, url: new URL( 'https://admin.example/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published' - ), - route: { id: '/(auth)/admin/piece/[...path]/published' } + ) } as unknown as Parameters[0] } @@ -114,18 +113,10 @@ test('loads only serving data/public assets, distinct tags and actual public URL }, tags, publication: { lastPublishedAt: 1234, markdownStatus: MarkdownPublicationStatus.MATCHING }, - publishedUrl: 'https://app.example/garden/pieces/removed-schema/public%20%23%3F%25', - links: { - source: '/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/source', - overview: '/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published', - page: '/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published/page', - icon: '/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published/icon', - share: '/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published/share' - } + publishedUrl: 'https://app.example/garden/pieces/removed-schema/public%20%23%3F%25' }) expect(Object.keys(data!).sort()).toEqual([ 'file', - 'links', 'piece', 'publication', 'publishedUrl', @@ -251,26 +242,6 @@ test('public URL encodes both serving type and slug, including literal percent c }) }) -test.each(['', '/page', '/icon', '/share'])( - 'loader serializes mounted navigation on %s without a Kit request store', - async (suffix) => { - const request = { - ...event(), - url: new URL( - `https://admin.example/garden/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published${suffix}/?x=1` - ), - route: { id: '/(auth)/admin/piece/[...path]/published' + suffix } - } as Parameters[0] - expect(await load(request)).toMatchObject({ - links: { - source: '/garden/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/source', - overview: '/garden/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published', - share: '/garden/admin/piece/old%20type/%E6%BA%90%20%23%3F%25.books.md/published/share' - } - }) - } -) - test('cache database failures follow normal loader failure semantics', async () => { query.executeTakeFirst.mockRejectedValue(new Error('Database unavailable')) await expect(load(event())).rejects.toThrow('Database unavailable') diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts index 042ae901..0601b7c5 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/published/page.test.ts @@ -3,6 +3,7 @@ import { beforeEach, expect, test, vi } from 'vitest' import { render } from 'svelte/server' import type { ComponentProps } from 'svelte' import { MarkdownPublicationStatus } from '#lib/pieces/types.js' +import { editorHref, publishedHref } from '#lib/pieces/navigation.js' import { publishedPiece as rawPublishedPiece, asset @@ -19,20 +20,25 @@ const publishedPiece = { } } -const { page } = vi.hoisted(() => ({ +const { page, resolve } = vi.hoisted(() => ({ page: { - url: new URL('https://app.test/admin/piece/books/book.md/published'), + url: new URL('https://app.test/'), + route: { id: '/(auth)/admin/piece/[...path]/published' }, params: {}, data: { config: { url: { app: '/garden', luzzle_assets: '/cdn' } } } - } + }, + resolve: vi.fn((route: string, params?: { path?: string; tag?: string }) => { + if (route === '/tags/[tag]' && params?.tag !== undefined) return `/tags/${params.tag}` + if (route.startsWith('/(auth)/admin/piece/[...path]/') && params?.path !== undefined) + return route.replace('/(auth)', '').replace('[...path]', params.path) + if (params || route.includes('[')) throw new Error(`Unexpected route: ${route}`) + return route.startsWith('/') ? route : `/${route}` + }) })) vi.mock('$app/state', () => ({ page })) -vi.mock('$app/paths', () => ({ - resolve: (route: string, params: Record) => - route.replace(/\[(?:\.\.\.)?(\w+)\]/g, (_, name: string) => params[name]) -})) +vi.mock('$app/paths', () => ({ resolve })) // These synchronous renderer stubs establish route props, URLs and landmarks; -// they do not exercise the wrappers' static catalogs or client prop updates. +// they do not exercise native Kit resolution, the wrappers' static catalogs or client prop updates. vi.mock('#lib/pieces/components/page.svelte', async () => ({ default: (await import('#lib/pieces/viewer/fixtures/PageRenderer.svelte')).default })) @@ -53,13 +59,6 @@ function data( tags: [{ slug: 'fiction', tag: 'Fiction' }], publication: { lastPublishedAt: time, markdownStatus: status }, publishedUrl: 'https://public.test/garden/pieces/books/published%20%23%3F%25', - links: { - source: '/admin/piece/books/book.md/source', - overview: '/admin/piece/books/book.md/published', - page: '/admin/piece/books/book.md/published/page', - icon: '/admin/piece/books/book.md/published/icon', - share: '/admin/piece/books/book.md/published/share' - }, config: { content: { text: { title: 'Garden', description: 'Description' } }, url: { app: '/garden', app_assets: '/static', luzzle_assets: '/cdn' } @@ -68,7 +67,9 @@ function data( } } beforeEach(() => { - page.url = new URL('https://app.test/admin/piece/books/book.md/published') + vi.clearAllMocks() + page.url = new URL(publishedHref(data().file), 'https://app.test') + page.route.id = '/(auth)/admin/piece/[...path]/published' }) test('Overview shows escaped published identity, actual completion time and external public link', () => { @@ -90,7 +91,7 @@ test('Overview shows escaped published identity, actual completion time and exte 'href="https://public.test/garden/pieces/books/published%20%23%3F%25" target="_blank" rel="noopener noreferrer"' ) expect(html).toContain(`${data().publishedUrl}`) - expect(html).not.toContain('href="/admin/piece/books/book.md/source"') + expect(html).not.toContain(`href="${editorHref(data().file)}"`) expect(html).not.toContain('>Open<') expect(html).not.toContain(']*dir="ltr"[^>]*>/) expect(link).not.toContain('aria-label=') } - expect(html).not.toContain(props.links.source) + expect(html).not.toContain(editorHref(props.file)) expect(html).not.toContain('nested folder/') expect(links[0]).toContain(`href="${props.publishedUrl}"`) expect(links[0]).toContain(`>${props.publishedUrl}`) @@ -212,7 +213,7 @@ test('Overview, not Share, links only the actual recorded generated output', () /]*href="\/cdn\/pieces\/assets\/piece\/generated.png"[^>]*>/ )?.[0] expect(generatedLink).toContain('target="_blank"') - expect(generatedLink).toContain('rel="noopener noreferrer"') + expect(generatedLink).toContain('rel="external noopener noreferrer"') expect(html).toMatch(/]*href="\/cdn\/pieces\/assets\/piece\/generated.png"[^>]*>View<\/a>/) expect(html).not.toContain('>Open<') expect(html).not.toContain(']*>Source<\/th>/) expect(result.body.match(/
{ + page.url = new URL(`${publishedHref(data().file)}/${view}`, 'https://app.test') + page.route.id = `/(auth)/admin/piece/[...path]/published/${view}` const result = render(PublishedTree, { props: { data: data(), view } }) + expect(result.body).toContain( + `href="${publishedHref(data().file)}/${view}" aria-current="page"` + ) + expect(result.body.match(/aria-current="page"/g)).toHaveLength(1) expect(result.body.match(/
{ if (!disposed && data.file === file) { editorErrorReason = 'generation-url' @@ -684,7 +687,8 @@ {unpublishDisabled} onPublish={() => { const file = data.file - void goto(publishHref(file)).catch(() => { + const query = publishQuery(file) + void goto(resolve(`admin/publish?${query}`)).catch(() => { if (!disposed && data.file === file) { editorErrorReason = 'publish' editorErrorDetail = '' diff --git a/apps/web/src/routes/(auth)/admin/pieces/create/[[directory]]/+page.svelte b/apps/web/src/routes/(auth)/admin/pieces/create/[[directory]]/+page.svelte index f6b2d4dc..6baad450 100644 --- a/apps/web/src/routes/(auth)/admin/pieces/create/[[directory]]/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/pieces/create/[[directory]]/+page.svelte @@ -1,4 +1,5 @@ diff --git a/apps/web/src/routes/pieces/+layout.svelte b/apps/web/src/routes/pieces/+layout.svelte index 6fa12ff5..e17f9d8a 100644 --- a/apps/web/src/routes/pieces/+layout.svelte +++ b/apps/web/src/routes/pieces/+layout.svelte @@ -2,16 +2,19 @@ import Nav from '#lib/components/layout/simple/nav.svelte' import BellIcon from 'virtual:icons/ph/bell-simple' import { page } from '$app/state' + import { resolve } from '$app/paths' const { children } = $props() {#snippet rightItems()} {#if page.params.piece} - {:else} - + {/if} {/snippet} diff --git a/apps/web/src/routes/pieces/[[piece]]/+page.svelte b/apps/web/src/routes/pieces/[[piece]]/+page.svelte index 90833580..883a0a3d 100644 --- a/apps/web/src/routes/pieces/[[piece]]/+page.svelte +++ b/apps/web/src/routes/pieces/[[piece]]/+page.svelte @@ -1,5 +1,6 @@ {#snippet rssIcon()} - + {/snippet}