impl(web,cli): static SPA build + thin web.Server (tauri port phase 3)#77
Draft
andykais-claude wants to merge 1 commit into
Draft
Conversation
Phase 3 of the Tauri port plan. The SvelteKit app is now a pure static
SPA; the SSR/server runtime is gone. @forager/web becomes a thin static
asset host that mounts ForagerServer for /rpc and /files.
Frontend (packages/web/src)
- hooks.server.ts: deleted. No more SSR-time Forager construction.
- +layout.server.ts: deleted. Replaced by +layout.ts that fetches the
config via RPC at boot. Layout still propagates data.config to child
pages so the controllers don't change.
- +page.server.ts: deleted. Replaced by +page.ts + +page.svelte that
perform a client-side goto('/browse', { replaceState: true }).
- routes/rpc/[signature]/+server.ts, routes/files/media_file/[id]/+server.ts,
routes/files/thumbnail/[id]/+server.ts: deleted. The handlers all live
in @forager/server now and are mounted by web.Server directly.
Custom adapter (packages/web/adapter/adapter.js)
- Stripped to a static-only build. Equivalent to @sveltejs/adapter-static
with fallback: 'index.html', plus the bytes-import packaging so
'deno compile' can embed the SPA inside the CLI binary.
- Drops writeServer + the strip_type_imports hack.
@forager/web runtime (packages/web/adapter/lib/mod.ts)
- Embedded SvelteKit kitServer + manifest gone.
- Server.handle_request: ForagerServer.try_handle_request(req) → static
file via serveDir → SPA fallback (build/static/index.html). Immutable
asset routes still get long cache-control; SPA shell gets no-cache.
- The 'kit' option block is gone from ServerOptions; CLI no longer
plumbs FORAGER_INSTANCE/FORAGER_CONFIG via env.
- 'preview' mode still works; entrypoint shape is unchanged.
- shim_config.ts re-exports @forager/server/config so the existing
@forager/web/config import path keeps working for downstream consumers.
vite dev (packages/web/vite.config.ts)
- Added a proxy: /rpc and /files forward to FORAGER_API_URL (default
http://127.0.0.1:8000). 'deno task --cwd packages/web dev' now expects
a 'forager gui'/'forager serve' running separately.
CLI (packages/cli/src/cli.ts)
- 'init' and 'gui' no longer pass kit.env. They construct web.Server
with just forager + config.
Result: web/adapter/lib drops from 1.04 MB to 227 KB embedded in the CLI
binary (the entire SvelteKit server bundle is gone). Browser experience,
RPC payloads, /files/* responses (including Range), and direct navigation
to /browse, /tags, /tags/<slug> all verified end-to-end against the
compiled binary.
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of the Tauri port plan. Stacked on phase 2; base branch is
cursor/tauri-frontend-phase2-1e82. Please review/merge phases 1 and 2 first, then this.The SvelteKit app is now a pure static SPA — there is no SSR, no
+server.tsroutes, nohooks.server.ts, no SvelteKit runtime in the production binary.@forager/webbecomes a thin static-asset host that mountsForagerServerfor/rpc/*and/files/*and servesindex.htmlas the SPA fallback for everything else.Walkthrough
forager_phase3_static_spa_smoketest.mp4
End-to-end browser test of the static SPA against the compiled CLI binary:
/redirects client-side to/browse, direct navigation to/tagsand/tags/cat(URLs that don't exist on disk) correctly serves the SPA shell + client-side routing, video plays via Range requests.Browse view rendered by the static SPA
Direct navigation to /tags works via SPA fallback
Deep client route /tags/cat works via SPA fallback
Video playback via Range requests still works
What changed
Frontend (
packages/web/src)hooks.server.ts,+layout.server.ts,+page.server.ts, and all three+server.tsfiles (/rpc/[signature],/files/media_file/[id],/files/thumbnail/[id]). Phase 1 had already moved their logic into@forager/server— they were thin shims at that point.+layout.tsfetchesconfigvia RPC at boot and propagates it asdata.configso the controllers don't need to change.+page.ts++page.svelteperform a client-sidegoto('/browse', { replaceState: true }), replacing the old 307 server redirect.Custom adapter (
packages/web/adapter/adapter.js)@sveltejs/adapter-staticwithfallback: 'index.html', plus the bytes-import packaging that letsdeno compileembed the SPA inside the CLI binary.builder.writeServer(...)and thestrip_type_importshack on the SvelteKit server bundle (no server bundle exists anymore).Static frontend host (
packages/web/adapter/lib/mod.ts)kitServer+manifestare gone.Server.handle_requestis now:ForagerServer.try_handle_request(req)→ static file viaserveDir→ SPA fallback (build/static/index.html)._app/immutable/*still getscache-control: public, max-age=31536000, immutable. The SPA shell getscache-control: no-cacheso deploys roll out instantly.kitoption block is gone fromServerOptions; CLI no longer plumbsFORAGER_INSTANCE/FORAGER_CONFIGvia env.previewmode (deno task --cwd packages/web/adapter/lib preview) still works; entrypoint shape is unchanged.packages/web/adapter/lib/shim_config.tsre-exports@forager/server/configso the existing@forager/web/configimport path keeps working for downstream consumers.vite dev(packages/web/vite.config.ts)/rpcand/filesforward toFORAGER_API_URL(defaulthttp://127.0.0.1:8000).deno task --cwd packages/web devnow expects aforager gui/forager serverunning separately. Phase 4 will add CORS so the dev server can also talk cross-origin.CLI (
packages/cli/src/cli.ts)initandguisimplified — they constructweb.Serverwith just{ forager, config, ... }(nokit.env).Numbers
packages/web/adapter/libembedded in CLI binarybuild/server/*web/The entire SvelteKit server runtime (
Server,manifest,chunks/internal.js,chunks/migration_v11.js, etc.) is no longer shipped.Testing
deno task --cwd packages/web build:local— clean static SPA build (26 files:_app/*,favicon.png,index.html).deno check packages/web/adapter/lib/mod.ts— clean.deno check packages/cli/src/cli.ts— clean.deno task compile:local— full CLI binary builds; web tree is 227 KB.deno task --cwd packages/cli test— 2/2 pass.deno test packages/web/test/connection.test.ts— 8/8 pass (phase 2 unit tests).GET /→ 200 SPA shell (1217 bytes); the SPA thengoto('/browse').GET /browse,GET /tags,GET /tags/anything→ all 200 + same SPA shell (fallback works for arbitrary client routes).GET /favicon.png→ 200 image/png 4461 bytes.GET /_app/version.json→ 200 (real static file).PUT /rpc/forager.media.search→ JSON RPC payload.GET /files/thumbnail/1→ 200 image/jpeg 36544 bytes.GET /files/media_file/2withRange: bytes=0-1023→ 206 +content-range: bytes 0-1023/400240./tagsand/tags/cat, video playback all work end-to-end.Follow-ups (not in this PR)
web.server.cors_allow_origins,forager servecommand, config schema additions.@forager/desktopTauri shell scaffolding.To show artifacts inline, enable in settings.