@object-ui/auth 17.3.0 → 17.6.0

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 (49) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +184 -13
  3. package/dist/ActiveOrganizationStorage.d.ts +168 -0
  4. package/dist/ActiveOrganizationStorage.d.ts.map +1 -0
  5. package/dist/ActiveOrganizationStorage.js +515 -0
  6. package/dist/AuthContext.d.ts +34 -1
  7. package/dist/AuthContext.d.ts.map +1 -1
  8. package/dist/AuthGuard.d.ts +8 -1
  9. package/dist/AuthGuard.d.ts.map +1 -1
  10. package/dist/AuthGuard.js +16 -3
  11. package/dist/AuthProvider.d.ts +1 -1
  12. package/dist/AuthProvider.d.ts.map +1 -1
  13. package/dist/AuthProvider.js +359 -32
  14. package/dist/ForgotPasswordForm.d.ts +1 -1
  15. package/dist/ForgotPasswordForm.d.ts.map +1 -1
  16. package/dist/ForgotPasswordForm.js +5 -5
  17. package/dist/LoginForm.d.ts +1 -1
  18. package/dist/LoginForm.d.ts.map +1 -1
  19. package/dist/LoginForm.js +4 -4
  20. package/dist/PreviewBanner.js +1 -1
  21. package/dist/RegisterForm.d.ts +1 -1
  22. package/dist/RegisterForm.d.ts.map +1 -1
  23. package/dist/RegisterForm.js +3 -3
  24. package/dist/SocialSignInButtons.js +2 -2
  25. package/dist/UserMenu.js +2 -2
  26. package/dist/createAuthClient.d.ts +23 -1
  27. package/dist/createAuthClient.d.ts.map +1 -1
  28. package/dist/createAuthClient.js +112 -25
  29. package/dist/createAuthenticatedFetch.d.ts +9 -9
  30. package/dist/createAuthenticatedFetch.d.ts.map +1 -1
  31. package/dist/createAuthenticatedFetch.js +82 -37
  32. package/dist/index.d.ts +20 -19
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +28 -17
  35. package/dist/invitation-status.d.ts +59 -0
  36. package/dist/invitation-status.d.ts.map +1 -0
  37. package/dist/invitation-status.js +32 -0
  38. package/dist/types.d.ts +52 -4
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/useAuth.d.ts +1 -1
  41. package/dist/useAuth.d.ts.map +1 -1
  42. package/dist/useAuth.js +6 -1
  43. package/dist/useWorkspaceAdminStatus.d.ts +85 -0
  44. package/dist/useWorkspaceAdminStatus.d.ts.map +1 -0
  45. package/dist/useWorkspaceAdminStatus.js +102 -0
  46. package/package.json +5 -5
  47. package/dist/useIsWorkspaceAdmin.d.ts +0 -21
  48. package/dist/useIsWorkspaceAdmin.d.ts.map +0 -1
  49. package/dist/useIsWorkspaceAdmin.js +0 -47
@@ -6,11 +6,113 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  * This source code is licensed under the MIT license found in the
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
- import { useState, useEffect, useCallback, useMemo } from 'react';
10
- import { authGateEvents } from './auth-gate-events';
11
- import { AuthCtx } from './AuthContext';
12
- import { createAuthClient } from './createAuthClient';
13
- import { ActiveOrganizationStorage } from './createAuthenticatedFetch';
9
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
10
+ import { authGateEvents } from './auth-gate-events.js';
11
+ import { AuthCtx } from './AuthContext.js';
12
+ import { createAuthClient, TokenStorage } from './createAuthClient.js';
13
+ import { ActiveOrganizationStorage, SessionUserScope } from './ActiveOrganizationStorage.js';
14
+ /**
15
+ * Prefix of every `MetadataProvider` seed entry in `sessionStorage`
16
+ * (`objectui:metadata:<type>:<orgId>:<principal>`, `@object-ui/app-shell`).
17
+ *
18
+ * Spelled out here rather than imported: `@object-ui/app-shell` depends on
19
+ * `@object-ui/auth`, so the constant cannot travel in that direction without a
20
+ * dependency cycle. The two ends are held together by BEHAVIOUR instead of by
21
+ * a shared symbol — `MetadataProvider.crossPrincipalSeed.test.tsx` fills the
22
+ * cache by running the real `MetadataProvider` and empties it by signing out
23
+ * through this provider, so a prefix that drifts on either side fails there
24
+ * rather than silently purging nothing.
25
+ */
26
+ const METADATA_SEED_CACHE_PREFIX = 'objectui:metadata:';
27
+ /**
28
+ * Drop the client-side caches that belong to the session being ended
29
+ * (objectui#5198).
30
+ *
31
+ * `sessionStorage` is per-TAB, not per-session, and no sign-out call site
32
+ * reloads the page — `AppSidebar`, `AppHeader`, `UserMenu` and
33
+ * `RemediationOverlay` all just call `signOut()` and let the SPA keep running.
34
+ * So without this the next person to sign in in the same tab (a shared or
35
+ * kiosk browser, a handover, a support session) is SEEDED from the previous
36
+ * user's entry, and what is in it is that user's PERMISSION-FILTERED app list —
37
+ * the server filters `GET /api/v1/meta/:type` per session. That makes it a
38
+ * cross-principal disclosure, not ordinary staleness, which is why it is
39
+ * cleared here and not left to the tab being closed.
40
+ *
41
+ * Two properties of the loop are load-bearing:
42
+ *
43
+ * - It matches by PREFIX. The keys are org-scoped (objectui#4486), so a purge
44
+ * that recomputed the scope would depend on `ActiveOrganizationStorage`
45
+ * still holding the org those entries were written under. A prefix sweep has
46
+ * no such dependency and therefore no ordering hazard of its own.
47
+ * - It runs BEFORE `ActiveOrganizationStorage.clear()` anyway, so the ordering
48
+ * stays correct for anything scope-derived added later: clearing the org
49
+ * first would leave such a purge computing the no-org scope and deleting
50
+ * nothing while the real entries survived.
51
+ *
52
+ * `MetadataProvider` additionally keys the entry by session fingerprint, so a
53
+ * blob that escapes this purge (a tab open across the upgrade, a session ended
54
+ * by expiry rather than by this call) is unreadable rather than merely
55
+ * undeleted. The two halves are deliberately independent.
56
+ *
57
+ * ## The `try` guards the SNAPSHOT, not the walk (objectui#5777)
58
+ *
59
+ * `Object.keys(sessionStorage)` can itself throw — partitioned iframes, some
60
+ * privacy modes — and that is the reason a guard exists here at all, so it
61
+ * stays. What it must NOT do is wrap the loop: a `removeItem` that throws on
62
+ * key `n` would abort the walk, leaving every still-unvisited MATCHING key
63
+ * unswept, with the failure swallowed so the caller believes the sweep
64
+ * completed. The keys this purges are a cross-principal disclosure risk on a
65
+ * shared browser (objectui#5198), not ordinary staleness — a partial sweep
66
+ * leaves an arbitrary subset of the previous principal's permission-filtered
67
+ * app list readable to whoever signs in next in this tab. So each
68
+ * `removeItem` gets its own `try`: one uncooperative key costs exactly that
69
+ * key. Same defect class and same remedy as `sweepStore` in
70
+ * `ActiveOrganizationStorage.ts` (objectui#5763) — different file and
71
+ * different caller (sign-out, not sign-in) is why it is fixed here rather
72
+ * than there.
73
+ *
74
+ * ## Reported, not quarantined — same reason as `sweepStore`
75
+ *
76
+ * This function walks keys it does not own reads for: `MetadataProvider`
77
+ * (`@object-ui/app-shell`) is the reader of the seed cache, not this
78
+ * provider — there is no local `get()` here to guard, so there is nothing to
79
+ * quarantine the way `ActiveOrganizationStorage.clear()` quarantines a key
80
+ * (objectui#5731). What IS mirrored is the reporting channel: a key whose
81
+ * `removeItem` throws is named in a `console.warn`, so a partial sweep is
82
+ * discoverable instead of silent. The caller (`signOut`, above) cannot act on
83
+ * it either way — the session is already ending — so, like `sweepStore` on
84
+ * the sign-in path, this must not throw.
85
+ */
86
+ function purgeSignedOutClientCaches() {
87
+ if (typeof sessionStorage !== 'undefined') {
88
+ let keys;
89
+ try {
90
+ // Snapshot the keys first (`Object.keys`) — removing entries during a
91
+ // live index walk shifts the ones behind it and skips half of them.
92
+ // Same idiom as the `MarketplacePackagePage` purge loop.
93
+ keys = Object.keys(sessionStorage);
94
+ }
95
+ catch {
96
+ keys = []; /* storage unavailable */
97
+ }
98
+ const unswept = [];
99
+ for (const key of keys) {
100
+ if (!key.startsWith(METADATA_SEED_CACHE_PREFIX))
101
+ continue;
102
+ try {
103
+ sessionStorage.removeItem(key);
104
+ }
105
+ catch {
106
+ unswept.push(key);
107
+ }
108
+ }
109
+ if (unswept.length > 0) {
110
+ console.warn(`[purgeSignedOutClientCaches] could not remove ${unswept.length} key(s) from sessionStorage: ` +
111
+ `${unswept.join(', ')}. The signed-out user's metadata seed cache may still be readable under these keys.`);
112
+ }
113
+ }
114
+ ActiveOrganizationStorage.clear();
115
+ }
14
116
  /**
15
117
  * Authentication context provider.
16
118
  *
@@ -49,12 +151,113 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
49
151
  const [activeOrganization, setActiveOrganization] = useState(null);
50
152
  const [activeMember, setActiveMember] = useState(null);
51
153
  const [isOrganizationsLoading, setIsOrganizationsLoading] = useState(false);
154
+ // objectui#5619 — the two halves of "the membership answer has landed".
155
+ // Kept as POSITIVE resolution flags rather than another `isLoading`: a
156
+ // loading flag reads `false` both before the request starts and after it
157
+ // finishes, and that is precisely the ambiguity this card exists to remove.
158
+ const [organizationsResolved, setOrganizationsResolved] = useState(false);
159
+ const [activeMemberResolved, setActiveMemberResolved] = useState(false);
52
160
  // Determine if we're in preview mode
53
161
  const isPreviewMode = previewMode != null;
54
162
  // If auth is disabled or in preview mode, automatically set as authenticated
55
163
  const isAuthenticated = (enabled && !isPreviewMode)
56
164
  ? user !== null && session !== null
57
165
  : true;
166
+ // True while `loadSession` is awaiting the server. Read by the rotation
167
+ // subscription below: a rotation the server performs INSIDE our own
168
+ // `getSession` is already reflected in the answer we are about to apply, and
169
+ // re-entering on it would loop this provider against the server forever.
170
+ const sessionLoadInFlight = useRef(false);
171
+ // objectui#5750 — the org id `switchOrganization` last resolved to, tracked
172
+ // independently of React state so a switch that starts before the PREVIOUS
173
+ // switch's render has committed still compares against a value, not a stale
174
+ // closure. Kept in sync with every path that can move `activeOrganization`
175
+ // (not just `switchOrganization` itself — `refreshOrganizations`'
176
+ // single-membership repair also does), via the effect below.
177
+ const activeOrgIdRef = useRef(null);
178
+ // Bumped by every `switchOrganization` call that decides to re-resolve
179
+ // identity. Lets an EARLIER switch's still-in-flight `getSession()` notice
180
+ // a LATER switch superseded it and discard its answer instead of applying
181
+ // stale, previous-organization data over the newer one (see
182
+ // `switchOrganization`).
183
+ const sessionLoadGeneration = useRef(0);
184
+ /**
185
+ * The ONE session loader. Runs on mount (below) and on every later
186
+ * re-resolution — `refreshSession` on the context, and the rotation
187
+ * subscription — so "how the console learns who it is" has a single
188
+ * implementation rather than one per caller.
189
+ *
190
+ * `isCancelled` mirrors `refreshOrganizations` further down: an unmount or a
191
+ * superseding call must not write state, and must not clear the loading flag
192
+ * on behalf of a newer load.
193
+ */
194
+ const loadSession = useCallback(async (isCancelled) => {
195
+ sessionLoadInFlight.current = true;
196
+ try {
197
+ const result = await client.getSession();
198
+ if (isCancelled?.())
199
+ return;
200
+ if (result) {
201
+ setUser(result.user);
202
+ setSession(result.session);
203
+ }
204
+ else {
205
+ // A re-resolution that comes back empty means the session ENDED
206
+ // (revoked, expired, signed out in another tab). On mount both are
207
+ // already null, so this changes nothing there.
208
+ setUser(null);
209
+ setSession(null);
210
+ }
211
+ }
212
+ catch (err) {
213
+ if (isCancelled?.())
214
+ return;
215
+ setError(err instanceof Error ? err : new Error(String(err)));
216
+ }
217
+ finally {
218
+ sessionLoadInFlight.current = false;
219
+ if (!isCancelled?.()) {
220
+ setIsLoading(false);
221
+ }
222
+ }
223
+ }, [client]);
224
+ /**
225
+ * objectui#4467 — re-resolve identity in place. Never raises `isLoading`:
226
+ * the console stays on screen while the answer is fetched (`loadSession`
227
+ * only ever lowers the flag). Guest / preview identities are synthetic and
228
+ * have no server to ask, so this is a no-op there.
229
+ */
230
+ const refreshSession = useCallback(async () => {
231
+ if (!enabled || isPreviewMode)
232
+ return;
233
+ await loadSession();
234
+ }, [enabled, isPreviewMode, loadSession]);
235
+ /**
236
+ * objectui#4467 — an OWNERLESS session rotation re-resolves identity.
237
+ *
238
+ * `TokenStorage` notifies only when a token we already held is replaced by a
239
+ * different one (see its header). Sign-in / sign-out do not qualify: those
240
+ * callers update identity themselves. What does qualify is a rotation
241
+ * observed on the wire by code that has no idea it just changed who the user
242
+ * is — `createAuthenticatedFetch` capturing `set-auth-token` from a generic
243
+ * metadata action, which is exactly how the console starts and stops
244
+ * impersonation.
245
+ *
246
+ * This is the honest seam for the refresh. The console's action runtime
247
+ * executes `type: 'api'` actions generically; it cannot know that one
248
+ * particular endpoint was auth-relevant without hard-coding that endpoint
249
+ * into a generic runtime. The rotation IS the signal, and it is the server's
250
+ * own declaration rather than our guess about the URL.
251
+ */
252
+ useEffect(() => {
253
+ if (!enabled || isPreviewMode)
254
+ return;
255
+ return TokenStorage.subscribeRotation(() => {
256
+ if (sessionLoadInFlight.current)
257
+ return;
258
+ void loadSession();
259
+ });
260
+ }, [enabled, isPreviewMode, loadSession]);
58
261
  // Load session on mount (only if auth is enabled and not in preview mode)
59
262
  useEffect(() => {
60
263
  if (isPreviewMode) {
@@ -67,7 +270,13 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
67
270
  email: 'preview@preview.local',
68
271
  name,
69
272
  role,
70
- roles: [role],
273
+ // `positions` is the published spelling (framework ADR-0090 D3); this
274
+ // line was the LAST producer of the retired `roles` key
275
+ // (objectui#5424, maintainer ruling 2026-08-22). Emitting the position
276
+ // keeps every `positions` consumer — `AuthGuard`'s `requiredRoles`,
277
+ // the approver-identity derivations, workspace-admin leg 3 — seeing a
278
+ // preview identity shaped like a protocol-17 session.
279
+ positions: [role],
71
280
  });
72
281
  setSession({
73
282
  token: 'preview-token',
@@ -95,30 +304,9 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
95
304
  return;
96
305
  }
97
306
  let cancelled = false;
98
- async function loadSession() {
99
- try {
100
- const result = await client.getSession();
101
- if (cancelled)
102
- return;
103
- if (result) {
104
- setUser(result.user);
105
- setSession(result.session);
106
- }
107
- }
108
- catch (err) {
109
- if (cancelled)
110
- return;
111
- setError(err instanceof Error ? err : new Error(String(err)));
112
- }
113
- finally {
114
- if (!cancelled) {
115
- setIsLoading(false);
116
- }
117
- }
118
- }
119
- loadSession();
307
+ loadSession(() => cancelled);
120
308
  return () => { cancelled = true; };
121
- }, [client, enabled, isPreviewMode, previewMode]);
309
+ }, [client, enabled, isPreviewMode, previewMode, loadSession]);
122
310
  // Notify on auth state changes
123
311
  useEffect(() => {
124
312
  onAuthStateChange?.({
@@ -177,11 +365,27 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
177
365
  setUser(null);
178
366
  setSession(null);
179
367
  setError(null);
368
+ // The organization block is the previous principal's data too: the list
369
+ // is the workspaces THAT user belongs to (the switcher renders it), and
370
+ // a lingering `activeOrganization` would additionally suppress the
371
+ // re-resolution below — `refreshOrganizations` only asks the server for
372
+ // the active org `if (orgs.length > 0 && !activeOrganization)`, so the
373
+ // next sign-in in this tab would inherit the previous user's active
374
+ // workspace in context while storage had (correctly) forgotten it.
375
+ // `activeMember` follows from the effect that watches `activeOrganization`.
376
+ // Same pairing `deleteOrganization` / `leaveOrganization` already use in
377
+ // this file: drop the in-memory reference and the stored id together.
378
+ setOrganizations([]);
379
+ setActiveOrganization(null);
180
380
  }
181
381
  catch (err) {
182
382
  setError(err instanceof Error ? err : new Error(String(err)));
183
383
  }
184
384
  finally {
385
+ // Unconditional: `createAuthClient.signOut` clears `TokenStorage` BEFORE
386
+ // it rethrows a server error, so a failed call still leaves this tab
387
+ // without a session — the caches it authenticated for must go with it.
388
+ purgeSignedOutClientCaches();
185
389
  setIsLoading(false);
186
390
  }
187
391
  }, [client]);
@@ -392,6 +596,10 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
392
596
  finally {
393
597
  if (!isCancelled?.()) {
394
598
  setIsOrganizationsLoading(false);
599
+ // In `finally`, so a failed `listOrganizations` still counts as an
600
+ // answer. It is not a good answer, but leaving it unresolved would
601
+ // strand every gate that waits on it (objectui#5619).
602
+ setOrganizationsResolved(true);
395
603
  }
396
604
  }
397
605
  }, [client, enabled, isPreviewMode, activeOrganization]);
@@ -407,12 +615,25 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
407
615
  userId: 'preview-user',
408
616
  role,
409
617
  });
618
+ // Neither mode runs `refreshOrganizations` (its effect requires a real
619
+ // `user`, and the callback itself returns early), so this is the one
620
+ // place that can close BOTH halves for them (objectui#5619).
621
+ setOrganizationsResolved(true);
622
+ setActiveMemberResolved(true);
410
623
  return;
411
624
  }
412
625
  if (!activeOrganization) {
626
+ // "No active organization" is a real answer about the member row — but
627
+ // only once the org list itself has come back, which is what
628
+ // `organizationsResolved` below contributes. On mount this branch runs
629
+ // with the pipeline not yet started, and the combined flag stays false.
413
630
  setActiveMember(null);
631
+ setActiveMemberResolved(true);
414
632
  return;
415
633
  }
634
+ // A switch re-opens the question: until `getActiveMember()` answers for the
635
+ // NEW org, `activeMember` still holds the OLD org's row.
636
+ setActiveMemberResolved(false);
416
637
  try {
417
638
  const member = await client.getActiveMember();
418
639
  setActiveMember(member);
@@ -421,7 +642,42 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
421
642
  console.warn('[AuthProvider] Failed to load active member:', err);
422
643
  setActiveMember(null);
423
644
  }
645
+ finally {
646
+ setActiveMemberResolved(true);
647
+ }
424
648
  }, [client, enabled, isPreviewMode, previewMode, activeOrganization]);
649
+ /**
650
+ * objectui#5664 — the session-user-change invariant.
651
+ *
652
+ * Declared BEFORE the two effects below on purpose: effects run in the order
653
+ * their hooks were called, so this settles "whose browser is this" before
654
+ * `refreshActiveMember` and `refreshOrganizations` read or write anything
655
+ * org-scoped. `refreshOrganizations` in particular calls
656
+ * `ActiveOrganizationStorage.set()`, which resolves its key through the
657
+ * pointer this establishes.
658
+ *
659
+ * Keyed on the session user ID rather than wired into each of the five
660
+ * places that set `user` (mount / `refreshSession` / rotation, sign-in,
661
+ * sign-up, and the two provider flows). One implementation, and a sign-in
662
+ * path added later is covered without being told to be.
663
+ *
664
+ * Preview and auth-disabled mounts are excluded because their identities are
665
+ * SYNTHETIC — a fixed `preview-user` / `guest` id. Adopting one would read as
666
+ * a user change and purge a real user's state on any browser that opened a
667
+ * marketplace demo.
668
+ *
669
+ * What "purge" means, and why it is wholesale rather than key-by-key, is in
670
+ * `ActiveOrganizationStorage.ts`: it is an allowlist sweep, so the NEXT
671
+ * un-namespaced key is covered before anyone writes it.
672
+ */
673
+ useEffect(() => {
674
+ if (!enabled || isPreviewMode)
675
+ return;
676
+ const sessionUserId = user?.id;
677
+ if (!sessionUserId)
678
+ return;
679
+ SessionUserScope.adopt(sessionUserId);
680
+ }, [user?.id, enabled, isPreviewMode]);
425
681
  useEffect(() => {
426
682
  refreshActiveMember();
427
683
  }, [refreshActiveMember]);
@@ -433,8 +689,46 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
433
689
  refreshOrganizations(() => cancelled);
434
690
  return () => { cancelled = true; };
435
691
  }, [user, enabled, isPreviewMode]); // eslint-disable-line react-hooks/exhaustive-deps
692
+ // objectui#5750 — keep `activeOrgIdRef` current for every path that can
693
+ // move `activeOrganization`, not only `switchOrganization` (e.g.
694
+ // `refreshOrganizations`' single-membership repair calls
695
+ // `client.setActiveOrganization` directly). `switchOrganization` also
696
+ // writes it synchronously on its own success, below — this effect is the
697
+ // fallback for every OTHER path, and re-affirms after commit.
698
+ useEffect(() => {
699
+ activeOrgIdRef.current = activeOrganization?.id ?? null;
700
+ }, [activeOrganization]);
701
+ /**
702
+ * objectui#5750 — `switchOrganization` now DECLARES the re-resolution
703
+ * instead of relying on the objectui#4467 rotation subscription noticing
704
+ * one by accident.
705
+ *
706
+ * Why that accident cannot be trusted here: `POST /organization/set-active`
707
+ * always returns the SIGNED `token.signature` spelling in `set-auth-token`
708
+ * (better-auth's `bearer()` fires for any response that stages a session
709
+ * cookie), which differs from the UNSIGNED `session.token` spelling the
710
+ * client normally holds — so `TokenStorage.set` reads the flip as a
711
+ * rotation and the objectui#4467 subscription re-resolves identity. That
712
+ * accidental rotation is deterministic on the RAW token, not on the
713
+ * organization — so a SECOND switch whose response lands before the FIRST
714
+ * switch's follow-up `get-session` has re-armed it (re-stored the unsigned
715
+ * spelling) writes the SAME signed value already in storage. No value
716
+ * change, no notification, no re-resolution — `user.positions` is left
717
+ * answering for the organization the FIRST switch targeted, while
718
+ * `activeOrganization` already reads as the new one. Measured in #5749/#5750.
719
+ *
720
+ * The fix does not add a second call on the common path: it SUPPRESSES the
721
+ * accidental one (via `sessionLoadInFlight`, the same flag that already
722
+ * suppresses the rotation observed inside `loadSession`'s own
723
+ * `getSession()` call) and re-resolves explicitly instead, so a switch that
724
+ * changes the active organization still spends exactly one `get-session` —
725
+ * now unconditionally, so it does not depend on which spelling happened to
726
+ * already be in storage.
727
+ */
436
728
  const switchOrganization = useCallback(async (orgId) => {
437
729
  setError(null);
730
+ const previousOrgId = activeOrgIdRef.current;
731
+ sessionLoadInFlight.current = true;
438
732
  try {
439
733
  const org = await client.setActiveOrganization(orgId);
440
734
  setActiveOrganization(org);
@@ -445,13 +739,28 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
445
739
  else {
446
740
  ActiveOrganizationStorage.clear();
447
741
  }
742
+ const newOrgId = org?.id ?? null;
743
+ if (newOrgId !== previousOrgId) {
744
+ activeOrgIdRef.current = newOrgId;
745
+ // A LATER switch may already be in flight by the time this
746
+ // `getSession()` answer comes back (no debounce guards repeat
747
+ // switches — see the WorkspaceSwitcher/OrganizationLayout callers).
748
+ // Discard this answer if a newer switch has since claimed the
749
+ // generation, rather than clobber fresher, later-organization data
750
+ // with a stale, earlier one.
751
+ const generation = ++sessionLoadGeneration.current;
752
+ await loadSession(() => sessionLoadGeneration.current !== generation);
753
+ }
448
754
  }
449
755
  catch (err) {
450
756
  const authError = err instanceof Error ? err : new Error(String(err));
451
757
  setError(authError);
452
758
  throw authError;
453
759
  }
454
- }, [client]);
760
+ finally {
761
+ sessionLoadInFlight.current = false;
762
+ }
763
+ }, [client, loadSession]);
455
764
  const createOrganization = useCallback(async (data) => {
456
765
  setError(null);
457
766
  try {
@@ -515,6 +824,22 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
515
824
  // Guest mode (enabled=false) and preview mode both have hardcoded `isAuthenticated`
516
825
  // and a no-op signOut backend, so consumers should hide sign-out UIs.
517
826
  const isAuthEnabled = enabled && !isPreviewMode;
827
+ // objectui#5619 — see `AuthContext.isMembershipResolved` for the contract.
828
+ //
829
+ // Three modes, three reasons:
830
+ // - preview / auth-disabled: no pipeline runs; `refreshActiveMember`'s first
831
+ // branch closes both halves on its own effect.
832
+ // - no user: there is no membership to fetch, so the question is answered.
833
+ // Without this the flag would sit false forever on an unauthenticated
834
+ // visitor and hang any gate that waits on it. Consumers that must also
835
+ // wait for the SESSION read `isLoading` alongside this (the hook does).
836
+ // - signed in: both halves, because "no active organization" only means
837
+ // "no member row" once the org list has actually come back.
838
+ const isMembershipResolved = !isAuthEnabled
839
+ ? activeMemberResolved
840
+ : user == null
841
+ ? true
842
+ : organizationsResolved && activeMemberResolved;
518
843
  const value = useMemo(() => ({
519
844
  user,
520
845
  session,
@@ -527,6 +852,7 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
527
852
  signIn,
528
853
  signUp,
529
854
  signOut,
855
+ refreshSession,
530
856
  updateUser,
531
857
  forgotPassword,
532
858
  sendVerificationEmail,
@@ -549,6 +875,7 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
549
875
  activeOrganization,
550
876
  activeMember,
551
877
  isOrganizationsLoading,
878
+ isMembershipResolved,
552
879
  switchOrganization,
553
880
  createOrganization,
554
881
  refreshOrganizations,
@@ -568,10 +895,10 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
568
895
  listUserInvitations,
569
896
  }), [
570
897
  user, session, isAuthenticated, isAuthEnabled, isLoading, error, isPreviewMode, previewMode,
571
- signIn, signUp, signOut, updateUser, forgotPassword, sendVerificationEmail, resetPassword, changePassword, setInitialPassword, hasLocalPassword, getAuthConfig, signInWithProvider,
898
+ signIn, signUp, signOut, refreshSession, updateUser, forgotPassword, sendVerificationEmail, resetPassword, changePassword, setInitialPassword, hasLocalPassword, getAuthConfig, signInWithProvider,
572
899
  sendPhoneOtp, signInWithPhoneOtp, signInWithPhonePassword, requestPhonePasswordReset, resetPasswordWithPhoneOtp,
573
900
  remediationRequired, enrollTotp, verifyTotp,
574
- organizations, activeOrganization, activeMember, isOrganizationsLoading, switchOrganization, createOrganization, refreshOrganizations,
901
+ organizations, activeOrganization, activeMember, isOrganizationsLoading, isMembershipResolved, switchOrganization, createOrganization, refreshOrganizations,
575
902
  updateOrganization, deleteOrganization, leaveOrganization,
576
903
  getMembers, inviteMember, describeDelegableScope, removeMember, updateMemberRole,
577
904
  listInvitations, cancelInvitation, getInvitation, acceptInvitation, rejectInvitation, listUserInvitations,
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  import React from 'react';
9
- import type { AuthLinkComponentProps } from './types';
9
+ import type { AuthLinkComponentProps } from './types.js';
10
10
  /** Translatable labels for the ForgotPasswordForm */
11
11
  export interface ForgotPasswordFormLabels {
12
12
  emailLabel?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ForgotPasswordForm.d.ts","sourceRoot":"","sources":["../src/ForgotPasswordForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACtC,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,OAAO,EACP,QAAmB,EACnB,KAA6B,EAC7B,WAAyF,EACzF,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,GACZ,EAAE,uBAAuB,qBA0UzB"}
1
+ {"version":3,"file":"ForgotPasswordForm.d.ts","sourceRoot":"","sources":["../src/ForgotPasswordForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAYzD,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2FAA2F;IAC3F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACtC,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,wBAAwB;IACxB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,OAAO,EACP,QAAmB,EACnB,KAA6B,EAC7B,WAAyF,EACzF,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,GACZ,EAAE,uBAAuB,qBA0UzB"}
@@ -7,8 +7,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
9
  import { useEffect, useState } from 'react';
10
- import { useAuth } from './useAuth';
11
- import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthMailIcon, AuthSpinner, } from './authStyles';
10
+ import { useAuth } from './useAuth.js';
11
+ import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthMailIcon, AuthSpinner, } from './authStyles.js';
12
12
  const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
13
13
  const DefaultKeyIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", className: "h-6 w-6", "aria-hidden": "true", children: _jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }) }));
14
14
  /**
@@ -72,7 +72,7 @@ export function ForgotPasswordForm({ onSuccess, onError, loginUrl = '/login', ti
72
72
  submittingButton: labels.submittingButton ?? 'Sending…',
73
73
  successTitle: labels.successTitle ?? 'Check your email',
74
74
  successDescription: labels.successDescription ??
75
- "We've sent a password reset link to {{email}}. Please check your inbox.",
75
+ "We've sent a password reset link to {email}. Please check your inbox.",
76
76
  backToSignInText: labels.backToSignInText ?? 'Back to sign in',
77
77
  rememberPasswordText: labels.rememberPasswordText ?? 'Remember your password?',
78
78
  signInText: labels.signInText ?? 'Sign in',
@@ -156,8 +156,8 @@ export function ForgotPasswordForm({ onSuccess, onError, loginUrl = '/login', ti
156
156
  return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-7 sm:w-[400px]", children: [_jsx(AuthFormHeader, { icon: _jsx("span", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-emerald-500/10 text-emerald-600 ring-1 ring-emerald-500/15 dark:text-emerald-400", children: _jsx(AuthMailIcon, {}) }), title: l.phoneSuccessTitle, description: l.phoneSuccessDescription }), loginUrl && (_jsx("p", { className: "text-center text-sm text-muted-foreground", children: _jsxs(LinkComp, { href: loginUrl, className: AUTH_LINK_CLASS, children: ["\u2190 ", l.backToSignInText] }) }))] }));
157
157
  }
158
158
  if (submitted) {
159
- const successMsg = l.successDescription.includes('{{email}}')
160
- ? l.successDescription.replace('{{email}}', email)
159
+ const successMsg = l.successDescription.includes('{email}')
160
+ ? l.successDescription.replace('{email}', email)
161
161
  : `${l.successDescription} ${email}`;
162
162
  return (_jsxs("div", { className: "mx-auto flex w-full flex-col justify-center space-y-7 sm:w-[400px]", children: [_jsx(AuthFormHeader, { icon: _jsx("span", { className: "flex h-12 w-12 items-center justify-center rounded-2xl bg-emerald-500/10 text-emerald-600 ring-1 ring-emerald-500/15 dark:text-emerald-400", children: _jsx(AuthMailIcon, {}) }), title: l.successTitle, description: successMsg }), loginUrl && (_jsx("p", { className: "text-center text-sm text-muted-foreground", children: _jsxs(LinkComp, { href: loginUrl, className: AUTH_LINK_CLASS, children: ["\u2190 ", l.backToSignInText] }) }))] }));
163
163
  }
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  import React from 'react';
9
- import type { AuthLinkComponentProps } from './types';
9
+ import type { AuthLinkComponentProps } from './types.js';
10
10
  /** Translatable labels for the LoginForm */
11
11
  export interface LoginFormLabels {
12
12
  emailLabel?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYtD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8GAA8G;IAC9G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8FAA8F;IAC9F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yHAAyH;IACzH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,OAAO,EACP,WAAyB,EACzB,iBAAsC,EACtC,KAAiC,EACjC,WAAyD,EACzD,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,EACX,aAAa,GACd,EAAE,cAAc,qBAgbhB"}
1
+ {"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAYzD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8GAA8G;IAC9G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8FAA8F;IAC9F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yHAAyH;IACzH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,OAAO,EACP,WAAyB,EACzB,iBAAsC,EACtC,KAAiC,EACjC,WAAyD,EACzD,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,EACX,aAAa,GACd,EAAE,cAAc,qBAgbhB"}
package/dist/LoginForm.js CHANGED
@@ -7,10 +7,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
9
  import { useEffect, useState } from 'react';
10
- import { useAuth } from './useAuth';
11
- import { SocialSignInButtons } from './SocialSignInButtons';
12
- import { looksLikePhoneIdentifier, normalizePhoneIdentifier } from './phone-identifier';
13
- import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthSpinner, } from './authStyles';
10
+ import { useAuth } from './useAuth.js';
11
+ import { SocialSignInButtons } from './SocialSignInButtons.js';
12
+ import { looksLikePhoneIdentifier, normalizePhoneIdentifier } from './phone-identifier.js';
13
+ import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthSpinner, } from './authStyles.js';
14
14
  const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
15
15
  const DefaultLockIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", className: "h-6 w-6", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }));
16
16
  /**
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useAuth } from './useAuth';
2
+ import { useAuth } from './useAuth.js';
3
3
  /**
4
4
  * Banner component that displays a message when the app is in preview mode.
5
5
  * Only renders when preview mode is active. Uses the bannerMessage from
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  import React from 'react';
9
- import type { AuthLinkComponentProps } from './types';
9
+ import type { AuthLinkComponentProps } from './types.js';
10
10
  /** Translatable labels for the RegisterForm */
11
11
  export interface RegisterFormLabels {
12
12
  nameLabel?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"RegisterForm.d.ts","sourceRoot":"","sources":["../src/RegisterForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAWtD,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC;sFACkF;IAClF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;2CAEuC;IACvC,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,sBAAsB,EACtB,OAAO,EACP,QAAmB,EACnB,KAA2B,EAC3B,WAAqD,EACrD,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,EACX,aAAa,GACd,EAAE,iBAAiB,qBAuKnB"}
1
+ {"version":3,"file":"RegisterForm.d.ts","sourceRoot":"","sources":["../src/RegisterForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAWzD,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC;sFACkF;IAClF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;2CAEuC;IACvC,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC5D,uCAAuC;IACvC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,sBAAsB,EACtB,OAAO,EACP,QAAmB,EACnB,KAA2B,EAC3B,WAAqD,EACrD,IAAI,EACJ,QAAgB,EAChB,aAAa,EAAE,QAAsB,EACrC,MAAW,EACX,aAAa,GACd,EAAE,iBAAiB,qBAuKnB"}
@@ -7,9 +7,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
9
  import { useState } from 'react';
10
- import { useAuth } from './useAuth';
11
- import { SocialSignInButtons } from './SocialSignInButtons';
12
- import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthSpinner, } from './authStyles';
10
+ import { useAuth } from './useAuth.js';
11
+ import { SocialSignInButtons } from './SocialSignInButtons.js';
12
+ import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthSpinner, } from './authStyles.js';
13
13
  const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
14
14
  const DefaultUserPlusIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", className: "h-6 w-6", "aria-hidden": "true", children: [_jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }), _jsx("circle", { cx: "9", cy: "7", r: "4" }), _jsx("line", { x1: "19", y1: "8", x2: "19", y2: "14" }), _jsx("line", { x1: "22", y1: "11", x2: "16", y2: "11" })] }));
15
15
  /**