Skip to content

feat: close coverage-gate deferred gaps with real scaffold demos#870

Merged
vivek7405 merged 9 commits into
mainfrom
feat/close-coverage-gate-gaps
Jul 9, 2026
Merged

feat: close coverage-gate deferred gaps with real scaffold demos#870
vivek7405 merged 9 commits into
mainfrom
feat/close-coverage-gate-gaps

Conversation

@vivek7405

@vivek7405 vivek7405 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Advances #859

Add runnable scaffold demos for the surfaces the tier-2 coverage gate (#861) marked deferred, flipping each manifest entry from exempt to demo / demoed, and correcting the internal/deferred split the #861 regex bucketing got wrong. Single PR per the owner's request. Also fixes the stale tier-1-only framework-dev note #861 left behind.

Coverage moved this PR

Surface Demoed at start Demoed now Deferred now
@webjsdev/core exports 12 24 20
@webjsdev/server exports 21 22 36
routing convention files 6 12 6

What landed (real demos, each verified in a booted app)

  • Metadata routes: app/sitemap.ts (uses sitemap() from @webjsdev/server), app/robots.ts, app/manifest.ts. Flips 3 conventions + the server sitemap export.
  • Directives: extended the directives demo with live, until, keyed, unsafeHTML, ref + createRef (6 core).
  • Boundaries: error.ts (+ a crash/ route that throws so it is reachable), loading.ts, not-found.ts in the boundaries feature (3 conventions). /crash renders 500, an unmatched subtree URL renders 404.
  • Shadow DOM + signals: a <reactive-meter> shadow component using css (scoped styles), computed, effect, batch (4 core). Shadow DSD verified in SSR.
  • Client router: a <router-controls> component driving navigate() and revalidate() (2 core).

Internal/deferred correction

The #861 manifest classified the export surface with regex heuristics, which wrongly marked several DOCUMENTED app-facing exports as internal (removing them from the demo backlog). This PR corrects that: render, renderStream, renderToString, readBody, stampRemoteIp, attachWebSocket, the documented test helpers (buildRouteTable / matchPage / matchApi / rawActionRequest), and the session/store config (cookieSessionStorage / storeSessionStorage / storeSession / getStore) are now deferred, not internal. A handful of genuine framework internals (runInstrumentation, the low-level cookie serializers, runWithActionSignal, the wire parse/stringify) stay internal, each verified to have no app-facing doc surface.

What is left deferred, and why (kept honest, not force-demoed)

The remaining deferred surfaces are advanced or niche for a STARTER gallery, and are documented in agent-docs / docs. Forcing a demo for each would ship contrived teaching:

  • core (20): Suspense, Task/TaskStatus, the context API (createContext + Context*), niche lit directives (guard / templateContent / asyncAppend / asyncReplace / cache), cspNonce, disableClientRouter/enableClientRouter, redirect, richFetch, and the now-correctly-deferred render/renderStream/renderToString.
  • server (36): the auth/session surfaces, revalidate*, request helpers (json / readBody / headers / requestId / clientIp / cookies), setOnError, signed URLs, store setup, actionContext/actionSignal, sitemapIndex, the documented test helpers, startServer, OAuth presets. Several of these are demonstrable and are the natural next batch.
  • conventions (6): global-error / global-not-found (root-only boundaries) and the four image metadata routes (icon / apple-icon / opengraph-image / twitter-image).

#859 stays open to track that remainder (hence Advances, not Closes).

Verification

  • Coverage gate: 12/12, full scaffold suite 44/44.
  • Fresh full-stack generate: webjs check shows only no-scaffold-placeholder, webjs typecheck clean (exit 0), boots and serves every new route at the right status (sitemap/robots/manifest 200 with correct bodies, directives/components/client-router/boundaries 200, /crash 500, unmatched 404).
  • Self-review loop ran to a clean round; findings (a literal {{APP_NAME}} in a verbatim-copied manifest, an until() per-render flicker, and the internal-vs-deferred mis-classifications above) fixed on the branch.
  • Dogfood four-app boot: N/A because this PR touches only packages/cli/templates and test/, not packages/(core|server|cli)/src or the served wire.

@vivek7405 vivek7405 self-assigned this Jul 9, 2026

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the new gallery demos against the generated output, not just the templates. Two real bugs, both fixed here.

First, manifest.ts had name: '{{APP_NAME}}', but gallery files are copied verbatim with no substitution (only the base template files get {{APP_NAME}} replaced), so a fresh app served a literal {{APP_NAME}} in /manifest.json. My 'manifest 200' check only looked at status and missed the body. Hardcoded a neutral 'webjs app' the user adapts, and added a comment saying to set it by hand. Fixed in da05cd73.

Second, the directives demo built the until() promise inside render() via later(), so its identity changed every render and until reset to the 'loading...' fallback on any re-render (typing in the live input, rekey, reverse). Moved it to a field created once. Same commit.

Also rephrased the inline template comments to drop the xyz(): shape that invariant 11 bans (the enforcement hook does not scan HTML comments, so it slipped).

Everything else held: SSR safety of the shadow component and the directive demo, the crash/page.ts throw is inert at module load so the check and import-graph walk are unaffected, the metadata-route return contracts match the handler, and the 10 internal reclassifications are genuine framework/CLI internals.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second pass, zoomed in on the internal reclassification I did. I was too aggressive. cookieSessionStorage, storeSessionStorage (and its alias storeSession), and getStore are all app-facing and documented (docs/sessions, docs/cache, agent-docs/built-ins), so calling them internal was wrong and quietly dropped them from the demo backlog. Worse, cookieSessionStorage is literally the same function as cookieSession, which I had left as deferred, so the two aliases disagreed. Moved all four back to deferred in 618fadf9; the aliases now agree. The six that stayed internal (attachWebSocket, runInstrumentation, the low-level cookie serializers, runWithActionSignal) have no app-facing doc surface, so those hold.

The round-1 fixes verified clean: the manifest hardcode is right (gallery copies verbatim), and the until() field-init promise is SSR-safe (never awaited server-side, resolves-only, self-clearing timer).

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third pass, zoomed out to the whole manifest. The #861 regex bucketing was leakier than the first two rounds showed. readBody, stampRemoteIp, attachWebSocket, and the buildRouteTable/matchPage/matchApi/rawActionRequest test helpers are all documented app-facing (api-routes, rate-limiting, the testing guide) but were marked internal, and on the core side render, renderStream, and renderToString are literally in the public API table but were also internal. All moved to deferred in 42bdcaab.

Rather than keep finding these one round at a time, I swept EVERY remaining internal-marked core and server export for a documented import ... from '@webjsdev/...' usage. Nothing else flagged: the only core hits left are parse/stringify, which appear solely in the 'Generated by webjs (you never see this file)' RPC-stub snippet in the server-actions docs, so those are genuinely internal. Also fixed the PR body, which still described the round-1 '+10 internal' state that rounds 2-3 walked back.

Gate 12/12, scaffold suite 44/44.

@vivek7405 vivek7405 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fourth pass to confirm the classification is settled. Re-audited every internal-marked core and server export against the docs one more time: only parse and stringify hit, and only inside the generated-stub snippet, so the split is honest now. Body matches the final numbers, the JSON is well formed with no empty reasons or cross-section dupes, and the gate reconciles the live surfaces exactly. Clean.

@vivek7405 vivek7405 marked this pull request as ready for review July 9, 2026 08:58
vivek7405 added 9 commits July 9, 2026 16:17
The coverage-gate extension gated routing convention files and server
exports, but this framework-dev paragraph still claimed conventions stay
tier-1-only. Correct it to the shipped three-surface scope.
Close 4 coverage-gate deferred gaps: the scaffold now ships example
app/sitemap.ts (uses sitemap() from @webjsdev/server), app/robots.ts,
and app/manifest.ts, flipping the sitemap/robots/manifest routing
conventions and the server sitemap export from exempt to demonstrated.
Gate: conventions 6->9 demonstrated, server 21->22 demoed.
…keyed/unsafeHTML)

Extend the directives gallery demo with a second card covering live (a
controlled input), ref + createRef (a DOM-node handle), until (a pending
fallback), unsafeHTML (trusted raw HTML), and keyed (fresh subtree on
key change). Flips 6 core exports from deferred to demoed (core 12->18).
The niche completeness directives (guard, templateContent, asyncAppend/
asyncReplace) stay documented-deferred rather than ship contrived demos.
Round out the boundaries feature with the three remaining nested
boundary conventions: error.ts (with a crash/ sub-route that throws so
it is reachable), loading.ts (the Suspense fallback), and not-found.ts
(nearest 404, served for an unmatched subtree URL). Index links the new
routes. Flips error/loading/not-found conventions to demonstrated
(conventions 9->12). Verified: /crash renders 500, an unmatched URL 404.
Add a <reactive-meter> shadow-DOM component to the components feature:
static shadow=true with static styles=css`` (scoped CSS, the one place
the lit reflex is right), plus computed (derived signal), effect (a
browser-side reaction), and batch (coalesced writes). Flips css/computed/
effect/batch from deferred to demoed (core 18->22). Shadow DSD verified
in SSR output.
Add a <router-controls> component to the client-router feature that
drives programmatic soft navigation (navigate()) and browser snapshot
eviction (revalidate()) from JS handlers. Flips navigate/revalidate to
demoed (core 22->24).
…fold demos

Two review findings: (1) gallery files are copied verbatim with no
{{APP_NAME}} substitution, so manifest.ts served a literal '{{APP_NAME}}'
in /manifest.json; hardcode a neutral 'webjs app' the user adapts.
(2) the directives demo called later() inside render(), so until()'s new
promise identity each render flashed back to the fallback on any
re-render; create the promise once in a field. Also rephrase the inline
comments to satisfy the prose-punctuation invariant.
…internal)

Review round 2: cookieSessionStorage, storeSessionStorage (+ its alias
storeSession), and getStore are app-facing and documented (docs/sessions,
docs/cache, agent-docs/built-ins), so internal was wrong. Move them back
to deferred so they stay in the demo backlog. cookieSessionStorage now
matches its alias cookieSession. Six genuinely-internal reclassifications
(attachWebSocket, runInstrumentation, low-level cookie serializers,
runWithActionSignal) stand.
…ting

Review round 3: the #861 heuristic wrongly marked documented app-facing
exports internal. Move render/renderStream/renderToString (public API),
readBody/stampRemoteIp (api-routes/rate-limiting docs), attachWebSocket +
the buildRouteTable/matchPage/matchApi/rawActionRequest test helpers
(testing docs) to deferred. A proactive doc-import sweep of every
remaining internal-marked core+server export confirms none others are
mis-classed (parse/stringify appear only in a 'you never see this file'
generated-stub snippet, so they stay internal).
@vivek7405 vivek7405 force-pushed the feat/close-coverage-gate-gaps branch from 42bdcaa to 4c599c5 Compare July 9, 2026 10:47
@vivek7405 vivek7405 merged commit e62d7e4 into main Jul 9, 2026
@vivek7405 vivek7405 deleted the feat/close-coverage-gate-gaps branch July 9, 2026 10:47
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