@robelest/convex-auth 0.0.4-preview.27 → 0.0.4-preview.28

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 (88) hide show
  1. package/README.md +3 -5
  2. package/dist/bin.js +6488 -1571
  3. package/dist/browser/index.js +10 -7
  4. package/dist/browser/locks.js +3 -5
  5. package/dist/browser/navigation.js +7 -10
  6. package/dist/browser/runtime.js +35 -33
  7. package/dist/client/core/types.js +17 -0
  8. package/dist/client/factors/device.js +26 -19
  9. package/dist/client/index.js +151 -163
  10. package/dist/client/runtime/proxy.js +6 -6
  11. package/dist/client/services/adapters.js +3 -7
  12. package/dist/client/services/http.js +2 -5
  13. package/dist/client/services/resolve.js +5 -11
  14. package/dist/client/services/runtime.js +2 -5
  15. package/dist/component/_generated/component.d.ts +46 -0
  16. package/dist/component/index.d.ts +3 -3
  17. package/dist/component/model.d.ts +25 -25
  18. package/dist/component/public/identity/sessions.js +38 -1
  19. package/dist/component/public/identity/tokens.js +81 -3
  20. package/dist/component/public/identity/verifiers.js +9 -3
  21. package/dist/component/public.js +3 -3
  22. package/dist/component/schema.d.ts +320 -320
  23. package/dist/core/index.d.ts +380 -0
  24. package/dist/core/index.js +83 -0
  25. package/dist/otel.d.ts +13 -17
  26. package/dist/otel.js +39 -49
  27. package/dist/providers/email.d.ts +2 -2
  28. package/dist/providers/password.js +8 -16
  29. package/dist/providers/phone.js +2 -9
  30. package/dist/server/auth-context.d.ts +204 -0
  31. package/dist/server/auth-context.js +76 -0
  32. package/dist/server/auth.d.ts +25 -187
  33. package/dist/server/auth.js +5 -96
  34. package/dist/server/componentContext.d.ts +12 -0
  35. package/dist/server/componentContext.js +1 -0
  36. package/dist/server/config.js +1 -12
  37. package/dist/server/constants.js +6 -0
  38. package/dist/server/contract.d.ts +1 -1
  39. package/dist/server/core.js +5 -14
  40. package/dist/server/crypto.js +26 -18
  41. package/dist/server/db.js +6 -1
  42. package/dist/server/device.js +88 -78
  43. package/dist/server/http.d.ts +4 -3
  44. package/dist/server/http.js +74 -86
  45. package/dist/server/index.d.ts +2 -1
  46. package/dist/server/limits.js +22 -15
  47. package/dist/server/mounts.d.ts +103 -103
  48. package/dist/server/mutations/account.js +6 -4
  49. package/dist/server/mutations/invalidate.js +3 -6
  50. package/dist/server/mutations/oauth.js +86 -88
  51. package/dist/server/mutations/refresh.js +45 -87
  52. package/dist/server/mutations/register.js +19 -19
  53. package/dist/server/mutations/retrieve.js +17 -15
  54. package/dist/server/mutations/signature.js +9 -13
  55. package/dist/server/mutations/signin.js +7 -3
  56. package/dist/server/mutations/signout.js +10 -15
  57. package/dist/server/mutations/store.js +22 -12
  58. package/dist/server/mutations/verifier.js +11 -6
  59. package/dist/server/mutations/verify.js +55 -46
  60. package/dist/server/oauth/runtime.js +27 -25
  61. package/dist/server/passkey.js +299 -250
  62. package/dist/server/prefetch.js +283 -281
  63. package/dist/server/refresh.js +7 -60
  64. package/dist/server/runtime.d.ts +82 -206
  65. package/dist/server/runtime.js +63 -56
  66. package/dist/server/services/config.js +5 -3
  67. package/dist/server/services/logger.js +2 -4
  68. package/dist/server/services/providers.js +2 -4
  69. package/dist/server/services/refresh.js +2 -4
  70. package/dist/server/services/resolve.js +15 -14
  71. package/dist/server/services/signin.js +2 -4
  72. package/dist/server/sessions.js +32 -33
  73. package/dist/server/signin.js +177 -142
  74. package/dist/server/sso/domain.d.ts +20 -68
  75. package/dist/server/sso/domain.js +444 -413
  76. package/dist/server/sso/http.js +53 -59
  77. package/dist/server/sso/oidc.js +94 -80
  78. package/dist/server/tokens.js +13 -3
  79. package/dist/server/totp.js +153 -116
  80. package/dist/server/types.d.ts +2 -2
  81. package/dist/server/users.js +18 -23
  82. package/dist/server/utils/cache.js +51 -0
  83. package/dist/server/utils/dispatch.js +36 -0
  84. package/dist/server/utils/retry.js +24 -0
  85. package/dist/server/utils/span.js +32 -0
  86. package/dist/shared/errors.js +9 -3
  87. package/dist/shared/log.js +20 -22
  88. package/package.json +41 -33
@@ -1,4 +1,5 @@
1
1
  import { LOG_LEVELS, logMessage } from "../shared/log.js";
2
+ import { createDeferred } from "./core/types.js";
2
3
  import { createHandshakeError } from "./errors.js";
3
4
  import { createDeviceClient } from "./factors/device.js";
4
5
  import { createTotpClient } from "./factors/totp.js";
@@ -8,10 +9,9 @@ import { isRetriableProxyRefreshError, isTransientNetworkError, parseProxyErrorB
8
9
  import { createStorageHelpers } from "./runtime/storage.js";
9
10
  import { ClientAdapterFactoriesLive, ClientAdaptersLive } from "./services/adapters.js";
10
11
  import { ClientHttpLive } from "./services/http.js";
11
- import { ClientRuntimeLive } from "./services/runtime.js";
12
12
  import { resolveClientServices } from "./services/resolve.js";
13
+ import { ClientRuntimeLive } from "./services/runtime.js";
13
14
  import { ConvexError } from "convex/values";
14
- import { Cause, Deferred, Effect, Exit, Layer, Match, Ref, Schedule } from "effect";
15
15
 
16
16
  //#region src/client/index.ts
17
17
  const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
@@ -22,18 +22,16 @@ const INVITE_EMAIL_KEY = "__convexAuthPendingInviteEmail";
22
22
  const RETRY_BASE_MS = 500;
23
23
  const RETRY_MAX_RETRIES = 2;
24
24
  const AUTH_HANDSHAKE_TIMEOUT_MS = 5e3;
25
- const RETRY_SCHEDULE = Schedule.both(Schedule.jittered(Schedule.exponential(`${RETRY_BASE_MS} millis`)), Schedule.recurs(RETRY_MAX_RETRIES));
26
- function logClientErrorCause(message, cause) {
27
- logMessage("convex-auth/client", LOG_LEVELS.ERROR, [message, Cause.squash(cause)]);
28
- }
29
- function runPromiseBoundary(program) {
30
- return Effect.runPromiseExit(program).then(Exit.match({
31
- onSuccess: (value) => value,
32
- onFailure: (cause) => Promise.reject(Cause.squash(cause))
33
- }));
34
- }
35
- function runDetached(program, message) {
36
- Effect.runFork(program.pipe(Effect.catchCause((cause) => Effect.sync(() => logClientErrorCause(message, cause)))));
25
+ async function retryWithJitteredBackoff(fn, shouldRetry) {
26
+ let attempt = 0;
27
+ while (true) try {
28
+ return await fn();
29
+ } catch (error) {
30
+ if (attempt >= RETRY_MAX_RETRIES || !shouldRetry(error)) throw error;
31
+ const jitter = RETRY_BASE_MS * Math.pow(2, attempt) * (.5 + Math.random());
32
+ await new Promise((resolve) => setTimeout(resolve, jitter));
33
+ attempt++;
34
+ }
37
35
  }
38
36
  /**
39
37
  * Resolve the Convex deployment URL from the client.
@@ -104,7 +102,12 @@ function buildSignInRequestKey(provider, params) {
104
102
  function client(options) {
105
103
  const { convex, proxyPath, api: apiRefs } = options;
106
104
  const proxy = proxyPath;
107
- const services = resolveClientServices(Layer.mergeAll(ClientRuntimeLive(options.runtime ?? {}), ClientAdaptersLive(options.adapters ?? {}), ClientAdapterFactoriesLive(options.adapterFactories ?? {}), ClientHttpLive(proxy ? null : options.httpClient ?? null)));
105
+ const services = resolveClientServices({
106
+ runtime: ClientRuntimeLive(options.runtime ?? {}),
107
+ adapters: ClientAdaptersLive(options.adapters ?? {}),
108
+ adapterFactories: ClientAdapterFactoriesLive(options.adapterFactories ?? {}),
109
+ http: ClientHttpLive(proxy ? null : options.httpClient ?? null)
110
+ });
108
111
  const runtime = services.runtime;
109
112
  const adapters = services.adapters;
110
113
  function requireProxyRuntime() {
@@ -183,7 +186,6 @@ function client(options) {
183
186
  }
184
187
  };
185
188
  const subscribers = /* @__PURE__ */ new Set();
186
- const subscribersRef = Ref.makeUnsafe(subscribers);
187
189
  let disposeStorageListener = null;
188
190
  const httpClient = services.httpClient;
189
191
  const serverToken = typeof options.tokenSeed === "string" && options.tokenSeed.trim().length > 0 ? options.tokenSeed : null;
@@ -196,20 +198,20 @@ function client(options) {
196
198
  let destroyed = false;
197
199
  let activeSignIn = null;
198
200
  const handshakeWaiters = /* @__PURE__ */ new Set();
199
- const snapshotRef = Ref.makeUnsafe({
201
+ let snapshot = {
200
202
  phase: hasServerToken ? "authenticated" : isLoading ? "loading" : "unauthenticated",
201
203
  isLoading,
202
204
  isAuthenticated: hasServerToken,
203
205
  token
204
- });
206
+ };
205
207
  let handlingCodeFlow = false;
206
208
  const settleHandshakeWaiters = (epoch, outcome) => {
207
209
  for (const waiter of Array.from(handshakeWaiters)) {
208
210
  if (waiter.epoch !== epoch) continue;
209
211
  clearTimeout(waiter.timeoutId);
210
212
  handshakeWaiters.delete(waiter);
211
- if (outcome.type === "resolve") Effect.runSync(Deferred.succeed(waiter.deferred, void 0));
212
- else Effect.runSync(Deferred.fail(waiter.deferred, outcome.error));
213
+ if (outcome.type === "resolve") waiter.deferred.resolve(void 0);
214
+ else waiter.deferred.reject(outcome.error);
213
215
  }
214
216
  };
215
217
  const rejectObsoleteHandshakeWaiters = (activeEpoch) => {
@@ -217,10 +219,10 @@ function client(options) {
217
219
  if (waiter.epoch >= activeEpoch) continue;
218
220
  clearTimeout(waiter.timeoutId);
219
221
  handshakeWaiters.delete(waiter);
220
- Effect.runSync(Deferred.fail(waiter.deferred, createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
222
+ waiter.deferred.reject(createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
221
223
  ...waiter.context,
222
224
  reason: "token_changed"
223
- })));
225
+ }));
224
226
  }
225
227
  };
226
228
  const waitForAuthHandshake = async (context) => {
@@ -231,22 +233,22 @@ function client(options) {
231
233
  reason: "auth_rejected"
232
234
  });
233
235
  const epoch = authEpoch;
234
- const deferred = Deferred.makeUnsafe();
236
+ const deferred = createDeferred();
235
237
  const waiter = {
236
238
  epoch,
237
239
  context,
238
240
  deferred,
239
241
  timeoutId: setTimeout(() => {
240
242
  handshakeWaiters.delete(waiter);
241
- Effect.runSync(Deferred.fail(deferred, createHandshakeError("AUTH_HANDSHAKE_TIMEOUT", {
243
+ deferred.reject(createHandshakeError("AUTH_HANDSHAKE_TIMEOUT", {
242
244
  ...context,
243
245
  timeoutMs: AUTH_HANDSHAKE_TIMEOUT_MS
244
- })));
246
+ }));
245
247
  }, AUTH_HANDSHAKE_TIMEOUT_MS)
246
248
  };
247
249
  handshakeWaiters.add(waiter);
248
250
  try {
249
- await Effect.runPromise(Deferred.await(deferred));
251
+ await deferred.promise;
250
252
  } finally {
251
253
  clearTimeout(waiter.timeoutId);
252
254
  handshakeWaiters.delete(waiter);
@@ -262,24 +264,18 @@ function client(options) {
262
264
  if (updateSnapshot()) notify();
263
265
  };
264
266
  const notify = () => {
265
- for (const cb of Ref.getUnsafe(subscribersRef)) cb();
267
+ for (const cb of subscribers) cb();
266
268
  };
267
269
  const updateSnapshot = () => {
268
- const phase = {
269
- handshake: "handshake",
270
- loading: "loading",
271
- authenticated: "authenticated",
272
- unauthenticated: "unauthenticated"
273
- }[{ tag: token !== null && handshakePending ? "handshake" : isLoading ? "loading" : token !== null && authConfirmed ? "authenticated" : "unauthenticated" }.tag];
270
+ const phase = token !== null && handshakePending ? "handshake" : isLoading ? "loading" : token !== null && authConfirmed ? "authenticated" : "unauthenticated";
274
271
  const next = {
275
272
  phase,
276
273
  isLoading: phase === "loading" || phase === "handshake",
277
274
  isAuthenticated: phase === "authenticated",
278
275
  token
279
276
  };
280
- const snapshot = Ref.getUnsafe(snapshotRef);
281
277
  if (snapshot.phase === next.phase && snapshot.isLoading === next.isLoading && snapshot.isAuthenticated === next.isAuthenticated && snapshot.token === next.token) return false;
282
- Effect.runSync(Ref.set(snapshotRef, next));
278
+ snapshot = next;
283
279
  return true;
284
280
  };
285
281
  const finalizeLoadingState = () => {
@@ -357,16 +353,10 @@ function client(options) {
357
353
  };
358
354
  const passkeyAdapter = adapters.passkey ?? services.adapterFactories.passkey?.(adapterDeps);
359
355
  const verifyCode = async (args) => {
360
- return runPromiseBoundary(Effect.tryPromise({
361
- try: () => requireHttpClient().action(requireApiRefs().signIn, "code" in args ? {
362
- params: { code: args.code },
363
- verifier: args.verifier
364
- } : args),
365
- catch: (error) => error
366
- }).pipe(Effect.map((result) => result), Effect.retry({
367
- schedule: RETRY_SCHEDULE,
368
- while: isTransientNetworkError
369
- })));
356
+ return retryWithJitteredBackoff(() => requireHttpClient().action(requireApiRefs().signIn, "code" in args ? {
357
+ params: { code: args.code },
358
+ verifier: args.verifier
359
+ } : args), isTransientNetworkError);
370
360
  };
371
361
  const verifyCodeAndSetToken = async (args, opts) => {
372
362
  const result = await verifyCode(args);
@@ -430,27 +420,28 @@ function client(options) {
430
420
  })() : args ?? {};
431
421
  const flow = typeof params.flow === "string" && params.flow.length > 0 ? params.flow : "signIn";
432
422
  const signInKey = buildSignInRequestKey(provider, params);
433
- const handleSignInActionResult = async (result, options$1) => runPromiseBoundary(Match.value(result).pipe(Match.when({ kind: "redirect" }, (redirectResult) => Effect.gen(function* () {
434
- const redirectUrl = new URL(redirectResult.redirect);
435
- if (options$1.persistVerifier) yield* Effect.promise(() => storageSet(VERIFIER_STORAGE_KEY, redirectResult.verifier));
436
- if (runtime.location) yield* Effect.promise(async () => {
437
- await runtime.location.redirect(redirectUrl);
438
- });
439
- return {
440
- kind: "redirect",
441
- redirect: redirectUrl,
442
- verifier: redirectResult.verifier
423
+ const handleSignInActionResult = async (result, resultOptions) => {
424
+ if (result.kind === "redirect") {
425
+ const redirectUrl = new URL(result.redirect);
426
+ if (resultOptions.persistVerifier) await storageSet(VERIFIER_STORAGE_KEY, result.verifier);
427
+ if (runtime.location) await runtime.location.redirect(redirectUrl);
428
+ return {
429
+ kind: "redirect",
430
+ redirect: redirectUrl,
431
+ verifier: result.verifier
432
+ };
433
+ }
434
+ if (result.kind === "totpRequired") return {
435
+ kind: "totpRequired",
436
+ verifier: result.verifier
437
+ };
438
+ if (result.kind === "deviceCode") return {
439
+ kind: "deviceCode",
440
+ deviceCode: normalizeDeviceCodeResult(result.deviceCode)
443
441
  };
444
- })), Match.when({ kind: "totpRequired" }, (totpRequiredResult) => Effect.succeed({
445
- kind: "totpRequired",
446
- verifier: totpRequiredResult.verifier
447
- })), Match.when({ kind: "deviceCode" }, (deviceCodeResult) => Effect.succeed({
448
- kind: "deviceCode",
449
- deviceCode: normalizeDeviceCodeResult(deviceCodeResult.deviceCode)
450
- })), Match.when({ kind: "signedIn" }, (signedInResult) => Effect.gen(function* () {
451
- return (yield* Effect.promise(() => setTokenAndMaybeWait(options$1.shouldStore ? {
442
+ if (result.kind === "signedIn") return await setTokenAndMaybeWait(resultOptions.shouldStore ? {
452
443
  shouldStore: true,
453
- tokens: signedInResult.tokens,
444
+ tokens: result.tokens,
454
445
  waitForHandshake: true,
455
446
  context: {
456
447
  provider,
@@ -458,46 +449,41 @@ function client(options) {
458
449
  }
459
450
  } : {
460
451
  shouldStore: false,
461
- tokens: signedInResult.tokens === null ? null : { token: signedInResult.tokens.token },
452
+ tokens: result.tokens === null ? null : { token: result.tokens.token },
462
453
  waitForHandshake: true,
463
454
  context: {
464
455
  provider,
465
456
  flow
466
457
  }
467
- }))) ? { kind: "signedIn" } : { kind: "started" };
468
- })), Match.when({ kind: "started" }, () => Effect.succeed({ kind: "started" })), Match.when({ kind: "passkeyOptions" }, () => Effect.succeed({ kind: "started" })), Match.when({ kind: "totpSetup" }, () => Effect.succeed({ kind: "started" })), Match.exhaustive));
458
+ }) ? { kind: "signedIn" } : { kind: "started" };
459
+ return { kind: "started" };
460
+ };
469
461
  if (activeSignIn !== null) {
470
462
  if (activeSignIn.key === signInKey) return await activeSignIn.promise;
471
463
  throw new Error("Another sign-in flow is already in progress.");
472
464
  }
473
- const signInPromise = runPromiseBoundary(Effect.tryPromise({
474
- try: async () => {
475
- if (proxy) return await handleSignInActionResult(await proxyFetch({
476
- action: "auth:signIn",
477
- args: {
478
- provider,
479
- params
480
- }
481
- }), {
482
- shouldStore: false,
483
- persistVerifier: false
484
- });
485
- const verifier = await storageGet(VERIFIER_STORAGE_KEY) ?? void 0;
486
- await storageRemove(VERIFIER_STORAGE_KEY);
487
- return await handleSignInActionResult(await convex.action(requireApiRefs().signIn, {
465
+ const signInPromise = (async () => {
466
+ if (proxy) return await handleSignInActionResult(await proxyFetch({
467
+ action: "auth:signIn",
468
+ args: {
488
469
  provider,
489
- params,
490
- verifier
491
- }), {
492
- shouldStore: true,
493
- persistVerifier: true
494
- });
495
- },
496
- catch: (error) => error
497
- }).pipe(Effect.withSpan("convex-auth.client.signIn", { attributes: {
498
- provider: provider ?? "code",
499
- proxy: proxy !== void 0
500
- } })));
470
+ params
471
+ }
472
+ }), {
473
+ shouldStore: false,
474
+ persistVerifier: false
475
+ });
476
+ const verifier = await storageGet(VERIFIER_STORAGE_KEY) ?? void 0;
477
+ await storageRemove(VERIFIER_STORAGE_KEY);
478
+ return await handleSignInActionResult(await convex.action(requireApiRefs().signIn, {
479
+ provider,
480
+ params,
481
+ verifier
482
+ }), {
483
+ shouldStore: true,
484
+ persistVerifier: true
485
+ });
486
+ })();
501
487
  activeSignIn = {
502
488
  key: signInKey,
503
489
  promise: signInPromise
@@ -516,32 +502,28 @@ function client(options) {
516
502
  * signed-out user is a no-op.
517
503
  */
518
504
  const signOut = async () => {
519
- await runPromiseBoundary(Effect.gen(function* () {
520
- if (proxy) {
521
- yield* Effect.tryPromise({
522
- try: () => proxyFetch({
523
- action: "auth:signOut",
524
- args: {}
525
- }),
526
- catch: () => void 0
527
- }).pipe(Effect.catch(() => Effect.succeed(void 0)));
528
- yield* Effect.promise(() => setToken({
529
- shouldStore: false,
530
- tokens: null
531
- }));
532
- if (convex.clearAuth) convex.clearAuth();
533
- return;
534
- }
535
- yield* Effect.tryPromise({
536
- try: () => convex.action(requireApiRefs().signOut, {}),
537
- catch: () => void 0
538
- }).pipe(Effect.catch(() => Effect.succeed(void 0)));
539
- yield* Effect.promise(() => setToken({
540
- shouldStore: true,
505
+ if (proxy) {
506
+ try {
507
+ await proxyFetch({
508
+ action: "auth:signOut",
509
+ args: {}
510
+ });
511
+ } catch {}
512
+ await setToken({
513
+ shouldStore: false,
541
514
  tokens: null
542
- }));
515
+ });
543
516
  if (convex.clearAuth) convex.clearAuth();
544
- }).pipe(Effect.withSpan("convex-auth.client.signOut")));
517
+ return;
518
+ }
519
+ try {
520
+ await convex.action(requireApiRefs().signOut, {});
521
+ } catch {}
522
+ await setToken({
523
+ shouldStore: true,
524
+ tokens: null
525
+ });
526
+ if (convex.clearAuth) convex.clearAuth();
545
527
  };
546
528
  const fetchAccessToken = async ({ forceRefreshToken }) => {
547
529
  if (!forceRefreshToken) return token;
@@ -551,27 +533,25 @@ function client(options) {
551
533
  const tokenBeforeRefresh = token;
552
534
  return await withMutex("__convexAuthProxyRefresh", async () => {
553
535
  if (token !== tokenBeforeRefresh) return token;
554
- await runPromiseBoundary(Effect.tryPromise({
555
- try: () => proxyFetch({
536
+ try {
537
+ const result = await retryWithJitteredBackoff(() => proxyFetch({
556
538
  action: "auth:signIn",
557
539
  args: { refreshToken: true }
558
- }),
559
- catch: (error) => error
560
- }).pipe(Effect.retry({
561
- schedule: RETRY_SCHEDULE,
562
- while: isRetriableProxyRefreshError
563
- }), Effect.flatMap((result) => Effect.promise(() => isSignedInResult(result) && result.tokens ? setToken({
564
- shouldStore: false,
565
- tokens: { token: result.tokens.token },
566
- resyncConvexAuth: false
567
- }) : setToken({
568
- shouldStore: false,
569
- tokens: null,
570
- resyncConvexAuth: false
571
- }))), Effect.catch((error) => Effect.sync(() => {
540
+ }), isRetriableProxyRefreshError);
541
+ if (isSignedInResult(result) && result.tokens) await setToken({
542
+ shouldStore: false,
543
+ tokens: { token: result.tokens.token },
544
+ resyncConvexAuth: false
545
+ });
546
+ else await setToken({
547
+ shouldStore: false,
548
+ tokens: null,
549
+ resyncConvexAuth: false
550
+ });
551
+ } catch (error) {
572
552
  logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy refresh failed:", error]);
573
553
  if (token === null) finalizeLoadingState();
574
- }))).pipe(Effect.withSpan("convex-auth.client.refresh.proxy")));
554
+ }
575
555
  return token;
576
556
  });
577
557
  }
@@ -584,7 +564,7 @@ function client(options) {
584
564
  finalizeLoadingState();
585
565
  return null;
586
566
  }
587
- await runPromiseBoundary(Effect.promise(() => verifyCodeAndSetToken({ refreshToken }, { resyncConvexAuth: false })).pipe(Effect.withSpan("convex-auth.client.refresh.local")));
567
+ await verifyCodeAndSetToken({ refreshToken }, { resyncConvexAuth: false });
588
568
  return token;
589
569
  });
590
570
  };
@@ -595,18 +575,14 @@ function client(options) {
595
575
  const code = location.searchParams.get("code");
596
576
  if (!code) return;
597
577
  handlingCodeFlow = true;
598
- await runPromiseBoundary(Effect.tryPromise({
599
- try: async () => {
600
- await signIn(void 0, { code });
601
- const codeUrl = new URL(location.toString());
602
- codeUrl.searchParams.delete("code");
603
- await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
604
- },
605
- catch: () => void 0
606
- }).pipe(Effect.catch(() => Effect.succeed(void 0)), Effect.ensuring(Effect.sync(() => {
578
+ try {
579
+ await signIn(void 0, { code });
580
+ const codeUrl = new URL(location.toString());
581
+ codeUrl.searchParams.delete("code");
582
+ await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
583
+ } catch {} finally {
607
584
  handlingCodeFlow = false;
608
- }))));
609
- handlingCodeFlow = false;
585
+ }
610
586
  };
611
587
  const hydrateFromStorage = async () => {
612
588
  const storedToken = await storageGet(JWT_STORAGE_KEY) ?? null;
@@ -627,32 +603,44 @@ function client(options) {
627
603
  * @returns An unsubscribe function.
628
604
  */
629
605
  const onChange = (cb) => {
630
- cb(Ref.getUnsafe(snapshotRef));
631
- const wrapped = () => cb(Ref.getUnsafe(snapshotRef));
632
- Ref.getUnsafe(subscribersRef).add(wrapped);
606
+ cb(snapshot);
607
+ const wrapped = () => cb(snapshot);
608
+ subscribers.add(wrapped);
633
609
  return () => {
634
- Ref.getUnsafe(subscribersRef).delete(wrapped);
610
+ subscribers.delete(wrapped);
635
611
  };
636
612
  };
637
613
  if (!proxy && runtime.sync) disposeStorageListener = runtime.sync.subscribe(key(JWT_STORAGE_KEY), (value) => {
638
- runDetached(Effect.promise(() => setToken({
614
+ setToken({
639
615
  shouldStore: false,
640
616
  tokens: value === null ? null : { token: value }
641
- })), "[convex-auth] Storage event handler failed:");
617
+ }).catch((error) => {
618
+ logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Storage event handler failed:", error]);
619
+ });
642
620
  }) ?? null;
643
621
  bindConvexAuth();
644
622
  if (proxy) {
645
- if (!hasServerToken && runtime.environment !== "server") runDetached(Effect.promise(() => fetchAccessToken({ forceRefreshToken: true })), "[convex-auth] Proxy token refresh failed:");
646
- } else runDetached(Effect.promise(async () => {
647
- await hydrateFromStorage();
648
- await handleCodeFlow();
649
- }).pipe(Effect.catch(() => Effect.promise(() => setToken({
650
- shouldStore: false,
651
- tokens: null
652
- })).pipe(Effect.catch(() => Effect.succeed(void 0))))), "[convex-auth] SPA initialization failed:");
623
+ if (!hasServerToken && runtime.environment !== "server") fetchAccessToken({ forceRefreshToken: true }).catch((error) => {
624
+ logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy token refresh failed:", error]);
625
+ });
626
+ } else (async () => {
627
+ try {
628
+ await hydrateFromStorage();
629
+ await handleCodeFlow();
630
+ } catch {
631
+ try {
632
+ await setToken({
633
+ shouldStore: false,
634
+ tokens: null
635
+ });
636
+ } catch {}
637
+ }
638
+ })().catch((error) => {
639
+ logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] SPA initialization failed:", error]);
640
+ });
653
641
  return {
654
642
  get state() {
655
- return Ref.getUnsafe(snapshotRef);
643
+ return snapshot;
656
644
  },
657
645
  param,
658
646
  get invite() {
@@ -688,7 +676,7 @@ function client(options) {
688
676
  error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", { reason: "destroyed" })
689
677
  });
690
678
  disposeStorageListener?.();
691
- Ref.getUnsafe(subscribersRef).clear();
679
+ subscribers.clear();
692
680
  },
693
681
  ...passkeyAdapter ? { passkey: passkeyAdapter } : {}
694
682
  };
@@ -1,12 +1,7 @@
1
1
  import { ConvexError } from "convex/values";
2
- import { Schema } from "effect";
3
2
 
4
3
  //#region src/client/runtime/proxy.ts
5
4
  const NETWORK_ERROR_PATTERN = /(network|fetch|load failed|failed to fetch)/i;
6
- const ProxyErrorBodySchema = Schema.Struct({
7
- error: Schema.optional(Schema.String),
8
- authError: Schema.optional(Schema.Unknown)
9
- });
10
5
  /** @internal */
11
6
  function isTransientNetworkError(error) {
12
7
  return error instanceof TypeError || error instanceof Error && NETWORK_ERROR_PATTERN.test(error.message || "");
@@ -22,7 +17,12 @@ function isRetriableProxyRefreshError(error) {
22
17
  }
23
18
  /** @internal */
24
19
  function parseProxyErrorBody(value) {
25
- return Schema.decodeUnknownSync(ProxyErrorBodySchema)(value);
20
+ if (typeof value !== "object" || value === null) return {};
21
+ const obj = value;
22
+ return {
23
+ error: typeof obj.error === "string" ? obj.error : void 0,
24
+ authError: obj.authError
25
+ };
26
26
  }
27
27
 
28
28
  //#endregion
@@ -1,11 +1,7 @@
1
- import { Layer, ServiceMap } from "effect";
2
-
3
1
  //#region src/client/services/adapters.ts
4
- var ClientAdaptersService = class extends ServiceMap.Service()("ClientAdaptersService") {};
5
- var ClientAdapterFactoriesService = class extends ServiceMap.Service()("ClientAdapterFactoriesService") {};
6
- const ClientAdaptersLive = (adapters) => Layer.succeed(ClientAdaptersService)(adapters);
7
- const ClientAdapterFactoriesLive = (adapterFactories) => Layer.succeed(ClientAdapterFactoriesService)(adapterFactories);
2
+ const ClientAdaptersLive = (adapters) => adapters;
3
+ const ClientAdapterFactoriesLive = (adapterFactories) => adapterFactories;
8
4
 
9
5
  //#endregion
10
- export { ClientAdapterFactoriesLive, ClientAdapterFactoriesService, ClientAdaptersLive, ClientAdaptersService };
6
+ export { ClientAdapterFactoriesLive, ClientAdaptersLive };
11
7
  //# sourceMappingURL=adapters.js.map
@@ -1,9 +1,6 @@
1
- import { Layer, ServiceMap } from "effect";
2
-
3
1
  //#region src/client/services/http.ts
4
- var ClientHttpService = class extends ServiceMap.Service()("ClientHttpService") {};
5
- const ClientHttpLive = (httpClient) => Layer.succeed(ClientHttpService)({ httpClient });
2
+ const ClientHttpLive = (httpClient) => ({ httpClient });
6
3
 
7
4
  //#endregion
8
- export { ClientHttpLive, ClientHttpService };
5
+ export { ClientHttpLive };
9
6
  //# sourceMappingURL=http.js.map
@@ -1,16 +1,10 @@
1
- import { ClientAdapterFactoriesService, ClientAdaptersService } from "./adapters.js";
2
- import { ClientHttpService } from "./http.js";
3
- import { ClientRuntimeService } from "./runtime.js";
4
- import { Effect, Layer, ServiceMap } from "effect";
5
-
6
1
  //#region src/client/services/resolve.ts
7
- function resolveClientServices(layer) {
8
- const context = Effect.runSync(Effect.scoped(Layer.build(layer)));
2
+ function resolveClientServices(input) {
9
3
  return {
10
- runtime: ServiceMap.getUnsafe(context, ClientRuntimeService),
11
- adapters: ServiceMap.getUnsafe(context, ClientAdaptersService),
12
- adapterFactories: ServiceMap.getUnsafe(context, ClientAdapterFactoriesService),
13
- httpClient: ServiceMap.getUnsafe(context, ClientHttpService).httpClient
4
+ runtime: input.runtime,
5
+ adapters: input.adapters,
6
+ adapterFactories: input.adapterFactories,
7
+ httpClient: input.http.httpClient
14
8
  };
15
9
  }
16
10
 
@@ -1,9 +1,6 @@
1
- import { Layer, ServiceMap } from "effect";
2
-
3
1
  //#region src/client/services/runtime.ts
4
- var ClientRuntimeService = class extends ServiceMap.Service()("ClientRuntimeService") {};
5
- const ClientRuntimeLive = (runtime) => Layer.succeed(ClientRuntimeService)(runtime);
2
+ const ClientRuntimeLive = (runtime) => runtime;
6
3
 
7
4
  //#endregion
8
- export { ClientRuntimeLive, ClientRuntimeService };
5
+ export { ClientRuntimeLive };
9
6
  //# sourceMappingURL=runtime.js.map