Skip to content

feat(web)!: migrate to SvelteKit 3 and Vite 8 with build-time DB driver - #115

Open
flamboh wants to merge 4 commits into
stack/sveltekit-2-70from
stack/sveltekit-3
Open

flamboh wants to merge 4 commits into
stack/sveltekit-2-70from
stack/sveltekit-3

Conversation

@flamboh

@flamboh flamboh commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Warning

🤖 Claude Opus 5.5 on behalf of Oliver. This moves the dashboard to a prerelease SvelteKit (3.0.0-next.27). That is intentional.

ELI5

This moves the dashboard to the next major version of its web framework. It also picks the database (a local SQLite file or Cloudflare D1) when the app is built, instead of on every request.

Why

Alchemy's SvelteKit support needs kit ≥ 3.0.0-next.27 and Vite 7 or 8. SvelteKit 3 also removes platform.env on Cloudflare, so D1 has to come from cloudflare:workers. That module can't be imported in a Node build, so the runtime platform?.env.DB check had to go.

Implementation

  • Versions:
    • SvelteKit 3.0.0-next.27 and adapter-cloudflare 8.0.0-next.7, pinned exactly.
    • Vite 8, vite-plugin-svelte 7, TypeScript 6 and vitest 5.
    • Node 24 in shell.nix and .node-version, because kit 3 needs Node ≥ 22.17 and nixpkgs only has 22.16. The Node version pins across the stack are reconciled in feat(infra): add self-hosted container stack #117.
  • Config: svelte.config.js is gone. Its settings now live in sveltekit({...}) in vite.config.ts. $lib imports are now #lib subpath imports with file extensions, and env vars come from $app/env/private.
  • Build-time DB driver:
    • #db resolves to db/d1.ts under a custom atlantis-d1 condition, and to db/sqlite.ts otherwise.
    • ATLANTIS_DB_DRIVER is read only from the shell, never from .env. A stray .env value can't turn a deploy build into a SQLite worker.
    • vite build defaults to D1 and vite dev to SQLite. Routes no longer receive platform.
  • runed removed: URL state is read from page.url through the zod schema. All writes go through one updateSearch() called from event handlers, using goto(…, { reset: false }). The group-by clamp is a $derived, and URL sync no longer writes state from an effect.
  • Drill-down: Traffic Overview, the IP, protocol and spectrum breakdowns, Flow Characteristics and Unique Ports now each report a drill-down as one { groupBy, startDate, endDate } payload. The page applies it in a single updateSearch(). Previously the new group-by was sent before the new dates, so it was clamped against the old range.

Supported build and preview pairs

vite preview runs the server in Node, so it can only serve the SQLite build.

Goal Command Result
Local dev on SQLite bun run dev:web Vite dev server, no workerd
Local dev on D1 ATLANTIS_DB_DRIVER=d1 bun run dev:web Dev through the Workers runtime
Preview a production build bun run --cwd apps/web preview Always rebuilds with SQLite, then runs vite preview
Serve the deploy build bun run build:web, then cd apps/web && bunx wrangler dev D1 worker under wrangler

vite preview also ignores ATLANTIS_DB_DRIVER and always resolves SQLite. Running build:web and then preview used to load the Cloudflare bundle in Node and return 500. It now works.

Flows to exercise

Setup: a local SQLite dataset covering at least 90 days, with daily rows.

  1. Drill down from a wide daily range (Traffic Overview). Open /datasets/<id>?startDate=2025-01-01&endDate=2025-03-31&groupBy=date and click a daily point.
    • Expected: the URL becomes about a month around the clicked day with groupBy=hour.
    • Expected: the next /api/netflow/stats request has groupBy=hour.
    • Before this fix: the dates changed but groupBy stayed at date.
  2. Drill down in the IP, protocol or spectrum breakdown. Use the same URL and click a daily point.
    • Expected: groupBy=hour in the URL.
    • Expected: the next /api/ip/stats (or protocol/spectrum) request has granularity=1h.
  3. Clamping and history. Change the dates and the group-by, then pick a range longer than 62 days.
    • Expected: Day is forced and the finer options are disabled.
    • Expected: back and forward restore both the URL and the charts.
  4. Preview after a deploy build. Run bun run build:web, then bun run --cwd apps/web preview.
    • Expected: the dashboard serves local SQLite data. Before this fix it returned 500.
  5. Dev mode. Run bun run dev:web.
    • Expected: it reads local SQLite without starting workerd.

Edge cases and decisions

  • A URL with an invalid groupBy is no longer rewritten on load. The clamped value is shown, and the URL is corrected on the next filter change. The old effect pushed an extra history entry.
  • A start date that forces a clamp now sends one stats request instead of a stale request followed by a correct one.
  • A drill-down whose new range still allows the requested granularity keeps it. Clamping only applies when the target range is too wide.
  • preview always builds before serving, so it is slower than a bare vite preview. In exchange, it can't serve a stale or mismatched build.
  • The shadcn-svelte components.json still generates $lib imports. Fix them by hand when adding components.

Verification

Automated (all pass):

  • bun run format, lint, typecheck and test:web.
  • bun run test:e2e (8 tests). It includes the new dashboard-drilldown.spec.ts, which checks both the URL groupBy and the API granularity for Traffic Overview and the IP breakdown. Both tests fail without the fix.
  • The Playwright web server builds the default D1 worker first and then runs preview, so every e2e run covers build → preview. With the old preview script, all 8 tests fail.
  • build:web, which the e2e web server runs.

Checked manually on this branch before the fixes:

Remaining manual checks:

  • Drill-downs on the protocol and spectrum charts against real data.
  • wrangler dev against local D1 after build:web.

Claude Opus 5.5 · Claude Code (T3 Code)

- Upgrade to @sveltejs/kit 3.0.0-next.27, adapter-cloudflare 8.0.0-next.7,
  Vite 8, vite-plugin-svelte 7, TypeScript 6, and Vitest 5.
- Move kit config into vite.config.ts, replace $lib with #lib subpath
  imports, extend $app/tsconfig, and declare env vars in src/env.ts.
- Select the database driver at build time through the #db subpath import:
  the atlantis-d1 condition resolves the D1 driver (cloudflare:workers),
  the default resolves the SQLite driver. Routes no longer thread platform.
- Pair D1 with adapter-cloudflare and SQLite with no adapter, so plain dev
  never starts workerd and SQLite builds never overwrite the worker output.
- Patch runed useSearchParams to use the kit 3 goto options.
- Build a SQLite bundle inside the Playwright web server and run Node 24
  in the nix shell to meet the kit 3 Node minimum.
Read dashboard filters from page.url through the zod schema, keep the
group-by clamp derived, and write the URL only from event handlers via
goto with SvelteKit 3 options. Drop runed and its goto patch.
@flamboh
flamboh added this pull request to stack #118 September 25, 2026 10:46
@flamboh flamboh changed the title stack/sveltekit 3 feat(web)!: migrate to SvelteKit 3 and Vite 8 with build-time DB driver Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant