@nominalso/vibe-auth 0.2.1 → 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.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
@@ -60,7 +60,7 @@ function parseCallbackMessage(data) {
60
60
  }
61
61
  return null;
62
62
  }
63
- function createSilentAuth(config, getHostPrincipal) {
63
+ function createSilentAuth(config, getHostPrincipal, onBound) {
64
64
  const { supabase, provider, callbackPath, timeouts, lockName, boundUserKey } = config;
65
65
  async function getSessionUserId() {
66
66
  const { data } = await supabase.auth.getSession();
@@ -98,7 +98,10 @@ function createSilentAuth(config, getHostPrincipal) {
98
98
  if (priorUserId !== null && sessionUserId === priorUserId) return false;
99
99
  }
100
100
  const host = getHostPrincipal?.();
101
- if (host) writeBoundMarker(host);
101
+ if (host) {
102
+ writeBoundMarker(host);
103
+ onBound?.(host);
104
+ }
102
105
  return true;
103
106
  }
104
107
  function runHiddenAuthFrame(url) {
@@ -246,6 +249,7 @@ function createSilentAuth(config, getHostPrincipal) {
246
249
  ensureSession,
247
250
  rebindSession,
248
251
  isBoundTo,
252
+ stampBoundMarker: writeBoundMarker,
249
253
  clearBoundUser,
250
254
  completeWithResult
251
255
  };
@@ -520,6 +524,14 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
520
524
  let applyGen = 0;
521
525
  let pendingSessionRead;
522
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
+ };
523
535
  const applyPrincipal = async (principal) => {
524
536
  hostPrincipal = principal;
525
537
  const gen = ++applyGen;
@@ -532,10 +544,11 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
532
544
  sessionOk = await silentAuth.hasValidSession().catch(() => false);
533
545
  }
534
546
  if (cancelled || gen !== applyGen) return;
535
- if (sessionOk && silentAuth.isBoundTo(principal)) {
547
+ if (sessionBelongsTo(principal, sessionOk)) {
536
548
  setStatus("authenticated" /* Authenticated */);
537
549
  return;
538
550
  }
551
+ if (RETURNING_FROM_OAUTH) return;
539
552
  const ok = await silentAuth.rebindSession(principal).catch(() => false);
540
553
  if (cancelled || gen !== applyGen) return;
541
554
  if (!ok) void supabase.auth.signOut().catch(() => {
@@ -550,12 +563,13 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
550
563
  if (principal) {
551
564
  const sessionOk = await silentAuth.hasValidSession().catch(() => false);
552
565
  if (cancelled || gen !== applyGen) return;
553
- if (!(sessionOk && silentAuth.isBoundTo(principal))) return;
566
+ if (!sessionBelongsTo(principal, sessionOk)) return;
554
567
  }
555
568
  setStatus("authenticated" /* Authenticated */);
556
569
  };
557
570
  async function resolve() {
558
571
  if (RETURNING_FROM_OAUTH) {
572
+ acceptPrincipalUpdates = true;
559
573
  oauthTimer = window.setTimeout(() => {
560
574
  if (!cancelled)
561
575
  setStatus((s) => s === "checking" /* Checking */ ? "unauthenticated" /* Unauthenticated */ : s);
@@ -600,8 +614,10 @@ function createAuthGate(config, silentAuth, interactiveAuth, hostAuth) {
600
614
  window.addEventListener("storage", onStorage);
601
615
  const { data: sub } = supabase.auth.onAuthStateChange((event, session) => {
602
616
  if (cancelled || event === "INITIAL_SESSION") return;
603
- if (session) void openIfBound();
604
- else {
617
+ if (session) {
618
+ if (RETURNING_FROM_OAUTH && event === "SIGNED_IN") redeemedHere = true;
619
+ void openIfBound();
620
+ } else {
605
621
  pendingSessionRead = void 0;
606
622
  setStatus((s) => s === "authenticated" /* Authenticated */ ? "unauthenticated" /* Unauthenticated */ : s);
607
623
  }
@@ -687,12 +703,58 @@ function createHostAuth(config, silentAuth) {
687
703
  };
688
704
  }
689
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
+
690
748
  // src/createVibeAuth.ts
691
749
  function createVibeAuth(userConfig) {
692
750
  const config = resolveConfig(userConfig);
693
751
  assertPkce(config.supabase);
694
752
  let hostAuth;
695
- const silentAuth = createSilentAuth(config, () => hostAuth.getPrincipal());
753
+ const silentAuth = createSilentAuth(
754
+ config,
755
+ () => hostAuth.getPrincipal(),
756
+ (principal) => hostAuth.seedLastUserId(principal.userId, principal.tenant)
757
+ );
696
758
  const interactiveAuth = createInteractiveAuth(config, silentAuth);
697
759
  const { SilentCallback } = createCallbackHandler();
698
760
  hostAuth = createHostAuth(config, silentAuth);
@@ -713,6 +775,7 @@ function createVibeAuth(userConfig) {
713
775
  AuthGate,
714
776
  DefaultSignInScreen,
715
777
  SilentCallback,
778
+ wireVibeApp: createWireVibeApp(config, hostAuth),
716
779
  wireHostAuth,
717
780
  seedLastUserId,
718
781
  RESULT_MESSAGE_TYPE,