@oxy.so/services 11.4.0 → 11.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 (215) hide show
  1. package/lib/commonjs/index.js +0 -28
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/boot/runProviderColdBoot.js +47 -3
  4. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  5. package/lib/commonjs/ui/components/organizations/OrganizationClaimReview.js +2 -2
  6. package/lib/commonjs/ui/components/organizations/OrganizationClaimReview.js.map +1 -1
  7. package/lib/commonjs/ui/components/organizations/OrganizationDirectorySearch.js +5 -5
  8. package/lib/commonjs/ui/components/organizations/OrganizationDirectorySearch.js.map +1 -1
  9. package/lib/commonjs/ui/components/organizations/OrganizationSettings.js +4 -4
  10. package/lib/commonjs/ui/components/organizations/OrganizationSettings.js.map +1 -1
  11. package/lib/commonjs/ui/components/organizations/OrganizationTypePicker.js +2 -2
  12. package/lib/commonjs/ui/components/organizations/OrganizationTypePicker.js.map +1 -1
  13. package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js +3 -3
  14. package/lib/commonjs/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  15. package/lib/commonjs/ui/components/signIn/OxyEmailAccountChoice.js +113 -0
  16. package/lib/commonjs/ui/components/signIn/OxyEmailAccountChoice.js.map +1 -0
  17. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js +6 -2
  18. package/lib/commonjs/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  19. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js +4 -4
  20. package/lib/commonjs/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  21. package/lib/commonjs/ui/components/signIn/OxySignInPanel.js +76 -9
  22. package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
  23. package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js +6 -6
  24. package/lib/commonjs/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  25. package/lib/commonjs/ui/components/signIn/ReauthStep.js +4 -4
  26. package/lib/commonjs/ui/components/signIn/ReauthStep.js.map +1 -1
  27. package/lib/commonjs/ui/components/signIn/accountFlowParts.js +24 -22
  28. package/lib/commonjs/ui/components/signIn/accountFlowParts.js.map +1 -1
  29. package/lib/commonjs/ui/context/OxyContext.js +7 -4
  30. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  31. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +5 -2
  32. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  33. package/lib/commonjs/ui/oauth/applicationSessionLane.js +2 -2
  34. package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountMembersScreen.js +20 -63
  36. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +3 -3
  38. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/CreateAccountScreen.js +12 -12
  40. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/PreferencesScreen.js +3 -4
  42. package/lib/commonjs/ui/screens/PreferencesScreen.js.map +1 -1
  43. package/lib/commonjs/ui/utils/identityCrypto.js +19 -0
  44. package/lib/commonjs/ui/utils/identityCrypto.js.map +1 -0
  45. package/lib/module/index.js +0 -4
  46. package/lib/module/index.js.map +1 -1
  47. package/lib/module/ui/boot/runProviderColdBoot.js +47 -3
  48. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  49. package/lib/module/ui/components/organizations/OrganizationClaimReview.js +2 -2
  50. package/lib/module/ui/components/organizations/OrganizationClaimReview.js.map +1 -1
  51. package/lib/module/ui/components/organizations/OrganizationDirectorySearch.js +5 -5
  52. package/lib/module/ui/components/organizations/OrganizationDirectorySearch.js.map +1 -1
  53. package/lib/module/ui/components/organizations/OrganizationSettings.js +4 -4
  54. package/lib/module/ui/components/organizations/OrganizationSettings.js.map +1 -1
  55. package/lib/module/ui/components/organizations/OrganizationTypePicker.js +1 -1
  56. package/lib/module/ui/components/organizations/OrganizationTypePicker.js.map +1 -1
  57. package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js +1 -1
  58. package/lib/module/ui/components/signIn/OxyAuthenticatorPanel.js.map +1 -1
  59. package/lib/module/ui/components/signIn/OxyEmailAccountChoice.js +110 -0
  60. package/lib/module/ui/components/signIn/OxyEmailAccountChoice.js.map +1 -0
  61. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js +5 -1
  62. package/lib/module/ui/components/signIn/OxyLinkCommonsPanel.js.map +1 -1
  63. package/lib/module/ui/components/signIn/OxyPasswordPanel.js +1 -1
  64. package/lib/module/ui/components/signIn/OxyPasswordPanel.js.map +1 -1
  65. package/lib/module/ui/components/signIn/OxySignInPanel.js +71 -4
  66. package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
  67. package/lib/module/ui/components/signIn/OxySignUpPanel.js +5 -5
  68. package/lib/module/ui/components/signIn/OxySignUpPanel.js.map +1 -1
  69. package/lib/module/ui/components/signIn/ReauthStep.js +1 -1
  70. package/lib/module/ui/components/signIn/ReauthStep.js.map +1 -1
  71. package/lib/module/ui/components/signIn/accountFlowParts.js +3 -1
  72. package/lib/module/ui/components/signIn/accountFlowParts.js.map +1 -1
  73. package/lib/module/ui/context/OxyContext.js +6 -3
  74. package/lib/module/ui/context/OxyContext.js.map +1 -1
  75. package/lib/module/ui/context/hooks/useAuthOperations.js +4 -1
  76. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  77. package/lib/module/ui/oauth/applicationSessionLane.js +2 -2
  78. package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -1
  79. package/lib/module/ui/screens/AccountMembersScreen.js +20 -63
  80. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  81. package/lib/module/ui/screens/AccountSettingsScreen.js +2 -2
  82. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  83. package/lib/module/ui/screens/CreateAccountScreen.js +6 -6
  84. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  85. package/lib/module/ui/screens/PreferencesScreen.js +3 -4
  86. package/lib/module/ui/screens/PreferencesScreen.js.map +1 -1
  87. package/lib/module/ui/utils/identityCrypto.js +13 -0
  88. package/lib/module/ui/utils/identityCrypto.js.map +1 -0
  89. package/lib/typescript/commonjs/index.d.ts +0 -4
  90. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/ui/components/organizations/OrganizationClaimReview.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/ui/components/organizations/OrganizationDirectorySearch.d.ts +1 -1
  94. package/lib/typescript/commonjs/ui/components/organizations/OrganizationDirectorySearch.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/ui/components/organizations/OrganizationSettings.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/ui/components/organizations/OrganizationTypePicker.d.ts +1 -1
  97. package/lib/typescript/commonjs/ui/components/organizations/OrganizationTypePicker.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/components/signIn/OxyEmailAccountChoice.d.ts +20 -0
  100. package/lib/typescript/commonjs/ui/components/signIn/OxyEmailAccountChoice.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts +2 -0
  103. package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts +1 -1
  106. package/lib/typescript/commonjs/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts +1 -1
  108. package/lib/typescript/commonjs/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
  112. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts +1 -2
  114. package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts +1 -1
  116. package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/screens/PreferencesScreen.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/ui/utils/identityCrypto.d.ts +43 -0
  121. package/lib/typescript/commonjs/ui/utils/identityCrypto.d.ts.map +1 -0
  122. package/lib/typescript/module/index.d.ts +0 -4
  123. package/lib/typescript/module/index.d.ts.map +1 -1
  124. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  125. package/lib/typescript/module/ui/components/organizations/OrganizationClaimReview.d.ts.map +1 -1
  126. package/lib/typescript/module/ui/components/organizations/OrganizationDirectorySearch.d.ts +1 -1
  127. package/lib/typescript/module/ui/components/organizations/OrganizationDirectorySearch.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/components/organizations/OrganizationSettings.d.ts.map +1 -1
  129. package/lib/typescript/module/ui/components/organizations/OrganizationTypePicker.d.ts +1 -1
  130. package/lib/typescript/module/ui/components/organizations/OrganizationTypePicker.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/components/signIn/OxyAuthenticatorPanel.d.ts.map +1 -1
  132. package/lib/typescript/module/ui/components/signIn/OxyEmailAccountChoice.d.ts +20 -0
  133. package/lib/typescript/module/ui/components/signIn/OxyEmailAccountChoice.d.ts.map +1 -0
  134. package/lib/typescript/module/ui/components/signIn/OxyLinkCommonsPanel.d.ts.map +1 -1
  135. package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts +2 -0
  136. package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
  137. package/lib/typescript/module/ui/components/signIn/OxySignUpPanel.d.ts.map +1 -1
  138. package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts +1 -1
  139. package/lib/typescript/module/ui/components/signIn/ReauthStep.d.ts.map +1 -1
  140. package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts +1 -1
  141. package/lib/typescript/module/ui/components/signIn/accountFlowParts.d.ts.map +1 -1
  142. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  143. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  144. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
  145. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  146. package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts +1 -2
  147. package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts +1 -1
  149. package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  151. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/screens/PreferencesScreen.d.ts.map +1 -1
  153. package/lib/typescript/module/ui/utils/identityCrypto.d.ts +43 -0
  154. package/lib/typescript/module/ui/utils/identityCrypto.d.ts.map +1 -0
  155. package/package.json +6 -7
  156. package/src/index.ts +0 -5
  157. package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +166 -3
  158. package/src/ui/boot/runProviderColdBoot.ts +49 -4
  159. package/src/ui/components/organizations/OrganizationClaimReview.tsx +3 -5
  160. package/src/ui/components/organizations/OrganizationDirectorySearch.tsx +12 -9
  161. package/src/ui/components/organizations/OrganizationSettings.tsx +6 -9
  162. package/src/ui/components/organizations/OrganizationTypePicker.tsx +2 -1
  163. package/src/ui/components/signIn/OxyAuthenticatorPanel.tsx +2 -1
  164. package/src/ui/components/signIn/OxyEmailAccountChoice.tsx +116 -0
  165. package/src/ui/components/signIn/OxyLinkCommonsPanel.tsx +3 -1
  166. package/src/ui/components/signIn/OxyPasswordPanel.tsx +1 -1
  167. package/src/ui/components/signIn/OxySignInPanel.tsx +93 -6
  168. package/src/ui/components/signIn/OxySignUpPanel.tsx +6 -9
  169. package/src/ui/components/signIn/ReauthStep.tsx +2 -6
  170. package/src/ui/components/signIn/accountFlowParts.tsx +4 -2
  171. package/src/ui/context/OxyContext.tsx +7 -3
  172. package/src/ui/context/hooks/useAuthOperations.ts +2 -1
  173. package/src/ui/oauth/applicationSessionLane.ts +2 -2
  174. package/src/ui/screens/AccountMembersScreen.tsx +27 -71
  175. package/src/ui/screens/AccountSettingsScreen.tsx +6 -2
  176. package/src/ui/screens/CreateAccountScreen.tsx +10 -9
  177. package/src/ui/screens/PreferencesScreen.tsx +0 -2
  178. package/src/ui/utils/identityCrypto.ts +10 -0
  179. package/lib/commonjs/ui/components/AccountInvitationAcceptance.js +0 -97
  180. package/lib/commonjs/ui/components/AccountInvitationAcceptance.js.map +0 -1
  181. package/lib/commonjs/ui/components/DisplayCurrencySelector.js +0 -49
  182. package/lib/commonjs/ui/components/DisplayCurrencySelector.js.map +0 -1
  183. package/lib/commonjs/ui/hooks/useDisplayCurrency.js +0 -77
  184. package/lib/commonjs/ui/hooks/useDisplayCurrency.js.map +0 -1
  185. package/lib/commonjs/ui/hooks/useDisplayPrice.js +0 -59
  186. package/lib/commonjs/ui/hooks/useDisplayPrice.js.map +0 -1
  187. package/lib/module/ui/components/AccountInvitationAcceptance.js +0 -94
  188. package/lib/module/ui/components/AccountInvitationAcceptance.js.map +0 -1
  189. package/lib/module/ui/components/DisplayCurrencySelector.js +0 -44
  190. package/lib/module/ui/components/DisplayCurrencySelector.js.map +0 -1
  191. package/lib/module/ui/hooks/useDisplayCurrency.js +0 -72
  192. package/lib/module/ui/hooks/useDisplayCurrency.js.map +0 -1
  193. package/lib/module/ui/hooks/useDisplayPrice.js +0 -56
  194. package/lib/module/ui/hooks/useDisplayPrice.js.map +0 -1
  195. package/lib/typescript/commonjs/ui/components/AccountInvitationAcceptance.d.ts +0 -5
  196. package/lib/typescript/commonjs/ui/components/AccountInvitationAcceptance.d.ts.map +0 -1
  197. package/lib/typescript/commonjs/ui/components/DisplayCurrencySelector.d.ts +0 -3
  198. package/lib/typescript/commonjs/ui/components/DisplayCurrencySelector.d.ts.map +0 -1
  199. package/lib/typescript/commonjs/ui/hooks/useDisplayCurrency.d.ts +0 -11
  200. package/lib/typescript/commonjs/ui/hooks/useDisplayCurrency.d.ts.map +0 -1
  201. package/lib/typescript/commonjs/ui/hooks/useDisplayPrice.d.ts +0 -6
  202. package/lib/typescript/commonjs/ui/hooks/useDisplayPrice.d.ts.map +0 -1
  203. package/lib/typescript/module/ui/components/AccountInvitationAcceptance.d.ts +0 -5
  204. package/lib/typescript/module/ui/components/AccountInvitationAcceptance.d.ts.map +0 -1
  205. package/lib/typescript/module/ui/components/DisplayCurrencySelector.d.ts +0 -3
  206. package/lib/typescript/module/ui/components/DisplayCurrencySelector.d.ts.map +0 -1
  207. package/lib/typescript/module/ui/hooks/useDisplayCurrency.d.ts +0 -11
  208. package/lib/typescript/module/ui/hooks/useDisplayCurrency.d.ts.map +0 -1
  209. package/lib/typescript/module/ui/hooks/useDisplayPrice.d.ts +0 -6
  210. package/lib/typescript/module/ui/hooks/useDisplayPrice.d.ts.map +0 -1
  211. package/src/ui/components/AccountInvitationAcceptance.tsx +0 -90
  212. package/src/ui/components/DisplayCurrencySelector.tsx +0 -38
  213. package/src/ui/components/__tests__/AccountInvitationAcceptance.test.tsx +0 -61
  214. package/src/ui/hooks/useDisplayCurrency.ts +0 -64
  215. package/src/ui/hooks/useDisplayPrice.ts +0 -40
@@ -63,6 +63,22 @@ function makeEmptyAuthStore(): AuthStateStore {
63
63
  } as unknown as AuthStateStore;
64
64
  }
65
65
 
66
+ /** An auth store holding this origin's device credential. */
67
+ function makeCredentialAuthStore(): AuthStateStore {
68
+ return {
69
+ load: jest.fn(
70
+ async (): Promise<PersistedAuthState | null> => ({ deviceId: 'd1', deviceSecret: 's1' }),
71
+ ),
72
+ save: jest.fn(async () => undefined),
73
+ clear: jest.fn(async () => undefined),
74
+ } as unknown as AuthStateStore;
75
+ }
76
+
77
+ /** Put an OAuth return (`?code=`) on the address bar, as the IdP's redirect does. */
78
+ function landWithAuthorizationCode(): void {
79
+ window.history.replaceState(null, '', '/?code=auth-code&state=auth-state');
80
+ }
81
+
66
82
  interface Harness {
67
83
  options: RunProviderColdBootOptions;
68
84
  commitSession: jest.Mock;
@@ -120,6 +136,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
120
136
  mockTryCompleteOAuthReturn.mockClear();
121
137
  mockTryCompleteOAuthReturn.mockImplementation(async () => false);
122
138
  globalThis.sessionStorage?.clear();
139
+ window.history.replaceState(null, '', '/');
123
140
  });
124
141
 
125
142
  it('resolves a domain with no local credential SIGNED OUT instead of bouncing to the IdP', async () => {
@@ -160,7 +177,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
160
177
  return { kind: 'session', via: session.via, session } as const;
161
178
  },
162
179
  );
163
- const { options, commitSession } = makeHarness();
180
+ const { options, commitSession } = makeHarness({ authStore: makeCredentialAuthStore() });
164
181
 
165
182
  await runProviderColdBoot(options);
166
183
 
@@ -176,6 +193,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
176
193
  // A popup-mode app lands here whenever the browser blocked the popup and
177
194
  // `startWebOAuthSignIn` fell back to a full-page redirect.
178
195
  mockTryCompleteOAuthReturn.mockImplementation(async () => true);
196
+ landWithAuthorizationCode();
179
197
  const { options, markAuthResolved, setTokenReady } = makeHarness();
180
198
 
181
199
  await runProviderColdBoot(options);
@@ -190,6 +208,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
190
208
 
191
209
  it('runs the return leg even on a boot that finds no session', async () => {
192
210
  stubSignedOutBoot();
211
+ landWithAuthorizationCode();
193
212
  const { options } = makeHarness();
194
213
 
195
214
  await runProviderColdBoot(options);
@@ -199,6 +218,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
199
218
 
200
219
  it('commits the OAuth return as a deliberate sign-in', async () => {
201
220
  mockTryCompleteOAuthReturn.mockImplementation(async () => true);
221
+ landWithAuthorizationCode();
202
222
  const { options } = makeHarness();
203
223
 
204
224
  await runProviderColdBoot(options);
@@ -225,14 +245,22 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
225
245
  });
226
246
  });
227
247
 
248
+ // Each case lands with an OAuth return on the URL — the boot that must classify
249
+ // before it does anything — so "nothing ran first" is a real claim. The web
250
+ // origin with nothing to restore, which resolves before classification, is
251
+ // covered on its own below.
228
252
  describe('registered cold-boot classification', () => {
229
253
  beforeEach(() => {
230
254
  mockRunSessionColdBoot.mockReset();
231
255
  mockTryCompleteOAuthReturn.mockReset();
232
256
  mockTryCompleteOAuthReturn.mockResolvedValue(false);
257
+ landWithAuthorizationCode();
258
+ });
259
+ afterEach(() => {
260
+ window.history.replaceState(null, '', '/');
233
261
  });
234
262
  it('external classification bypasses prior secrets, shared device and identity pin work', async () => {
235
- const { options } = makeHarness();
263
+ const { options } = makeHarness({ authStore: makeCredentialAuthStore() });
236
264
  (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({
237
265
  id: 'external',
238
266
  name: 'External',
@@ -298,8 +326,9 @@ describe('registered cold-boot classification', () => {
298
326
 
299
327
  it('an unresponsive registry resolves signed-out without a late privileged continuation', async () => {
300
328
  jest.useFakeTimers();
329
+ landWithAuthorizationCode();
301
330
  try {
302
- const { options } = makeHarness();
331
+ const { options } = makeHarness({ authStore: makeCredentialAuthStore() });
303
332
  let release!: (app: unknown) => void;
304
333
  (options.oxyServices.apps.getPublic as jest.Mock).mockImplementation(
305
334
  () =>
@@ -327,5 +356,139 @@ it('an unresponsive registry resolves signed-out without a late privileged conti
327
356
  expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
328
357
  } finally {
329
358
  jest.useRealTimers();
359
+ window.history.replaceState(null, '', '/');
330
360
  }
331
361
  });
362
+
363
+ /**
364
+ * A web origin with nothing to restore — no warm token, no device secret, no
365
+ * OAuth return on the URL — ends signed out on either lane, so its routing does
366
+ * not wait on the registry round trip. Everything that would USE a credential
367
+ * still waits for the verdict.
368
+ */
369
+ describe('web origin with nothing to restore', () => {
370
+ const official = {
371
+ id: 'registered-app',
372
+ name: 'Registered App',
373
+ type: 'first_party',
374
+ isOfficial: true,
375
+ isInternal: false,
376
+ scopes: [],
377
+ };
378
+
379
+ beforeEach(() => {
380
+ mockRunSessionColdBoot.mockReset();
381
+ mockTryCompleteOAuthReturn.mockReset();
382
+ mockTryCompleteOAuthReturn.mockResolvedValue(false);
383
+ window.history.replaceState(null, '', '/');
384
+ });
385
+
386
+ function pendingRegistry(options: RunProviderColdBootOptions): (app: unknown) => void {
387
+ let release!: (app: unknown) => void;
388
+ (options.oxyServices.apps.getPublic as jest.Mock).mockImplementation(
389
+ () =>
390
+ new Promise((resolve) => {
391
+ release = resolve;
392
+ }),
393
+ );
394
+ return (app) => release(app);
395
+ }
396
+
397
+ it('resolves auth before the registry answers, and still classifies with no cache', async () => {
398
+ const { options, markAuthResolved } = makeHarness();
399
+ const onClassified = jest.fn();
400
+ const release = pendingRegistry(options);
401
+
402
+ const boot = runProviderColdBoot({ ...options, onClassified });
403
+ await new Promise((resolve) => setTimeout(resolve, 0));
404
+
405
+ expect(markAuthResolved).toHaveBeenCalled();
406
+ expect(options.oxyServices.apps.getPublic).toHaveBeenCalledWith(CLIENT_ID, { cache: false });
407
+ expect(onClassified).not.toHaveBeenCalled();
408
+
409
+ release(official);
410
+ await boot;
411
+
412
+ expect(onClassified).toHaveBeenCalledWith('device');
413
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
414
+ expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
415
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
416
+ expect(options.commitSession).not.toHaveBeenCalled();
417
+ });
418
+
419
+ it('a failed classification after the early resolution restores nothing', async () => {
420
+ const { options, markAuthResolved } = makeHarness();
421
+ (options.oxyServices.apps.getPublic as jest.Mock).mockRejectedValue(new Error('offline'));
422
+
423
+ await runProviderColdBoot(options);
424
+
425
+ expect(markAuthResolved).toHaveBeenCalled();
426
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
427
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
428
+ });
429
+
430
+ it('an origin holding a device secret still waits for the verdict before using it', async () => {
431
+ stubSignedOutBoot();
432
+ const { options, markAuthResolved } = makeHarness({ authStore: makeCredentialAuthStore() });
433
+ const release = pendingRegistry(options);
434
+
435
+ const boot = runProviderColdBoot(options);
436
+ await new Promise((resolve) => setTimeout(resolve, 0));
437
+
438
+ expect(markAuthResolved).not.toHaveBeenCalled();
439
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
440
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
441
+
442
+ release(official);
443
+ await boot;
444
+
445
+ expect(mockRunSessionColdBoot).toHaveBeenCalledTimes(1);
446
+ expect(markAuthResolved).toHaveBeenCalled();
447
+ });
448
+
449
+ it('an origin holding a device secret classified external never uses it', async () => {
450
+ const { options } = makeHarness({ authStore: makeCredentialAuthStore() });
451
+ (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({
452
+ ...official,
453
+ type: 'third_party',
454
+ isOfficial: false,
455
+ });
456
+
457
+ await runProviderColdBoot(options);
458
+
459
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
460
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
461
+ expect(options.authStore.save).not.toHaveBeenCalled();
462
+ expect(options.authStore.clear).not.toHaveBeenCalled();
463
+ });
464
+
465
+ it('an OAuth return on the URL waits for the verdict', async () => {
466
+ landWithAuthorizationCode();
467
+ const { options, markAuthResolved } = makeHarness();
468
+ const release = pendingRegistry(options);
469
+
470
+ const boot = runProviderColdBoot(options);
471
+ await new Promise((resolve) => setTimeout(resolve, 0));
472
+ expect(markAuthResolved).not.toHaveBeenCalled();
473
+ expect(options.authStore.load).not.toHaveBeenCalled();
474
+
475
+ release(official);
476
+ await boot;
477
+ expect(mockTryCompleteOAuthReturn).toHaveBeenCalledTimes(1);
478
+ window.history.replaceState(null, '', '/');
479
+ });
480
+
481
+ it('identity mode always waits for the verdict', async () => {
482
+ stubSignedOutBoot();
483
+ const { options, markAuthResolved } = makeHarness({ sessionMode: 'identity' });
484
+ const release = pendingRegistry(options);
485
+
486
+ const boot = runProviderColdBoot(options);
487
+ await new Promise((resolve) => setTimeout(resolve, 0));
488
+ expect(markAuthResolved).not.toHaveBeenCalled();
489
+
490
+ release(official);
491
+ await boot;
492
+ expect(markAuthResolved).toHaveBeenCalled();
493
+ });
494
+ });
@@ -14,6 +14,28 @@ import { isWebBrowser } from '../utils/isWebBrowser';
14
14
  import { isNetConnectivityExplicitlyOffline } from '../utils/netConnectivity';
15
15
  import type { CommitInput } from '../context/oxyContextTypes';
16
16
 
17
+ /**
18
+ * Whether the URL carries an OAuth return (`?code=` or `?error=`) for
19
+ * `tryCompleteOAuthReturn` to consume. Reads the address bar only.
20
+ */
21
+ function hasOAuthReturnOnUrl(): boolean {
22
+ const search = (globalThis as { location?: { search?: string } }).location?.search;
23
+ if (!search) return false;
24
+ const params = new URLSearchParams(search);
25
+ return params.has('code') || params.has('error');
26
+ }
27
+
28
+ /**
29
+ * Whether this origin's own store holds anything a web boot could restore from:
30
+ * a warm access token to plant or a device secret to mint with. A presence
31
+ * check only — nothing read here is used, sent or synced before the
32
+ * classification allows it.
33
+ */
34
+ async function hasRestorableState(store: AuthStateStore): Promise<boolean> {
35
+ const persisted = await store.load();
36
+ return Boolean(persisted?.accessToken || persisted?.deviceSecret);
37
+ }
38
+
17
39
  /** How long the cold boot waits for the post-boot SessionClient handoff (ms). */
18
40
  export const SESSION_HANDOFF_DEADLINE_MS = 6000;
19
41
 
@@ -139,10 +161,33 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
139
161
  setTokenReady(false);
140
162
 
141
163
  try {
142
- // Classification precedes every local-secret, shared-credential and identity
143
- // probe. Unavailable registry metadata fails closed, including offline boot.
144
- const lane = await (opts.resolveSessionLane?.() ??
145
- resolveApplicationSessionLane(oxyServices, clientId));
164
+ // Classification precedes every USE of a local secret, the shared credential
165
+ // and the identity key. Unavailable registry metadata fails closed, including
166
+ // offline boot. It starts now, before anything else, so its round trip
167
+ // overlaps the local work below.
168
+ const classification = (async () =>
169
+ opts.resolveSessionLane?.() ?? resolveApplicationSessionLane(oxyServices, clientId))();
170
+ // Observed so a rejection that lands before it is awaited is not reported as
171
+ // unhandled; every path below still awaits it and sees the rejection.
172
+ classification.catch(() => undefined);
173
+
174
+ // The verdict decides HOW a session is restored, never WHETHER there is one.
175
+ // A web origin with nothing to restore and no OAuth return on the URL ends
176
+ // signed out on either lane, so routing need not wait a network round trip
177
+ // for an answer that cannot change the outcome. The classification still
178
+ // completes below and still gates every later sign-in
179
+ // (`ensureDeviceSessionLane` shares this same in-flight lookup).
180
+ if (!identityBound && isWebBrowser() && !hasOAuthReturnOnUrl()) {
181
+ if (!(await hasRestorableState(authStore))) {
182
+ markAuthResolved();
183
+ const lane = await classification;
184
+ await opts.onClassified?.(lane);
185
+ if (lane === 'oauth') setTokenReady(true);
186
+ return;
187
+ }
188
+ }
189
+
190
+ const lane = await classification;
146
191
  await opts.onClassified?.(lane);
147
192
  if (identityBound && lane !== 'device') return;
148
193
  // The redirect transport's RETURN leg. An app reaches it whenever the browser
@@ -2,10 +2,8 @@ import { useState } from 'react';
2
2
  import { ActivityIndicator, View } from 'react-native';
3
3
  import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
4
4
  import { isHttpRequestError } from '@oxy.so/core';
5
- import {
6
- organizationClaimReviewSchema,
7
- type OrganizationClaimReview as OrganizationClaimReviewInput,
8
- } from '@oxy.so/contracts';
5
+ import type { OrganizationClaimReview as OrganizationClaimReviewInput } from '@oxy.so/contracts';
6
+ import { organizationClaimReviewCheck, parseContract } from '@oxy.so/contracts/client';
9
7
  import { Button } from '@oxy.so/bloom/button';
10
8
  import { SettingsListGroup, SettingsListItem } from '@oxy.so/bloom/settings-list';
11
9
  import { TextField, TextFieldInput } from '@oxy.so/bloom/text-field';
@@ -123,7 +121,7 @@ function ReviewDetail({ claimId, onClose }: { claimId: string; onClose: () => vo
123
121
  throw new Error('Independent staff review is required');
124
122
  return oxyServices.organizations.reviewClaim(
125
123
  claimId,
126
- organizationClaimReviewSchema.parse({ decision, evidenceReference: evidence }),
124
+ parseContract(organizationClaimReviewCheck, { decision, evidenceReference: evidence }),
127
125
  );
128
126
  },
129
127
  onError: (error) => {
@@ -1,12 +1,13 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { ActivityIndicator, View } from 'react-native';
3
3
  import { useInfiniteQuery, useMutation, useQueryClient } from '@tanstack/react-query';
4
+ import type { Organization, OrganizationType } from '@oxy.so/contracts';
4
5
  import {
5
- createOrganizationSchema,
6
- organizationClaimInputSchema,
7
- type Organization,
8
- type OrganizationType,
9
- } from '@oxy.so/contracts';
6
+ createOrganizationCheck,
7
+ organizationClaimInputCheck,
8
+ parseContract,
9
+ checkContract,
10
+ } from '@oxy.so/contracts/client';
10
11
  import { Avatar } from '@oxy.so/bloom/avatar';
11
12
  import { Button } from '@oxy.so/bloom/button';
12
13
  import { SettingsListGroup, SettingsListItem } from '@oxy.so/bloom/settings-list';
@@ -56,7 +57,7 @@ export function OrganizationDirectorySearch({
56
57
  throw new Error('An active organization and personal session are required');
57
58
  return oxyServices.organizations.claim(
58
59
  selected.accountId,
59
- organizationClaimInputSchema.parse({ evidenceReference: evidence }),
60
+ parseContract(organizationClaimInputCheck, { evidenceReference: evidence }),
60
61
  );
61
62
  },
62
63
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['oxy', 'organizations'] }),
@@ -65,7 +66,7 @@ export function OrganizationDirectorySearch({
65
66
  mutationFn: () => {
66
67
  if (!personalReady) throw new Error('A personal session is required');
67
68
  return oxyServices.organizations.create(
68
- createOrganizationSchema.parse({ name: search.trim(), organizationType: type }),
69
+ parseContract(createOrganizationCheck, { name: search.trim(), organizationType: type }),
69
70
  );
70
71
  },
71
72
  onSuccess: (organization) => {
@@ -210,8 +211,10 @@ export function OrganizationDirectorySearch({
210
211
  loading={create.isPending}
211
212
  onPress={() => {
212
213
  if (
213
- !createOrganizationSchema.safeParse({ name: search.trim(), organizationType: type })
214
- .success
214
+ checkContract(createOrganizationCheck, {
215
+ name: search.trim(),
216
+ organizationType: type,
217
+ }) === null
215
218
  ) {
216
219
  setValidationError(true);
217
220
  return;
@@ -1,11 +1,8 @@
1
1
  import { useState } from 'react';
2
2
  import { ActivityIndicator, View } from 'react-native';
3
3
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
4
- import {
5
- updateOrganizationSchema,
6
- type Organization,
7
- type OrganizationType,
8
- } from '@oxy.so/contracts';
4
+ import type { Organization, OrganizationType, UpdateOrganization } from '@oxy.so/contracts';
5
+ import { updateOrganizationCheck, checkContract } from '@oxy.so/contracts/client';
9
6
  import { Button } from '@oxy.so/bloom/button';
10
7
  import { toast } from '@oxy.so/bloom/toast';
11
8
  import { SettingsListGroup } from '@oxy.so/bloom/settings-list';
@@ -62,7 +59,7 @@ function OrganizationForm({ organization }: { organization: Organization }) {
62
59
  const [type, setType] = useState<OrganizationType>(organization.organizationType);
63
60
  const [invalid, setInvalid] = useState(false);
64
61
  const save = useMutation({
65
- mutationFn: (input: ReturnType<typeof updateOrganizationSchema.parse>) => {
62
+ mutationFn: (input: UpdateOrganization) => {
66
63
  if (!canUsePrivateApi) throw new Error('An authenticated session is required');
67
64
  return oxyServices.organizations.update(organization.accountId, input);
68
65
  },
@@ -113,14 +110,14 @@ function OrganizationForm({ organization }: { organization: Organization }) {
113
110
  disabled={save.isPending || !canUsePrivateApi}
114
111
  loading={save.isPending}
115
112
  onPress={() => {
116
- const input = updateOrganizationSchema.safeParse({
113
+ const input = checkContract(updateOrganizationCheck, {
117
114
  legalName: legalName.trim() || null,
118
115
  websiteUrl: website.trim() || null,
119
116
  countryCode: country.trim().toUpperCase() || null,
120
117
  organizationType: type,
121
118
  });
122
- setInvalid(!input.success);
123
- if (input.success) save.mutate(input.data);
119
+ setInvalid(input === null);
120
+ if (input) save.mutate(input);
124
121
  }}
125
122
  >
126
123
  {t('organizationDirectory.save')}
@@ -1,4 +1,5 @@
1
- import { ORGANIZATION_TYPES, type OrganizationType } from '@oxy.so/contracts';
1
+ import type { OrganizationType } from '@oxy.so/contracts';
2
+ import { ORGANIZATION_TYPES } from '@oxy.so/contracts/client';
2
3
  import { SettingsListGroup, SettingsListItem } from '@oxy.so/bloom/settings-list';
3
4
  import { Text } from '@oxy.so/bloom/typography';
4
5
  import { useI18n } from '../../hooks/useI18n';
@@ -19,7 +19,8 @@ import { CodeBlock } from '@oxy.so/bloom/code';
19
19
  import { useTheme } from '@oxy.so/bloom/theme';
20
20
  import { toast } from '@oxy.so/bloom/toast';
21
21
  import { Text } from '@oxy.so/bloom/typography';
22
- import { TOTP_DIGITS, type TotpEnrollResponse } from '@oxy.so/contracts';
22
+ import type { TotpEnrollResponse } from '@oxy.so/contracts';
23
+ import { TOTP_DIGITS } from '@oxy.so/contracts/client';
23
24
  import { useQueryClient } from '@tanstack/react-query';
24
25
  import { useOxy } from '../../context/OxyContext';
25
26
  import { useSignInMethods } from '../../hooks/queries/useAuthMethods';
@@ -0,0 +1,116 @@
1
+ /**
2
+ * "Choose an account" after an email was proven — the email holds several
3
+ * accounts, and the code or the link just proved the mailbox they share.
4
+ *
5
+ * Presentational, and the same rounded list as `OxyAccountPicker`: each row is
6
+ * the account's name and `@handle` on the left, its avatar and a chevron on the
7
+ * right. The host signs the chosen account in (`oxy.auth.email.choose`).
8
+ */
9
+
10
+ import type React from 'react';
11
+ import { Fragment, useState } from 'react';
12
+ import { ScrollView } from 'react-native';
13
+ import { Pressable, View } from 'react-native-css/components';
14
+ import { Avatar } from '@oxy.so/bloom/avatar';
15
+ import { RiArrowRightSLine } from '@oxy.so/bloom/icons/RiArrowRightSLine';
16
+ import { SpinnerIcon } from '@oxy.so/bloom/loading';
17
+ import { useTheme } from '@oxy.so/bloom/theme';
18
+ import { Text } from '@oxy.so/bloom/typography';
19
+ import type { EmailSignInAccountOption } from '@oxy.so/contracts';
20
+ import { getNormalizedUserHandle } from '@oxy.so/core';
21
+ import { useOxy } from '../../context/OxyContext';
22
+
23
+ /** The list scrolls past this height (`max-h-96`). */
24
+ const LIST_MAX_HEIGHT = 384;
25
+ const AVATAR_SIZE = 40;
26
+
27
+ export interface OxyEmailAccountChoiceProps {
28
+ accounts: EmailSignInAccountOption[];
29
+ onChoose: (account: EmailSignInAccountOption) => void;
30
+ /** The account being signed in, whose row shows it is busy. */
31
+ pendingId?: string | null;
32
+ /** Disables every row while a choice is in flight. */
33
+ disabled?: boolean;
34
+ }
35
+
36
+ export const OxyEmailAccountChoice: React.FC<OxyEmailAccountChoiceProps> = ({
37
+ accounts,
38
+ onChoose,
39
+ pendingId = null,
40
+ disabled = false,
41
+ }) => {
42
+ const theme = useTheme();
43
+ const { oxyServices } = useOxy();
44
+ const [hovered, setHovered] = useState<number | null>(null);
45
+
46
+ /** The hairline under row `index`, hidden while either neighbour is hovered. */
47
+ const divider = (index: number) =>
48
+ index === accounts.length - 1 ? null : hovered === index || hovered === index + 1 ? (
49
+ <View className="h-px mx-[8px]" />
50
+ ) : (
51
+ <View className="h-px mx-[8px] bg-border opacity-50" />
52
+ );
53
+
54
+ return (
55
+ <View
56
+ className="bg-fill-secondary rounded-[22px] overflow-hidden"
57
+ testID="email-account-choice"
58
+ >
59
+ <ScrollView style={{ maxHeight: LIST_MAX_HEIGHT }} contentContainerStyle={{ padding: 8 }}>
60
+ {accounts.map((account, index) => {
61
+ const handle = getNormalizedUserHandle({ username: account.username });
62
+ const name = account.displayName ?? (handle ? `@${handle}` : account.id);
63
+ const busy = pendingId === account.id;
64
+ return (
65
+ <Fragment key={account.id}>
66
+ <Pressable
67
+ className={`flex-row items-center gap-[12px] rounded-[14px] p-[8px] ${hovered === index && !disabled ? 'bg-fill' : ''} ${disabled && !busy ? 'opacity-50' : ''}`}
68
+ onPress={() => onChoose(account)}
69
+ onHoverIn={() => setHovered(index)}
70
+ onHoverOut={() => setHovered((value) => (value === index ? null : value))}
71
+ disabled={disabled}
72
+ accessibilityRole="button"
73
+ accessibilityLabel={name}
74
+ accessibilityState={{ disabled, busy }}
75
+ testID={`email-account-${account.id}`}
76
+ >
77
+ <View className="flex-1 min-w-0">
78
+ <Text
79
+ className="text-body text-text"
80
+ style={{ fontWeight: '500' }}
81
+ numberOfLines={1}
82
+ >
83
+ {name}
84
+ </Text>
85
+ {account.displayName && handle ? (
86
+ <Text className="text-bodySmall text-text-secondary" numberOfLines={1}>
87
+ @{handle}
88
+ </Text>
89
+ ) : null}
90
+ </View>
91
+ <View className="flex-row items-center gap-[8px] shrink-0">
92
+ <Avatar
93
+ source={
94
+ account.avatar
95
+ ? oxyServices.assets.publicUrl(account.avatar, 'thumb')
96
+ : undefined
97
+ }
98
+ variant="thumb"
99
+ name={name}
100
+ size={AVATAR_SIZE}
101
+ />
102
+ {busy ? (
103
+ <SpinnerIcon size={20} color={theme.colors.primary} />
104
+ ) : (
105
+ <RiArrowRightSLine size="sm" fill={theme.colors.textSecondary} />
106
+ )}
107
+ </View>
108
+ </Pressable>
109
+ {divider(index)}
110
+ </Fragment>
111
+ );
112
+ })}
113
+ </ScrollView>
114
+ </View>
115
+ );
116
+ };
@@ -19,9 +19,9 @@ import { Button } from '@oxy.so/bloom/button';
19
19
  import { Loading } from '@oxy.so/bloom/loading';
20
20
  import { useTheme } from '@oxy.so/bloom/theme';
21
21
  import { Text } from '@oxy.so/bloom/typography';
22
- import { deriveIdentityLinkCode } from '@oxy.so/core/crypto';
23
22
  import type { IdentityLinkCreateResponse } from '@oxy.so/contracts';
24
23
  import { useOxy } from '../../context/OxyContext';
24
+ import { loadIdentityCrypto } from '../../utils/identityCrypto';
25
25
  import { useSignInMethods } from '../../hooks/queries/useAuthMethods';
26
26
  import { useI18n } from '../../hooks/useI18n';
27
27
  import { OxyAuthScreen, OxyAuthScreenHeader } from './OxyAuthScreen';
@@ -99,6 +99,8 @@ export const OxyLinkCommonsPanel: React.FC<OxyLinkCommonsPanelProps> = ({ onLink
99
99
  const state = await oxyServices.identity.links.get(link.linkId);
100
100
  if (stopped) return;
101
101
  if (state.status === 'signed' && state.publicKey) {
102
+ const { deriveIdentityLinkCode } = await loadIdentityCrypto();
103
+ if (stopped) return;
102
104
  setStep({
103
105
  name: 'matching',
104
106
  link,
@@ -11,7 +11,7 @@ import { StyleSheet } from 'react-native';
11
11
  import { Field } from '@oxy.so/bloom/field';
12
12
  import { Switch } from '@oxy.so/bloom/switch';
13
13
  import { toast } from '@oxy.so/bloom/toast';
14
- import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH } from '@oxy.so/contracts';
14
+ import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH } from '@oxy.so/contracts/client';
15
15
  import { useQueryClient } from '@tanstack/react-query';
16
16
  import { useOxy } from '../../context/OxyContext';
17
17
  import { useSignInMethods } from '../../hooks/queries/useAuthMethods';