From 81962e436cc8c809b181473b8ef0f1343d46ef96 Mon Sep 17 00:00:00 2001 From: eleith Date: Mon, 2 Nov 2026 15:14:32 -0800 Subject: [PATCH 1/4] Apply SvelteKit 3 migration --- .changeset/web-framework-upgrade.md | 5 + apps/web/MIGRATION_TASKS.md | 141 ++++++++++++++++++ apps/web/package.json | 8 +- apps/web/src/hooks.server.test.ts | 9 +- apps/web/src/hooks.server.ts | 5 +- .../src/lib/components/DocumentHead.svelte | 2 +- .../web/src/lib/components/ThemeToggle.svelte | 2 +- .../bookmarks/BookmarkDetails.svelte | 2 +- .../bookmarks/BookmarkDetailsDialog.svelte | 2 +- .../components/bookmarks/BookmarkLogo.svelte | 2 +- .../components/search/BookmarkSearch.svelte | 19 ++- apps/web/src/lib/search/engine.test.ts | 2 +- apps/web/src/lib/search/engine.ts | 2 +- apps/web/src/lib/server/auth/access.test.ts | 3 +- apps/web/src/lib/server/auth/access.ts | 3 +- apps/web/src/lib/server/auth/index.ts | 4 +- apps/web/src/routes/(protected)/+page.svelte | 53 ++++--- .../src/routes/(public)/signin/+page.svelte | 6 +- apps/web/src/routes/+error.svelte | 2 +- apps/web/src/routes/+layout.svelte | 4 +- apps/web/svelte.config.js | 26 ---- apps/web/tsconfig.json | 12 +- apps/web/vite.config.ts | 26 +++- 23 files changed, 255 insertions(+), 85 deletions(-) create mode 100644 .changeset/web-framework-upgrade.md create mode 100644 apps/web/MIGRATION_TASKS.md delete mode 100644 apps/web/svelte.config.js diff --git a/.changeset/web-framework-upgrade.md b/.changeset/web-framework-upgrade.md new file mode 100644 index 0000000..5040a14 --- /dev/null +++ b/.changeset/web-framework-upgrade.md @@ -0,0 +1,5 @@ +--- +'@directory/web': patch +--- + +Upgrade the web app to SvelteKit 3. diff --git a/apps/web/MIGRATION_TASKS.md b/apps/web/MIGRATION_TASKS.md new file mode 100644 index 0000000..a5ea753 --- /dev/null +++ b/apps/web/MIGRATION_TASKS.md @@ -0,0 +1,141 @@ + + +# 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 + +### 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/routes/(protected)/+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/routes/(protected)/+page.svelte` + +### `data-sveltekit-*` uses `false` instead of `"off"` + +The `"off"` value for `data-sveltekit-*` link attributes has been removed in favour of `false`. + +#### What to do + +Replace `data-sveltekit-...="off"` with `data-sveltekit-...="false"`. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-data-sveltekit-uses-false-instead-of-'off') + +#### Files to review + +- [ ] `src/routes/(protected)/+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.test.ts` +- [ ] `src/lib/server/auth/access.test.ts` +- [ ] `src/lib/server/auth/index.test.ts` +- [ ] `src/routes/(protected)/(endpoints)/config/version/+server.ts` +- [ ] `src/routes/(public)/healthz/+server.ts` +- [ ] `src/routes/(public)/schema.json/+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) + +### Review `redirect(...)` destinations for external URLs + +`redirect(...)` to an external URL now requires an `external` option. + +#### What to do + +The automatic migration only analyzes static URLs. Inspect each `redirect` call: if the destination can be an external URL at runtime, pass `{ external: true }` for a blanket allow, or `{ external: [...] }` for a list of permitted origins, or keep it as is when determining that this should only handle app-internal redirects. + +#### References + +- [Migrating to SvelteKit v3](https://svelte.dev/docs/kit/migrating-to-sveltekit-3#Miscellaneous-External-redirects-must-be-opted-into) + +#### Files to review + +- [ ] `src/hooks.server.test.ts` +- [ ] `src/lib/server/auth/access.test.ts` +- [ ] `src/lib/server/auth/actions.ts` +- [ ] `src/routes/(protected)/+page.server.ts` +- [ ] `src/routes/(public)/signin/+page.server.ts` + +## 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/package.json b/apps/web/package.json index 08bf49f..cbe0be8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -29,8 +29,8 @@ "devDependencies": { "@iconify-json/ph": "catalog:", "@playwright/test": "catalog:", - "@sveltejs/adapter-node": "catalog:", - "@sveltejs/kit": "catalog:", + "@sveltejs/adapter-node": "^6.0.0", + "@sveltejs/kit": "^3.0.0", "@sveltejs/vite-plugin-svelte": "catalog:", "@types/node": "catalog:", "@vitest/coverage-v8": "catalog:", @@ -40,5 +40,9 @@ "unplugin-icons": "catalog:", "vite": "catalog:", "vitest": "catalog:" + }, + "imports": { + "#lib": "./src/lib/index.js", + "#lib/*": "./src/lib/*" } } diff --git a/apps/web/src/hooks.server.test.ts b/apps/web/src/hooks.server.test.ts index 5bab3ce..b722333 100644 --- a/apps/web/src/hooks.server.test.ts +++ b/apps/web/src/hooks.server.test.ts @@ -1,5 +1,6 @@ +import type { Handle } from '@sveltejs/kit/hooks'; +import type { RequestEvent } from '@sveltejs/kit'; import { validateConfig } from '@directory/config'; -import type { Handle, RequestEvent } from '@sveltejs/kit'; import { themeStyleTag } from './lib/server/appearance.js'; import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'; import type { Authentication } from './lib/server/auth/index.js'; @@ -329,9 +330,13 @@ describe('server hooks', () => { for (const cookie of ['', 'unrelated-cookie=opaque']) { for (const [path, routeId, params] of [ ['/public/photo.svg', '/(public)/public/[...file]', { file: 'photo.svg' }], + ['/public/site.svg', '/(public)/public/[...file]', { file: 'site.svg' }], + ['/%70ublic/photo.svg', '/(public)/public/[...file]', { file: 'photo.svg' }], + ['/public/unknown.svg', '/(public)/public/[...file]', { file: 'unknown.svg' }], + [ '/bookmarks/photos/logo', '/(protected)/(endpoints)/bookmarks/[id]/logo', @@ -423,10 +428,12 @@ describe('server hooks', () => { const themedHead = await transform({ html: head, done: false }); expect(themedHead).toBe(`\t${themeStyleTag(captured.display.appearance)}\n${head}`); expect(await transform({ html: ending, done: true })).toBe(ending); + return new Response(opening + themedHead + ending, { headers: { 'content-type': 'text/html' } }); }); + const response = await hooks.handle({ event: request, resolve }); const html = await response.text(); expect(html.match(/