You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ad8414f
Browse filesBrowse the repository at this point in the historyBrowse files
fix(compose): mount guards are framework-supplied beforeLoads (#825)
* fix(compose): mount guards are framework-supplied beforeLoads — client-rendered loads resolve the session (#818)
On a client-rendered full page load the root context's session is still
unresolved when the mount gates ran, so they read a signed-in visitor as
signed out — authed pages bounced through /login, and an admin reloading
/admin landed on /dashboard.
The mount registry no longer declares gate kinds. Each mount carries
shape metadata (guarded, implemented), and constructTree takes guards —
ordinary beforeLoads keyed by mount id — supplied by the composing
framework; construction fails loudly when a guarded mount has none. The
default wiring guards authenticated with requireSession and admin with
requireAdmin, so guards resolve through the session query cache (SSR
cache-hit, client-rendered one cached fetch) and inherit the auth policy
— tri-state reads, banned handling, login redirects with the return
path — from one place. The engine loses its last auth semantics:
HostContext, GateUser, /login, and role checks are gone.
Co-authored-by: adenarc <334235131+adenarc@users.noreply.github.com>
* fix(compose): standalone-clean guard wiring — session-cache split from the auth client; CSR spec drops the anonymous logout jar
CI on #825 caught two defects in the new surface:
1. The shim's static edge into auth-guards dragged the better-auth client
(@better-auth/api-key) into everything-dev/ui/manifest, and the
standalone tarball install could not resolve it. The session-cache
primitives (sessionQueryKey, sessionQueryOptions, refreshSessionCache)
move to ui/auth-session — type-only imports, no runtime deps — and
auth.ts re-exports them; auth-guards consumes the dependency-free
module, so the manifest surface imports clean.
2. csr-guarded-reload's signed-out case injected the "logout" cookie
jar, which is an anonymous signed-in session — requireSession passes
and requireAdmin redirects to /dashboard. Signed-out is no cookies at
all; the test now asserts /admin → /login?redirect=%2Fadmin on a
fresh context.
* fix(compose): guard wiring moves to the core ui's composition entry — ui/manifest is standalone again
The standalone-install gate failure was a layering signal, not a
packaging problem: the shim put the app's auth policy inside the generic
composition surface, which must import with only everything-dev's
declared dependencies (it is what the host consumes). Both attempted
workarounds — a dependency-free auth-session split, then a dynamic
import — patched policy living in the wrong layer.
The wiring now lives where the core ui composes itself, where the auth
client is already present: a new everything-dev/ui/compose entry (the
engine plus the default guards) consumed by the client boot (hydrate)
and the ./compose expose the host loads for SSR (compose.gen template).
ui/manifest returns to main's pure re-export byte-for-byte, the
auth-session split is reverted (auth.ts is the only auth module), and
the standalone gate passes by construction.
* fix(compose): the compose entry exports only constructTree — template sources engine constants from ui/manifest
tsdown treeshake dropped the re-exported external bindings
(CORE_UI_PLUGIN_KEY, ComposePayloadSchema) from dist/ui/compose.mjs, so
every consumer that builds against dist (the ui container) failed
ESModulesLinkingError while typecheck passed — it resolves the
development condition against src. Verified the fix against both
resolution conditions: dist entries node-imported with asserted export
keys, and the host suite whose setup builds the ui container.
CORE_UI_PLUGIN_KEY and ComposePayloadSchema are engine vocabulary and
stay on ui/manifest; everything-dev/ui/compose exports the wired
constructTree, which is what it is.
---------
Co-authored-by: adenarc <334235131+adenarc@users.noreply.github.com>
Mount guards are ordinary beforeLoads supplied by the composing framework, replacing gate kinds. The mount registry now carries shape metadata only (`guarded`, `implemented`) and `constructTree` takes `guards` keyed by mount id — construction fails loudly when a guarded mount has none. The default wiring guards `authenticated` with `requireSession` and `admin` with `requireAdmin`, so a client-rendered full page load resolves the session through the query cache instead of reading a signed-in visitor as signed out (#818): full loads of authed pages no longer bounce through `/login`, and admin pages keep the `?redirect=` return path. Banned users now bounce at the mount, and signed-in non-admins on admin mounts land on `/dashboard`. The engine's invented `HostContext`/`GateUser` types are gone — it has no auth semantics.
Copy file name to clipboardExpand all lines: docs/adr/0008-manifest-composition.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -52,3 +52,7 @@ Investigation (2026-09) found the framework itself converging on the same substr
52
52
-`MOUNT_REGISTRY_VERSION` bumps; all compose digests invalidate once.
53
53
- Plan 025 (landing carve-out) executes manifest-first as the second production consumer; plan 026 (shell into host) simplifies — the host constructs all routes, in-process or manifest-fed, one construction story; plan 028 (`app.ts`) absorbs a stable manifest-only plugin shape.
54
54
-`bos types gen` gains merged route-type emission from manifests (typed cross-plugin `<Link>`) — replaces the `pluginPath` seam.
55
+
56
+
## Amendment (2026-11): mount guards are beforeLoads, not gate kinds
57
+
58
+
The registry no longer declares gate kinds (`none`/`session`/`admin`/`organization`/`team`) — each mount carries shape metadata only (`guarded`, `implemented`). Guard implementations are ordinary `beforeLoad`s supplied by the composing framework via `ConstructInput.guards`, keyed by mount id; the engine composes guard-then-declared and fails construction loudly when a `guarded` mount has no guard. The engine becomes fully auth-agnostic (no `/login`, no user/role knowledge); session semantics — tri-state query-cache reads (#527/#689), banned handling, login redirects — live in the framework glue and ride the same guards apps author in their own route files. `HostContext`/`GateUser` are deleted; the engine↔glue contract is TanStack's own `beforeLoad` shape. `MOUNT_REGISTRY_VERSION` bumps to 6; compose digests invalidate once (as the bump exists to do).
0 commit comments