@nominalso/vibe-auth 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/AGENTS.md +22 -5
  2. package/README.md +17 -10
  3. package/llms.txt +1 -1
  4. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -39,11 +39,28 @@ export const auth = createVibeAuth({
39
39
  render the app's provider tree there (see **Root wiring** below).
40
40
  2. **The app root.** Wrap everything in `<auth.AuthGate>`. The app renders nothing of itself
41
41
  until authenticated.
42
- 3. **The host bridge**, if embedded via `@nominalso/vibe-bridge`. Call
43
- `auth.wireHostAuth(bridge)` at module scope, then start `bridge.connect()`
44
- immediately in that same module do not defer it to a React effect. Seed
45
- `auth.seedLastUserId(ctx.user.id, ctx.tenant)` once `connect()` resolves,
46
- from a **parent** of `AuthGate`, not inside gated children.
42
+ 3. **The host bridge**, if embedded via `@nominalso/vibe-bridge`. One call, at
43
+ module scope, from a **parent** of `AuthGate` — never a React effect, and
44
+ never a module reachable only through a `lazy()` chunk:
45
+
46
+ ```ts
47
+ export const bridge = new VibeAppBridge()
48
+ export const { hostContext, getHostContext, subscribeHostContext } =
49
+ auth.wireVibeApp<ContextPayload>(bridge)
50
+ ```
51
+
52
+ `wireVibeApp` registers the context subscriber and host auth **before**
53
+ `connect()`, seeds the host principal `AuthGate` waits on **before**
54
+ publishing context, skips the handshake entirely in the callback document,
55
+ and resolves `null` rather than rejecting when there is no host. Pass
56
+ `onContextChange` / `onDataReset` / `onSubrouteRequest` as options in the
57
+ same call; each is a single subscriber, and routing them through here is what
58
+ guarantees exactly one of each.
59
+
60
+ `wireHostAuth` / `seedLastUserId` are the pre-0.2.3 hand-wiring. They are
61
+ still exported for compatibility, but new code should not call them — four of
62
+ seven migrated apps got that sequence wrong, each differently, which is why
63
+ `wireVibeApp` exists.
47
64
 
48
65
  ## Root wiring (SSR — React #418)
49
66
 
package/README.md CHANGED
@@ -62,17 +62,22 @@ function Root() {
62
62
 
63
63
  ```ts
64
64
  // Start the Nominal host handshake at module scope, before React mounts.
65
+ import { VibeAppBridge, type ContextPayload } from '@nominalso/vibe-bridge'
65
66
  import { auth } from '@/lib/auth'
66
- import { bridge } from './bridge'
67
67
 
68
- const unsub = auth.wireHostAuth(bridge)
69
- export const hostContext = bridge.connect().then((ctx) => {
70
- auth.seedLastUserId(ctx.user.id, ctx.tenant)
71
- return ctx
72
- })
68
+ export const bridge = new VibeAppBridge()
69
+
70
+ export const { hostContext, getHostContext, subscribeHostContext } =
71
+ auth.wireVibeApp<ContextPayload>(bridge)
73
72
  ```
74
73
 
75
- See [`AGENTS.md`](./AGENTS.md) for SSR root wiring and host `seedLastUserId` rules.
74
+ One call does the whole handshake, in the one correct order: context subscriber
75
+ and host auth registered before `connect()`, the host principal seeded before
76
+ context is published, the handshake skipped in the callback document, and `null`
77
+ rather than a rejection when there is no host. Pass `onContextChange`,
78
+ `onDataReset` and `onSubrouteRequest` as options in the same call.
79
+
80
+ See [`AGENTS.md`](./AGENTS.md) for SSR root wiring and the full ordering rules.
76
81
  Timeouts live on `VibeAuthTimeouts` in the package `.d.ts`.
77
82
 
78
83
  ## Why a client you own, not one this package creates
@@ -87,11 +92,13 @@ control**, not a preference (the `.d.ts` on `VibeAuthConfig` explains why).
87
92
 
88
93
  - `ensureSession()` / `rebindSession({ userId, tenant })` — the cross-document-safe core (Web Lock
89
94
  serialised, terminal-capped, sibling-adopting). Most apps never call these directly; the
90
- gate and `wireHostAuth` do.
95
+ gate and `wireVibeApp` do.
91
96
  - `AuthGate` / `DefaultSignInScreen` / `SilentCallback` — the three React pieces. Override
92
97
  `signInScreen`/`loader` props on `AuthGate` for branding.
93
- - `wireHostAuth(bridge)` / `seedLastUserId(userId, tenant)` — the Nominal-host integration (logout,
94
- identity/tenant switch). Seed from a parent of `AuthGate` after `connect()`.
98
+ - `wireVibeApp(bridge, options?)` — the Nominal-host integration in one call (handshake,
99
+ host context store, logout and identity/tenant switch). Call it at module scope from a
100
+ parent of `AuthGate`. (`wireHostAuth` / `seedLastUserId` are the pre-0.2.3 hand-wiring,
101
+ still exported for compatibility; new code should not use them.)
95
102
  - Fully configurable timeouts (`VibeAuthTimeouts` in the `.d.ts`) — every value defaults to
96
103
  what shipped after this flow's production incidents.
97
104
 
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Silent Supabase OIDC login for Nominal Vibe Apps that authenticate through Supabase federated with the Nominal host's IdP (Descope). A configurable, cross-document-safe replacement for a hand-copied `silentAuth.ts`/`AuthGate.tsx` pair generated from the `silent-supabase-oidc-login` skill: Web-Lock-serialised silent SSO (`prompt=none`), identity-switch rebinding, a popup/top-level-redirect interactive fallback, and a React `AuthGate` that never renders the app until authenticated.
4
4
 
5
- The integration is always the same: call `createVibeAuth({ supabase, provider })` once, synchronously, at the top of its own eagerly-imported module (your own PKCE-configured Supabase client — this package never creates one); mount the returned `SilentCallback` at the callback path (default `/silent-callback`), ungated, outside the gate; wrap the app root in `<AuthGate>`; and, if embedded via `@nominalso/vibe-bridge`, call `wireHostAuth(bridge)` before `connect()` then `seedLastUserId(ctx.user.id, ctx.tenant)` after from a parent of `AuthGate`. `provider` is the app's Supabase Auth → Providers value — never guess it.
5
+ The integration is always the same: call `createVibeAuth({ supabase, provider })` once, synchronously, at the top of its own eagerly-imported module (your own PKCE-configured Supabase client — this package never creates one); mount the returned `SilentCallback` at the callback path (default `/silent-callback`), ungated, outside the gate; wrap the app root in `<AuthGate>`; and, if embedded via `@nominalso/vibe-bridge`, call `wireVibeApp(bridge)` once at module scope from a parent of `AuthGate` — it performs the whole host handshake in the one correct order and returns `{ bridge, hostContext, getHostContext, subscribeHostContext }`. (`wireHostAuth` / `seedLastUserId` are the pre-0.2.3 hand-wiring; they still exist but new code should not use them.) `provider` is the app's Supabase Auth → Providers value — never guess it.
6
6
 
7
7
  Timing constraint: the factory call must happen synchronously at module scope, before any React effect or async work lets supabase-js's `detectSessionInUrl` strip the callback URL. In an SSR framework (Next.js, TanStack Start, Remix), the root layout must learn the current route from the router's own location state (identical on server and client), not from a render-time `typeof window` check — see `AGENTS.md` (Root wiring).
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nominalso/vibe-auth",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Silent Supabase OIDC login for Nominal Vibe Apps — a configurable, cross-document-safe auth gate (Web-Lock-serialised silent SSO, identity-switch rebinding, popup fallback) that replaces a hand-copied per-app implementation.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/nominalso/vibe-apps-sdk#readme",