feat: close coverage-gate deferred gaps with real scaffold demos#870
Conversation
vivek7405
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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.
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).
42bdcaa to
4c599c5
Compare
Advances #859
Add runnable scaffold demos for the surfaces the tier-2 coverage gate (#861) marked
deferred, flipping each manifest entry fromexempttodemo/demoed, and correcting the internal/deferred split the #861 regex bucketing got wrong. Single PR per the owner's request. Also fixes the staletier-1-onlyframework-dev note #861 left behind.Coverage moved this PR
@webjsdev/coreexports@webjsdev/serverexportsWhat landed (real demos, each verified in a booted app)
app/sitemap.ts(usessitemap()from@webjsdev/server),app/robots.ts,app/manifest.ts. Flips 3 conventions + the serversitemapexport.live,until,keyed,unsafeHTML,ref+createRef(6 core).error.ts(+ acrash/route that throws so it is reachable),loading.ts,not-found.tsin the boundaries feature (3 conventions)./crashrenders 500, an unmatched subtree URL renders 404.<reactive-meter>shadow component usingcss(scoped styles),computed,effect,batch(4 core). Shadow DSD verified in SSR.<router-controls>component drivingnavigate()andrevalidate()(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 nowdeferred, notinternal. A handful of genuine framework internals (runInstrumentation, the low-level cookie serializers,runWithActionSignal, the wireparse/stringify) stayinternal, 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: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-deferredrender/renderStream/renderToString.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.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, notCloses).Verification
webjs checkshows onlyno-scaffold-placeholder,webjs typecheckclean (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,/crash500, unmatched 404).{{APP_NAME}}in a verbatim-copied manifest, anuntil()per-render flicker, and the internal-vs-deferred mis-classifications above) fixed on the branch.packages/cli/templatesandtest/, notpackages/(core|server|cli)/srcor the served wire.