Skip to content

dogfood: scaffold serves public at /public/* so the documented /sw.js service worker 404s #830

Description

@vivek7405

Problem

In a generated scaffold, the opt-in service worker (#271) cannot register at the path webjs documents. Static assets under public/ serve ONLY at /public/*, but every service-worker surface registers /sw.js at the site root:

  • agent-docs/service-worker.md (the registration snippet + "Registered from /sw.js, the worker's scope is the site root /").
  • The shipped packages/cli/templates/public/sw.js self-registration comment (/sw.js?v=<build>).
  • Root AGENTS.md app layout documents public/* as "static assets, served at /<name>" (i.e. root), which itself contradicts the actual /public/* serving.

Empirically, in a freshly generated full-stack app: GET /sw.js -> 404, GET /public/sw.js -> 200 (same for /offline.html vs /public/offline.html). So a user who follows the documented recipe registers a 404 and gets no worker. Worse, the only path that DOES serve (/public/sw.js) gives the worker a /public/* scope by default, so it would not control the site root even if registered, unless the response carries Service-Worker-Allowed: /.

Found while reviewing the scaffold feature gallery (#826): the new app/features/service-worker demo had to choose a path, and this inconsistency surfaced. The demo now uses /sw.js (matching the canonical docs + correct scope), which will 404 until this is fixed.

Design / approach

Serve the small set of "root assets" a webjs app expects at the site root, from public/. The framework already special-cases /favicon.ico -> public/favicon.ico in the same branch, so extend that to the service-worker + PWA root files: /sw.js, /offline.html, /robots.txt, /manifest.webmanifest (the exact set can match what the metadata routes + service-worker feature assume). For /sw.js, also send Service-Worker-Allowed: / so the worker controls the whole origin regardless of the served path. Decide and document ONE canonical model, then make AGENTS.md, agent-docs/service-worker.md, the shipped public/sw.js, and the scaffold layout all agree with it. (The scaffold layout currently uses /public/tailwind-browser.js, which matches the actual /public/* serving, so the two conventions coexist inconsistently today.)

Implementation notes (for the implementing agent)

  • Where to edit: packages/server/src/dev.js around L1828-1839 (the path.startsWith('/public/') || path === '/favicon.ico' static branch) and the equivalent path in the prod listener(s) (packages/server/src/listener-*.js / the serve path) so dev and prod agree. Mirror the existing /favicon.ico -> /public/favicon.ico remap for the root-asset set, and add the Service-Worker-Allowed: / header on the /sw.js response.
  • Landmines: the existing branch has a traversal guard (decoded .. containment under appDir/public/); keep any new remap inside that guard. Service-worker SCOPE is the real trap: serving the bytes at root is necessary but the browser also scopes by the request path, so the Service-Worker-Allowed header (or serving the file physically at root) is required for site-wide scope. Bun listener (listener-bun.js) has its own static path; fix both (runtime parity, Support both Bun and Node runtimes (first-class create + run) #508) and add a test/bun/* assertion.
  • Invariants: keep it no-build; do not move public/ files at scaffold time (they stay under public/, only the SERVED URL changes).
  • Tests + docs: add a handle: /sw.js serves public/sw.js at root with Service-Worker-Allowed unit test (packages/server/test/dev/dev-handler.test.js), a Bun parity assertion, and reconcile AGENTS.md + agent-docs/service-worker.md + packages/cli/templates/public/sw.js + the scaffold gallery app/features/service-worker demo to the chosen canonical path.

Acceptance criteria

  • GET /sw.js serves public/sw.js (200) in a generated scaffold, in dev AND prod, on Node AND Bun.
  • The /sw.js response carries Service-Worker-Allowed: / (or the file is physically root-scoped) so the worker controls the whole origin.
  • agent-docs/service-worker.md, root AGENTS.md, the shipped public/sw.js, and the app/features/service-worker demo all describe the SAME, working path.
  • A counterfactual proves the test fires (revert the remap -> /sw.js 404s -> test red).
  • Docs / AGENTS.md updated to state the canonical public-root-asset serving model.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions