Skip to content

feat(fragment): Web Fragments render engine — /@fragment gateway + render_engine switch [EE2-2313]#90

Merged
AndrewKirkovski merged 8 commits into
masterfrom
ee2-2313-web-fragments
Jul 21, 2026
Merged

feat(fragment): Web Fragments render engine — /@fragment gateway + render_engine switch [EE2-2313]#90
AndrewKirkovski merged 8 commits into
masterfrom
ee2-2313-web-fragments

Conversation

@AndrewKirkovski

@AndrewKirkovski AndrewKirkovski commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Backend for the Web Fragments render engine (EE2-2313) — an experimental second page-render engine for the Wippy Web Host, alongside the main srcdoc-iframe engine (the default). A view.page renders as a reframed same-origin realm reflected into a <web-fragment> shadow root; the iframe engine remains the default and unchanged.

wippy/views (published 0.5.9)

  • /@fragment/{id}/{path...} gateway (api/fragment_gateway.lua) serving the reframing contract: a reframed stub (Sec-Fetch-Dest: iframe), the app document transformed for the realm, and asset proxying. Per-branch Cache-Control (stub shared-cacheable; access-gated document/assets private) and a memoized host import-map.
  • The gateway owns its own /@fragment router. wippy/views self-declares the http.router and binds it to a new server requirement that defaults to app:gateway. A consuming app needs zero fragment wiring — override server only if your http.service entry has a different id.
  • No boot abort. A consumer that never enables fragments just boots on the main iframe engine; a deployment with fragments enabled but the gateway unreachable falls back to iframe via the host capability probe. There is no mandatory consumer-provided requirement and no fail-loud link error — a missing/unavailable fragment path degrades to iframe, it does not stop boot.
  • Per-page renderEngine projected into the page descriptor (page_registry.lua, bundled_meta.lua, api/render.lua).

wippy/facade (published 0.6.28)

  • render_engine requirement → hostConfig.renderEngine (default iframe; only the exact string fragment opts in).
  • Web Host CDN ref bumped to webcomponents-1.0.46; the loading.js public_files embed re-vendored.

Both published to the Hub. Docs: the Render Engines / Views / Facade pages in wippy/docs.

…olves absolute [EE2-2313]

The fragment_gateway entry declared public_registry/metadata_http/private_ip
policies but NOT wippy.views:public_env_runtime — the one that permits
env.get on PUBLIC_*. Every sibling endpoint (list_components, find_by_tag,
render) declares it; the gateway was the odd one out.

Consequence chain: the gateway calls page_registry.resolve_base_url(), whose
env.get("PUBLIC_API_URL") then runs under the gateway's actor and is DENIED →
origin = "" → base_url stays relative (/app/app/main/) → the document fetch is
rejected by public_metadata_http_runtime, which only allows http(s)://* →
"Fragment document fetch failed: not allowed: /app/app/main/app.html".

So PUBLIC_API_URL was never broken as a value (.env has it; the facade in the
same process resolves it; the consumer binds env_storage for views) — it simply
could not be read from this runtime.

Also add the missing `workers: 4` to the gateway pool: every sibling endpoint in
this file declares it, and without it the pool's concurrency is inconsistent with
the file's own convention — a latent head-of-line stall for fragment traffic.

Gateway error message now includes the attempted URL, which is what made this
diagnosable.

Verified: document branch returns the transformed doc (<wf-html>/<wf-head>/
<wf-body>) with the host CSS links injected; fragment-poc A–G + H–I and
fragment-parity all green (3 passed).
…[EE2-2313]

Stage 1A (BE). Surfaces a page's render engine so the host fork can read it:

- page_registry.lua: extract `render_engine = meta.render_engine` in both
  extract_page_info and pages.get (raw/nil, per the nil-propagation convention);
  add the field to the PageDetail type.
- bundled_meta.project_page_response: emit `wippy.renderEngine = w.renderEngine`
  (from the bundled wippy-meta.json), then a SCALAR (not deep-merge) YAML operator
  override — `page.render_engine` wins when present. Mirrors the proxy/config
  overlay precedence (YAML-first).
- render.lua synthesize_from_registry: emit `renderEngine = page.render_engine`
  so unmigrated pages (no wippy-meta.json) still receive the operator override.

Wire response stays wippy-component-1.0 (no schema bump): a new optional field
with a defined absent behavior. Verified live: operator meta.render_engine: iframe
on a page renders it as srcdoc under a global fragment deployment.
…emo [EE2-2313]

Rename the gateway route /frag/ -> /@fragment/. Add per-branch Cache-Control: stub -> public,max-age=300 (no page, no can_access, deploy-stable); document + assets -> private (both are can_access-gated per user, so a shared/CDN cache would serve the 200 to users who fail the check - private = browser-only caching, the fail-safe; public/immutable is a future win gated on a per-page public flag). Memoize the host import-map fetch (was a synchronous GET on every fragment mount). Remove the dead strip_doctype/neutralize passes (defined but never called; reframed's writable-dom handles script activation).
… [EE2-2313]

Split the fragment_gateway endpoints off api_router onto a new fragment_router ns.requirement (mirrors the api_router pattern), so the consumer mounts the gateway on its OWN top-level /@Fragment prefix instead of under /api/public - CDN-cache-routable and free of api.public's token_auth. Endpoint path is now router-relative (/{id}/{path...}); the router supplies /@Fragment. Investigation confirmed the two other deferred Part C items are genuinely infeasible: reframed 0.8.2 derives stub+document+assets from ONE src (elements.js:1099/1145/1185; no 3-URL split without forking the library), and the page descriptor carries only a URL - no fs handle, no fs.* policy in views, and absolute-URL pages have no local fs at all - so no GENERIC fs-direct read; the loopback http_client stays.
…bed [EE2-2313]

Bump the Web Host CDN ref to webcomponents-1.0.46 across the checklist files (_index.yaml fe_facade_url default, Makefile WEB_HOST_CDN, README, config_handler_test) plus the stale config_dependency_test (1.0.39->1.0.46), and re-vendor the loading.js public_files embed from the live 1.0.46 CDN. Published as wippy/facade@0.6.28 (views@0.5.8 carries the fragment gateway, published from its already-committed source).
…ring, no boot abort [EE2-2313]

The Web-Fragments gateway router is now owned by wippy/views: a self-declared
http.router (`wippy.views.api:fragment_router`, prefix /@Fragment) bound to a new
`server` ns.requirement that DEFAULTS to app:gateway. A consuming app needs zero
fragment wiring and never aborts boot — a deployment that never enables fragments
just boots on the iframe (main) engine, and opting into fragments is purely the
facade `render_engine` switch plus the FE capability probe.

This replaces the previous mandatory consumer-provided `fragment_router`
requirement (no default), which aborted boot with "unresolved requirements" for
any consumer that upgraded without wiring it — the failure mode was wrong: a
missing value must degrade to iframe, not fail loud.

Also removes the dead extract_and_merge_importmap function (never called; tripped
the lint type check). Gateway comments reframed: iframe = main/default engine,
Web Fragments = experimental opt-in.

Verified: views lint (26 entries, clean) + test (94/94) green from src/views/test
as CI runs them; app-template boots in fragment mode with no fragment wiring and
/@Fragment routes to the gateway handler.
@AndrewKirkovski
AndrewKirkovski merged commit 2634c1a into master Jul 21, 2026
18 checks passed
@AndrewKirkovski
AndrewKirkovski deleted the ee2-2313-web-fragments branch July 21, 2026 10:41
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