@nominalso/vibe-auth 0.2.2 → 0.2.3
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.
- package/dist/index.cjs +60 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -1
- package/dist/index.d.ts +114 -1
- package/dist/index.js +60 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -147,6 +147,15 @@ interface SilentAuth {
|
|
|
147
147
|
* `rebindSession`.
|
|
148
148
|
*/
|
|
149
149
|
isBoundTo(principal: HostPrincipal): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Record that the live session belongs to this principal.
|
|
152
|
+
*
|
|
153
|
+
* Normally `completeWithResult` does this after redeeming a code. The
|
|
154
|
+
* top-level redirect flow never routes through that function — the session
|
|
155
|
+
* arrives via supabase-js's `detectSessionInUrl` — so `AuthGate` stamps it
|
|
156
|
+
* there instead, and only for a `SIGNED_IN` it observed in that document.
|
|
157
|
+
*/
|
|
158
|
+
stampBoundMarker(principal: HostPrincipal): void;
|
|
150
159
|
clearBoundUser(): void;
|
|
151
160
|
/** @internal exposed for interactive.ts, which shares completeWithResult/parseCallbackMessage's private helpers */
|
|
152
161
|
completeWithResult(result: AuthResult): Promise<boolean>;
|
|
@@ -238,6 +247,97 @@ interface AuthGateBundle {
|
|
|
238
247
|
*/
|
|
239
248
|
declare function createAuthGate(config: ResolvedVibeAuthConfig, silentAuth: SilentAuth, interactiveAuth: InteractiveAuth, hostAuth?: HostAuth): AuthGateBundle;
|
|
240
249
|
|
|
250
|
+
/**
|
|
251
|
+
* The contract between this package and `@nominalso/vibe-bridge`, plus the
|
|
252
|
+
* shape `wireVibeApp` hands back.
|
|
253
|
+
*
|
|
254
|
+
* Its own module for the same reason as `principal.ts`: `wireVibeApp` and
|
|
255
|
+
* `createVibeAuth` both speak these types, and consumers re-export them, so
|
|
256
|
+
* putting them in the implementation would make every reader import the
|
|
257
|
+
* implementation to name a type.
|
|
258
|
+
*
|
|
259
|
+
* Everything here is **structural**. `@nominalso/vibe-auth` must keep zero
|
|
260
|
+
* dependency on `@nominalso/vibe-bridge` — they publish separately and version
|
|
261
|
+
* independently, and a hard dependency would couple their release cycles.
|
|
262
|
+
*/
|
|
263
|
+
/**
|
|
264
|
+
* The subset of `ContextPayload` this package reads. Apps get their own full
|
|
265
|
+
* `ContextPayload` back via the `Ctx` type parameter; structural assignability
|
|
266
|
+
* already allows the extra properties.
|
|
267
|
+
*
|
|
268
|
+
* Deliberately NO index signature. One here looks harmless but makes the
|
|
269
|
+
* constraint unsatisfiable by the very type it exists for: TypeScript does not
|
|
270
|
+
* give an `interface` an implicit index signature, so `ContextPayload` — an
|
|
271
|
+
* interface — failed `Ctx extends HostContextLike`.
|
|
272
|
+
*/
|
|
273
|
+
interface HostContextLike {
|
|
274
|
+
tenant: string;
|
|
275
|
+
user: {
|
|
276
|
+
id: string;
|
|
277
|
+
};
|
|
278
|
+
/** Set by the host only when it lets a user clear the app's data. */
|
|
279
|
+
enableDataReset?: boolean;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The subset of `VibeAppBridge` `wireVibeApp` needs.
|
|
283
|
+
*
|
|
284
|
+
* Deliberately does NOT extend `AuthBridgeLike`, i.e. does not require
|
|
285
|
+
* `onAuthChange`. That method is `@internal` in `@nominalso/vibe-bridge` and
|
|
286
|
+
* stripped from its published `.d.ts`, so a real `VibeAppBridge` does not
|
|
287
|
+
* satisfy a constraint that demands it — requiring it here would make
|
|
288
|
+
* `wireVibeApp(bridge)` reject the only argument it is ever passed.
|
|
289
|
+
*
|
|
290
|
+
* It still exists at runtime, and `wireVibeApp` is the sanctioned caller, so
|
|
291
|
+
* that one call casts to `AuthBridgeLike` locally.
|
|
292
|
+
*
|
|
293
|
+
* `onDataReset` / `onSubrouteRequest` are optional so an app on an older bridge
|
|
294
|
+
* still type-checks; `wireVibeApp` feature-detects both before calling them.
|
|
295
|
+
*/
|
|
296
|
+
interface VibeBridgeLike {
|
|
297
|
+
onContextChange(cb: (ctx: HostContextLike) => void): () => void;
|
|
298
|
+
connect(): Promise<HostContextLike>;
|
|
299
|
+
onDataReset?(cb: (payload: ResetPayloadLike) => void): () => void;
|
|
300
|
+
onSubrouteRequest?(cb: (subroute: string) => void): () => void;
|
|
301
|
+
}
|
|
302
|
+
/** Informational payload of a host `RESET_DATA` push. */
|
|
303
|
+
interface ResetPayloadLike {
|
|
304
|
+
reason?: string;
|
|
305
|
+
}
|
|
306
|
+
interface WireVibeAppOptions<Ctx extends HostContextLike> {
|
|
307
|
+
/**
|
|
308
|
+
* Extra work on each post-connect context push (subsidiary / period switch)
|
|
309
|
+
* — refetch scoped data here. The built-in store updates first, so
|
|
310
|
+
* `getHostContext()` already returns `next` by the time this runs.
|
|
311
|
+
*/
|
|
312
|
+
onContextChange?: (next: Ctx) => void;
|
|
313
|
+
/**
|
|
314
|
+
* Reset the app to its initial state. Wired only when the host actually
|
|
315
|
+
* enabled it (`ctx.enableDataReset`), so passing it is always safe.
|
|
316
|
+
*/
|
|
317
|
+
onDataReset?: (payload: ResetPayloadLike) => void;
|
|
318
|
+
/**
|
|
319
|
+
* Host-originated subroute pushes. Registering anything — even a no-op —
|
|
320
|
+
* suppresses the bridge's `history.pushState` fallback, which otherwise
|
|
321
|
+
* navigates a single-route iframe away and unmounts in-flight dialogs.
|
|
322
|
+
*/
|
|
323
|
+
onSubrouteRequest?: (subroute: string) => void;
|
|
324
|
+
}
|
|
325
|
+
interface VibeAppWiring<Ctx extends HostContextLike> {
|
|
326
|
+
/** The bridge you passed in, returned so one import covers the whole module. */
|
|
327
|
+
bridge: VibeBridgeLike;
|
|
328
|
+
/**
|
|
329
|
+
* Resolves the host context, or `null` when there is no host to reach — a
|
|
330
|
+
* standalone preview, the OAuth callback document, or SSR. **Never rejects:**
|
|
331
|
+
* a failed handshake is expected outside Nominal, and `AuthGate` falls back
|
|
332
|
+
* to interactive sign-in on its own.
|
|
333
|
+
*/
|
|
334
|
+
hostContext: Promise<Ctx | null>;
|
|
335
|
+
/** Current host context; `null` before connect resolves and when standalone. */
|
|
336
|
+
getHostContext(): Ctx | null;
|
|
337
|
+
/** Subscribe to context changes. Returns an unsubscribe function. */
|
|
338
|
+
subscribeHostContext(listener: (ctx: Ctx | null) => void): () => void;
|
|
339
|
+
}
|
|
340
|
+
|
|
241
341
|
interface VibeAuth {
|
|
242
342
|
/** True when a Supabase session already exists locally. */
|
|
243
343
|
hasValidSession(): Promise<boolean>;
|
|
@@ -269,11 +369,24 @@ interface VibeAuth {
|
|
|
269
369
|
DefaultSignInScreen: ReturnType<typeof createAuthGate>['DefaultSignInScreen'];
|
|
270
370
|
/** Mount this, and ONLY this, at `callbackPath` — ungated, outside `AuthGate`. */
|
|
271
371
|
SilentCallback: ReturnType<typeof createCallbackHandler>['SilentCallback'];
|
|
372
|
+
/**
|
|
373
|
+
* **The one call an embedded app needs.** Wires the bridge to this auth
|
|
374
|
+
* instance and starts the host handshake — `onContextChange`, `wireHostAuth`,
|
|
375
|
+
* the callback-document guard, `connect()`, and `seedLastUserId` — in the
|
|
376
|
+
* one correct order. Returns a context store plus the connect promise.
|
|
377
|
+
*
|
|
378
|
+
* Call it once, synchronously, at module scope of an eagerly imported module.
|
|
379
|
+
* Prefer this over calling `wireHostAuth` / `seedLastUserId` by hand.
|
|
380
|
+
*/
|
|
381
|
+
wireVibeApp<Ctx extends HostContextLike>(bridge: VibeBridgeLike, options?: WireVibeAppOptions<Ctx>): VibeAppWiring<Ctx>;
|
|
272
382
|
/**
|
|
273
383
|
* Wire the host's auth signal to this Supabase client. Call at module
|
|
274
384
|
* scope, BEFORE `bridge.connect()`, so `AuthGate` will not open on a
|
|
275
385
|
* leftover Supabase session before it knows the current Nominal user.
|
|
276
386
|
* Returns the unsubscribe function.
|
|
387
|
+
*
|
|
388
|
+
* Low-level: `wireVibeApp` already does this in the right order. Reach for
|
|
389
|
+
* this only when an app genuinely cannot hand its bridge over.
|
|
277
390
|
*/
|
|
278
391
|
wireHostAuth(bridge: AuthBridgeLike): () => void;
|
|
279
392
|
/**
|
|
@@ -339,4 +452,4 @@ declare function createVibeAuth(userConfig: VibeAuthConfig): VibeAuth;
|
|
|
339
452
|
|
|
340
453
|
declare const RESULT_MESSAGE_TYPE = "silent-auth-result";
|
|
341
454
|
|
|
342
|
-
export { type AuthBridgeLike, type AuthGateProps, type AuthResult, AuthResultKind, type HostPrincipal, RESULT_MESSAGE_TYPE, type ResolvedVibeAuthConfig, type ResolvedVibeAuthTimeouts, type SignInAuthenticated, type SignInFailed, SignInFailureReason, SignInKind, type SignInRedirecting, type SignInResult, type VibeAuth, type VibeAuthConfig, type VibeAuthTimeouts, createVibeAuth };
|
|
455
|
+
export { type AuthBridgeLike, type AuthGateProps, type AuthResult, AuthResultKind, type HostContextLike, type HostPrincipal, RESULT_MESSAGE_TYPE, type ResetPayloadLike, type ResolvedVibeAuthConfig, type ResolvedVibeAuthTimeouts, type SignInAuthenticated, type SignInFailed, SignInFailureReason, SignInKind, type SignInRedirecting, type SignInResult, type VibeAppWiring, type VibeAuth, type VibeAuthConfig, type VibeAuthTimeouts, type VibeBridgeLike, type WireVibeAppOptions, createVibeAuth };
|
package/dist/index.d.ts
CHANGED
|
@@ -147,6 +147,15 @@ interface SilentAuth {
|
|
|
147
147
|
* `rebindSession`.
|
|
148
148
|
*/
|
|
149
149
|
isBoundTo(principal: HostPrincipal): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Record that the live session belongs to this principal.
|
|
152
|
+
*
|
|
153
|
+
* Normally `completeWithResult` does this after redeeming a code. The
|
|
154
|
+
* top-level redirect flow never routes through that function — the session
|
|
155
|
+
* arrives via supabase-js's `detectSessionInUrl` — so `AuthGate` stamps it
|
|
156
|
+
* there instead, and only for a `SIGNED_IN` it observed in that document.
|
|
157
|
+
*/
|
|
158
|
+
stampBoundMarker(principal: HostPrincipal): void;
|
|
150
159
|
clearBoundUser(): void;
|
|
151
160
|
/** @internal exposed for interactive.ts, which shares completeWithResult/parseCallbackMessage's private helpers */
|
|
152
161
|
completeWithResult(result: AuthResult): Promise<boolean>;
|
|
@@ -238,6 +247,97 @@ interface AuthGateBundle {
|
|
|
238
247
|
*/
|
|
239
248
|
declare function createAuthGate(config: ResolvedVibeAuthConfig, silentAuth: SilentAuth, interactiveAuth: InteractiveAuth, hostAuth?: HostAuth): AuthGateBundle;
|
|
240
249
|
|
|
250
|
+
/**
|
|
251
|
+
* The contract between this package and `@nominalso/vibe-bridge`, plus the
|
|
252
|
+
* shape `wireVibeApp` hands back.
|
|
253
|
+
*
|
|
254
|
+
* Its own module for the same reason as `principal.ts`: `wireVibeApp` and
|
|
255
|
+
* `createVibeAuth` both speak these types, and consumers re-export them, so
|
|
256
|
+
* putting them in the implementation would make every reader import the
|
|
257
|
+
* implementation to name a type.
|
|
258
|
+
*
|
|
259
|
+
* Everything here is **structural**. `@nominalso/vibe-auth` must keep zero
|
|
260
|
+
* dependency on `@nominalso/vibe-bridge` — they publish separately and version
|
|
261
|
+
* independently, and a hard dependency would couple their release cycles.
|
|
262
|
+
*/
|
|
263
|
+
/**
|
|
264
|
+
* The subset of `ContextPayload` this package reads. Apps get their own full
|
|
265
|
+
* `ContextPayload` back via the `Ctx` type parameter; structural assignability
|
|
266
|
+
* already allows the extra properties.
|
|
267
|
+
*
|
|
268
|
+
* Deliberately NO index signature. One here looks harmless but makes the
|
|
269
|
+
* constraint unsatisfiable by the very type it exists for: TypeScript does not
|
|
270
|
+
* give an `interface` an implicit index signature, so `ContextPayload` — an
|
|
271
|
+
* interface — failed `Ctx extends HostContextLike`.
|
|
272
|
+
*/
|
|
273
|
+
interface HostContextLike {
|
|
274
|
+
tenant: string;
|
|
275
|
+
user: {
|
|
276
|
+
id: string;
|
|
277
|
+
};
|
|
278
|
+
/** Set by the host only when it lets a user clear the app's data. */
|
|
279
|
+
enableDataReset?: boolean;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The subset of `VibeAppBridge` `wireVibeApp` needs.
|
|
283
|
+
*
|
|
284
|
+
* Deliberately does NOT extend `AuthBridgeLike`, i.e. does not require
|
|
285
|
+
* `onAuthChange`. That method is `@internal` in `@nominalso/vibe-bridge` and
|
|
286
|
+
* stripped from its published `.d.ts`, so a real `VibeAppBridge` does not
|
|
287
|
+
* satisfy a constraint that demands it — requiring it here would make
|
|
288
|
+
* `wireVibeApp(bridge)` reject the only argument it is ever passed.
|
|
289
|
+
*
|
|
290
|
+
* It still exists at runtime, and `wireVibeApp` is the sanctioned caller, so
|
|
291
|
+
* that one call casts to `AuthBridgeLike` locally.
|
|
292
|
+
*
|
|
293
|
+
* `onDataReset` / `onSubrouteRequest` are optional so an app on an older bridge
|
|
294
|
+
* still type-checks; `wireVibeApp` feature-detects both before calling them.
|
|
295
|
+
*/
|
|
296
|
+
interface VibeBridgeLike {
|
|
297
|
+
onContextChange(cb: (ctx: HostContextLike) => void): () => void;
|
|
298
|
+
connect(): Promise<HostContextLike>;
|
|
299
|
+
onDataReset?(cb: (payload: ResetPayloadLike) => void): () => void;
|
|
300
|
+
onSubrouteRequest?(cb: (subroute: string) => void): () => void;
|
|
301
|
+
}
|
|
302
|
+
/** Informational payload of a host `RESET_DATA` push. */
|
|
303
|
+
interface ResetPayloadLike {
|
|
304
|
+
reason?: string;
|
|
305
|
+
}
|
|
306
|
+
interface WireVibeAppOptions<Ctx extends HostContextLike> {
|
|
307
|
+
/**
|
|
308
|
+
* Extra work on each post-connect context push (subsidiary / period switch)
|
|
309
|
+
* — refetch scoped data here. The built-in store updates first, so
|
|
310
|
+
* `getHostContext()` already returns `next` by the time this runs.
|
|
311
|
+
*/
|
|
312
|
+
onContextChange?: (next: Ctx) => void;
|
|
313
|
+
/**
|
|
314
|
+
* Reset the app to its initial state. Wired only when the host actually
|
|
315
|
+
* enabled it (`ctx.enableDataReset`), so passing it is always safe.
|
|
316
|
+
*/
|
|
317
|
+
onDataReset?: (payload: ResetPayloadLike) => void;
|
|
318
|
+
/**
|
|
319
|
+
* Host-originated subroute pushes. Registering anything — even a no-op —
|
|
320
|
+
* suppresses the bridge's `history.pushState` fallback, which otherwise
|
|
321
|
+
* navigates a single-route iframe away and unmounts in-flight dialogs.
|
|
322
|
+
*/
|
|
323
|
+
onSubrouteRequest?: (subroute: string) => void;
|
|
324
|
+
}
|
|
325
|
+
interface VibeAppWiring<Ctx extends HostContextLike> {
|
|
326
|
+
/** The bridge you passed in, returned so one import covers the whole module. */
|
|
327
|
+
bridge: VibeBridgeLike;
|
|
328
|
+
/**
|
|
329
|
+
* Resolves the host context, or `null` when there is no host to reach — a
|
|
330
|
+
* standalone preview, the OAuth callback document, or SSR. **Never rejects:**
|
|
331
|
+
* a failed handshake is expected outside Nominal, and `AuthGate` falls back
|
|
332
|
+
* to interactive sign-in on its own.
|
|
333
|
+
*/
|
|
334
|
+
hostContext: Promise<Ctx | null>;
|
|
335
|
+
/** Current host context; `null` before connect resolves and when standalone. */
|
|
336
|
+
getHostContext(): Ctx | null;
|
|
337
|
+
/** Subscribe to context changes. Returns an unsubscribe function. */
|
|
338
|
+
subscribeHostContext(listener: (ctx: Ctx | null) => void): () => void;
|
|
339
|
+
}
|
|
340
|
+
|
|
241
341
|
interface VibeAuth {
|
|
242
342
|
/** True when a Supabase session already exists locally. */
|
|
243
343
|
hasValidSession(): Promise<boolean>;
|
|
@@ -269,11 +369,24 @@ interface VibeAuth {
|
|
|
269
369
|
DefaultSignInScreen: ReturnType<typeof createAuthGate>['DefaultSignInScreen'];
|
|
270
370
|
/** Mount this, and ONLY this, at `callbackPath` — ungated, outside `AuthGate`. */
|
|
271
371
|
SilentCallback: ReturnType<typeof createCallbackHandler>['SilentCallback'];
|
|
372
|
+
/**
|
|
373
|
+
* **The one call an embedded app needs.** Wires the bridge to this auth
|
|
374
|
+
* instance and starts the host handshake — `onContextChange`, `wireHostAuth`,
|
|
375
|
+
* the callback-document guard, `connect()`, and `seedLastUserId` — in the
|
|
376
|
+
* one correct order. Returns a context store plus the connect promise.
|
|
377
|
+
*
|
|
378
|
+
* Call it once, synchronously, at module scope of an eagerly imported module.
|
|
379
|
+
* Prefer this over calling `wireHostAuth` / `seedLastUserId` by hand.
|
|
380
|
+
*/
|
|
381
|
+
wireVibeApp<Ctx extends HostContextLike>(bridge: VibeBridgeLike, options?: WireVibeAppOptions<Ctx>): VibeAppWiring<Ctx>;
|
|
272
382
|
/**
|
|
273
383
|
* Wire the host's auth signal to this Supabase client. Call at module
|
|
274
384
|
* scope, BEFORE `bridge.connect()`, so `AuthGate` will not open on a
|
|
275
385
|
* leftover Supabase session before it knows the current Nominal user.
|
|
276
386
|
* Returns the unsubscribe function.
|
|
387
|
+
*
|
|
388
|
+
* Low-level: `wireVibeApp` already does this in the right order. Reach for
|
|
389
|
+
* this only when an app genuinely cannot hand its bridge over.
|
|
277
390
|
*/
|
|
278
391
|
wireHostAuth(bridge: AuthBridgeLike): () => void;
|
|
279
392
|
/**
|
|
@@ -339,4 +452,4 @@ declare function createVibeAuth(userConfig: VibeAuthConfig): VibeAuth;
|
|
|
339
452
|
|
|
340
453
|
declare const RESULT_MESSAGE_TYPE = "silent-auth-result";
|
|
341
454
|
|
|
342
|
-
export { type AuthBridgeLike, type AuthGateProps, type AuthResult, AuthResultKind, type HostPrincipal, RESULT_MESSAGE_TYPE, type ResolvedVibeAuthConfig, type ResolvedVibeAuthTimeouts, type SignInAuthenticated, type SignInFailed, SignInFailureReason, SignInKind, type SignInRedirecting, type SignInResult, type VibeAuth, type VibeAuthConfig, type VibeAuthTimeouts, createVibeAuth };
|
|
455
|
+
export { type AuthBridgeLike, type AuthGateProps, type AuthResult, AuthResultKind, type HostContextLike, type HostPrincipal, RESULT_MESSAGE_TYPE, type ResetPayloadLike, type ResolvedVibeAuthConfig, type ResolvedVibeAuthTimeouts, type SignInAuthenticated, type SignInFailed, SignInFailureReason, SignInKind, type SignInRedirecting, type SignInResult, type VibeAppWiring, type VibeAuth, type VibeAuthConfig, type VibeAuthTimeouts, type VibeBridgeLike, type WireVibeAppOptions, createVibeAuth };
|
package/dist/index.js
CHANGED
|
@@ -249,6 +249,7 @@ function createSilentAuth(config, getHostPrincipal, onBound) {
|
|
|
249
249
|
ensureSession,
|
|
250
250
|
rebindSession,
|
|
251
251
|
isBoundTo,
|
|
252
|
+
stampBoundMarker: writeBoundMarker,
|
|
252
253
|
clearBoundUser,
|
|
253
254
|
completeWithResult
|
|
254
255
|
};
|
|
@@ -523,6 +524,14 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
|
|
|
523
524
|
let applyGen = 0;
|
|
524
525
|
let pendingSessionRead;
|
|
525
526
|
let acceptPrincipalUpdates = !hostAuth?.isHostWired();
|
|
527
|
+
let redeemedHere = false;
|
|
528
|
+
const sessionBelongsTo = (principal, sessionOk) => {
|
|
529
|
+
if (!sessionOk) return false;
|
|
530
|
+
if (silentAuth.isBoundTo(principal)) return true;
|
|
531
|
+
if (!redeemedHere) return false;
|
|
532
|
+
silentAuth.stampBoundMarker(principal);
|
|
533
|
+
return true;
|
|
534
|
+
};
|
|
526
535
|
const applyPrincipal = async (principal) => {
|
|
527
536
|
hostPrincipal = principal;
|
|
528
537
|
const gen = ++applyGen;
|
|
@@ -535,10 +544,11 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
|
|
|
535
544
|
sessionOk = await silentAuth.hasValidSession().catch(() => false);
|
|
536
545
|
}
|
|
537
546
|
if (cancelled || gen !== applyGen) return;
|
|
538
|
-
if (
|
|
547
|
+
if (sessionBelongsTo(principal, sessionOk)) {
|
|
539
548
|
setStatus("authenticated" /* Authenticated */);
|
|
540
549
|
return;
|
|
541
550
|
}
|
|
551
|
+
if (RETURNING_FROM_OAUTH) return;
|
|
542
552
|
const ok = await silentAuth.rebindSession(principal).catch(() => false);
|
|
543
553
|
if (cancelled || gen !== applyGen) return;
|
|
544
554
|
if (!ok) void supabase.auth.signOut().catch(() => {
|
|
@@ -553,12 +563,13 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
|
|
|
553
563
|
if (principal) {
|
|
554
564
|
const sessionOk = await silentAuth.hasValidSession().catch(() => false);
|
|
555
565
|
if (cancelled || gen !== applyGen) return;
|
|
556
|
-
if (!(sessionOk
|
|
566
|
+
if (!sessionBelongsTo(principal, sessionOk)) return;
|
|
557
567
|
}
|
|
558
568
|
setStatus("authenticated" /* Authenticated */);
|
|
559
569
|
};
|
|
560
570
|
async function resolve() {
|
|
561
571
|
if (RETURNING_FROM_OAUTH) {
|
|
572
|
+
acceptPrincipalUpdates = true;
|
|
562
573
|
oauthTimer = window.setTimeout(() => {
|
|
563
574
|
if (!cancelled)
|
|
564
575
|
setStatus((s) => s === "checking" /* Checking */ ? "unauthenticated" /* Unauthenticated */ : s);
|
|
@@ -603,8 +614,10 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
|
|
|
603
614
|
window.addEventListener("storage", onStorage);
|
|
604
615
|
const { data: sub } = supabase.auth.onAuthStateChange((event, session) => {
|
|
605
616
|
if (cancelled || event === "INITIAL_SESSION") return;
|
|
606
|
-
if (session)
|
|
607
|
-
|
|
617
|
+
if (session) {
|
|
618
|
+
if (RETURNING_FROM_OAUTH && event === "SIGNED_IN") redeemedHere = true;
|
|
619
|
+
void openIfBound();
|
|
620
|
+
} else {
|
|
608
621
|
pendingSessionRead = void 0;
|
|
609
622
|
setStatus((s) => s === "authenticated" /* Authenticated */ ? "unauthenticated" /* Unauthenticated */ : s);
|
|
610
623
|
}
|
|
@@ -690,6 +703,48 @@ function createHostAuth(config, silentAuth) {
|
|
|
690
703
|
};
|
|
691
704
|
}
|
|
692
705
|
|
|
706
|
+
// src/wireVibeApp.ts
|
|
707
|
+
function createWireVibeApp(config, hostAuth) {
|
|
708
|
+
return function wireVibeApp(bridge, options = {}) {
|
|
709
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
710
|
+
let current = null;
|
|
711
|
+
function publish(next) {
|
|
712
|
+
current = next;
|
|
713
|
+
for (const listener of [...listeners]) listener(next);
|
|
714
|
+
}
|
|
715
|
+
bridge.onContextChange((next) => {
|
|
716
|
+
publish(next);
|
|
717
|
+
options.onContextChange?.(next);
|
|
718
|
+
});
|
|
719
|
+
if (options.onSubrouteRequest) bridge.onSubrouteRequest?.(options.onSubrouteRequest);
|
|
720
|
+
hostAuth.wireHostAuth(bridge);
|
|
721
|
+
const noHost = typeof window === "undefined" || window.location.pathname === config.callbackPath;
|
|
722
|
+
const hostContext = noHost ? Promise.resolve(null) : bridge.connect().then((ctx) => {
|
|
723
|
+
const next = ctx;
|
|
724
|
+
hostAuth.seedLastUserId(next.user.id, next.tenant);
|
|
725
|
+
if (next.enableDataReset && options.onDataReset) {
|
|
726
|
+
bridge.onDataReset?.(options.onDataReset);
|
|
727
|
+
}
|
|
728
|
+
publish(next);
|
|
729
|
+
return next;
|
|
730
|
+
}).catch((error) => {
|
|
731
|
+
console.warn("[vibe-auth] host connect failed", error);
|
|
732
|
+
return null;
|
|
733
|
+
});
|
|
734
|
+
return {
|
|
735
|
+
bridge,
|
|
736
|
+
hostContext,
|
|
737
|
+
getHostContext: () => current,
|
|
738
|
+
subscribeHostContext: (listener) => {
|
|
739
|
+
listeners.add(listener);
|
|
740
|
+
return () => {
|
|
741
|
+
listeners.delete(listener);
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
693
748
|
// src/createVibeAuth.ts
|
|
694
749
|
function createVibeAuth(userConfig) {
|
|
695
750
|
const config = resolveConfig(userConfig);
|
|
@@ -720,6 +775,7 @@ function createVibeAuth(userConfig) {
|
|
|
720
775
|
AuthGate,
|
|
721
776
|
DefaultSignInScreen,
|
|
722
777
|
SilentCallback,
|
|
778
|
+
wireVibeApp: createWireVibeApp(config, hostAuth),
|
|
723
779
|
wireHostAuth,
|
|
724
780
|
seedLastUserId,
|
|
725
781
|
RESULT_MESSAGE_TYPE,
|