@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
@@ -16,6 +16,8 @@
16
16
  * (the start response does not say which, by design); meanwhile
17
17
  * the screen asks whether the link was opened in this browser,
18
18
  * and signs in when it was.
19
+ * choose the email holds several accounts, and the code or the link just
20
+ * proved it: which one signs in (a username never gets here).
19
21
  * password the alternative for an account that has one.
20
22
  * second-factor the authenticator's code, or a backup code, when the account has one.
21
23
  *
@@ -34,15 +36,19 @@ import { useTheme } from '@oxy.so/bloom/theme';
34
36
  import { toast } from '@oxy.so/bloom/toast';
35
37
  import { Text } from '@oxy.so/bloom/typography';
36
38
  import type { SwitcherContextRow } from '@oxy.so/core/session';
39
+ import type {
40
+ EmailSignInAccountOption,
41
+ EmailSignInStepResult,
42
+ LoginResult,
43
+ } from '@oxy.so/contracts';
37
44
  import {
38
45
  EMAIL_CODE_LENGTH,
39
46
  EMAIL_SIGNIN_LONG_CODE_LENGTH,
40
47
  SIGN_IN_ERROR_CODES,
41
48
  TOTP_DIGITS,
49
+ isEmailSignInChooseAccount,
42
50
  isSecondFactorRequired,
43
- type LoginResult,
44
- type SignInStepResult,
45
- } from '@oxy.so/contracts';
51
+ } from '@oxy.so/contracts/client';
46
52
  import { useQueryClient } from '@tanstack/react-query';
47
53
  import { useOxy } from '../../context/OxyContext';
48
54
  import { useAccountDialogSnapshot } from '../../hooks/accountDialogSnapshot';
@@ -56,6 +62,7 @@ import { SIGN_IN_SLIDES } from './artwork';
56
62
  import { InlineCommonsQr } from './InlineCommonsQr';
57
63
  import { OxyAccountPicker } from './OxyAccountPicker';
58
64
  import { OxyAuthScreen, OxyAuthScreenHeader, OxyAuthSplit, OxyAuthTerms } from './OxyAuthScreen';
65
+ import { OxyEmailAccountChoice } from './OxyEmailAccountChoice';
59
66
  import {
60
67
  AccountFlowAction,
61
68
  AccountFlowCodeField,
@@ -94,6 +101,13 @@ const SIGN_IN_FLOW_KEY = 'signin';
94
101
  type Step =
95
102
  | { name: 'start' }
96
103
  | { name: 'check-email'; identifier: string; requestId: string; requestSecret: string }
104
+ | {
105
+ name: 'choose';
106
+ identifier: string;
107
+ requestId: string;
108
+ requestSecret: string;
109
+ accounts: EmailSignInAccountOption[];
110
+ }
97
111
  | { name: 'password'; identifier: string }
98
112
  | { name: 'second-factor'; challengeId: string; identifier: string };
99
113
 
@@ -153,6 +167,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
153
167
  const [useBackupCode, setUseBackupCode] = useState(saved?.useBackupCode ?? false);
154
168
  const [longCode, setLongCode] = useState(saved?.longCode ?? false);
155
169
  const [pending, setPending] = useState(false);
170
+ const [choosingId, setChoosingId] = useState<string | null>(null);
156
171
  const [error, setError] = useState<string | null>(null);
157
172
  const [notice, setNotice] = useState<string | null>(null);
158
173
  const [rateLimitSeconds, setRateLimitSeconds] = useState(0);
@@ -225,8 +240,25 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
225
240
  // One sign-in ends once: the typed code and the opened link can race.
226
241
  const finishingRef = useRef(false);
227
242
  const finish = useCallback(
228
- async (result: SignInStepResult, from: string): Promise<void> => {
243
+ async (
244
+ result: EmailSignInStepResult,
245
+ from: string,
246
+ request?: { requestId: string; requestSecret: string },
247
+ ): Promise<void> => {
229
248
  if (finishingRef.current) return;
249
+ if (isEmailSignInChooseAccount(result)) {
250
+ if (!request) return;
251
+ setError(null);
252
+ setNotice(null);
253
+ setStep({
254
+ name: 'choose',
255
+ identifier: from,
256
+ requestId: request.requestId,
257
+ requestSecret: request.requestSecret,
258
+ accounts: result.accounts,
259
+ });
260
+ return;
261
+ }
230
262
  if (isSecondFactorRequired(result)) {
231
263
  setError(null);
232
264
  setSecondFactorCode('');
@@ -301,7 +333,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
301
333
  requestSecret: request.requestSecret,
302
334
  code: typed.trim(),
303
335
  })
304
- .then((result) => finish(result, request.identifier))
336
+ .then((result) => finish(result, request.identifier, request))
305
337
  .catch((reason: unknown) => {
306
338
  setCode('');
307
339
  fail(reason);
@@ -309,6 +341,27 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
309
341
  .finally(() => setPending(false));
310
342
  };
311
343
 
344
+ const chooseAccount = (account: EmailSignInAccountOption) => {
345
+ if (step.name !== 'choose' || blocked) return;
346
+ const request = step;
347
+ const name = account.username ?? request.identifier;
348
+ setError(null);
349
+ setPending(true);
350
+ setChoosingId(account.id);
351
+ oxyServices.auth.email
352
+ .choose({
353
+ requestId: request.requestId,
354
+ requestSecret: request.requestSecret,
355
+ userId: account.id,
356
+ })
357
+ .then((result) => finish(result, name))
358
+ .catch(fail)
359
+ .finally(() => {
360
+ setPending(false);
361
+ setChoosingId(null);
362
+ });
363
+ };
364
+
312
365
  const submitPassword = () => {
313
366
  if (step.name !== 'password' || blocked) return;
314
367
  if (!password) {
@@ -369,7 +422,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
369
422
  try {
370
423
  const result = await oxyServices.auth.email.collect({ requestId, requestSecret });
371
424
  if (!('status' in result)) {
372
- await finishRef.current(result, requestIdentifier);
425
+ await finishRef.current(result, requestIdentifier, { requestId, requestSecret });
373
426
  return;
374
427
  }
375
428
  if (stopped) return;
@@ -550,6 +603,40 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
550
603
  );
551
604
  break;
552
605
  }
606
+ case 'choose': {
607
+ const request = step;
608
+ form = (
609
+ <OxyAuthScreen className={splits ? 'md:max-w-none' : undefined}>
610
+ <OxyAuthScreenHeader
611
+ title={t('signin.emailAccounts.title')}
612
+ description={t('signin.emailAccounts.description', {
613
+ identifier: request.identifier,
614
+ })}
615
+ />
616
+ <OxyEmailAccountChoice
617
+ accounts={request.accounts}
618
+ onChoose={chooseAccount}
619
+ pendingId={choosingId}
620
+ disabled={blocked}
621
+ />
622
+ {error ? (
623
+ <AccountFlowNotice type="error" testID="signin-choose-error">
624
+ {error}
625
+ </AccountFlowNotice>
626
+ ) : null}
627
+ {rateLimitNotice}
628
+ <View style={styles.links}>
629
+ <SubtleLink
630
+ label={t('signin.checkEmail.differentAccount')}
631
+ onPress={backToIdentifier}
632
+ disabled={pending}
633
+ testID="signin-different-account"
634
+ />
635
+ </View>
636
+ </OxyAuthScreen>
637
+ );
638
+ break;
639
+ }
553
640
  case 'password': {
554
641
  const request = step;
555
642
  form = (
@@ -15,11 +15,8 @@ import type React from 'react';
15
15
  import { useEffect, useState } from 'react';
16
16
  import { Linking, Platform } from 'react-native';
17
17
  import { toast } from '@oxy.so/bloom/toast';
18
- import {
19
- SIGN_IN_ERROR_CODES,
20
- emailAddressSchema,
21
- type EmailVerificationConfirmResponse,
22
- } from '@oxy.so/contracts';
18
+ import type { EmailVerificationConfirmResponse } from '@oxy.so/contracts';
19
+ import { SIGN_IN_ERROR_CODES, emailAddressCheck, checkContract } from '@oxy.so/contracts/client';
23
20
  import { useOxy } from '../../context/OxyContext';
24
21
  import { useAccountDialogSnapshot } from '../../hooks/accountDialogSnapshot';
25
22
  import { useI18n } from '../../hooks/useI18n';
@@ -126,13 +123,13 @@ export const OxySignUpPanel: React.FC<OxySignUpPanelProps> = ({
126
123
  };
127
124
 
128
125
  const submitEmail = () => {
129
- const parsed = emailAddressSchema.safeParse(email);
130
- if (!parsed.success) {
126
+ const address = checkContract(emailAddressCheck, email);
127
+ if (address === null) {
131
128
  setError(t('signup.email.invalid'));
132
129
  return;
133
130
  }
134
- setEmail(parsed.data);
135
- run(() => sendCode(parsed.data));
131
+ setEmail(address);
132
+ run(() => sendCode(address));
136
133
  };
137
134
 
138
135
  // The code step reports this promise's failure in place.
@@ -8,12 +8,8 @@
8
8
 
9
9
  import type React from 'react';
10
10
  import { useState } from 'react';
11
- import {
12
- EMAIL_CODE_LENGTH,
13
- TOTP_DIGITS,
14
- type ReauthAction,
15
- type ReauthProof,
16
- } from '@oxy.so/contracts';
11
+ import type { ReauthAction, ReauthProof } from '@oxy.so/contracts';
12
+ import { EMAIL_CODE_LENGTH, TOTP_DIGITS } from '@oxy.so/contracts/client';
17
13
  import { useOxy } from '../../context/OxyContext';
18
14
  import { useI18n } from '../../hooks/useI18n';
19
15
  import { SubtleLink } from '../authChooser/primitives';
@@ -18,6 +18,7 @@ import { useTheme } from '@oxy.so/bloom/theme';
18
18
  import { TextField, TextFieldHint, TextFieldInput, TextFieldLabel } from '@oxy.so/bloom/text-field';
19
19
  import { Text } from '@oxy.so/bloom/typography';
20
20
  import { WizardProgress } from '@oxy.so/bloom/wizard';
21
+ import type { EmailVerificationConfirmResponse } from '@oxy.so/contracts';
21
22
  import {
22
23
  EMAIL_CODE_LENGTH,
23
24
  EMAIL_SIGNIN_LONG_CODE_ALPHABET,
@@ -25,8 +26,7 @@ import {
25
26
  EMAIL_VERIFICATION_ERROR_CODES,
26
27
  SIGN_IN_ERROR_CODES,
27
28
  normalizeEmailSignInCode,
28
- type EmailVerificationConfirmResponse,
29
- } from '@oxy.so/contracts';
29
+ } from '@oxy.so/contracts/client';
30
30
  import { useOxy } from '../../context/OxyContext';
31
31
  import { useI18n } from '../../hooks/useI18n';
32
32
  import type { Translate } from '../authChooser/types';
@@ -85,6 +85,8 @@ export function describeSignInError(error: unknown, t: Translate): string {
85
85
  return t('signin.errors.originNotAllowed');
86
86
  case SIGN_IN_ERROR_CODES.usernameTaken:
87
87
  return t('signup.username.taken');
88
+ case SIGN_IN_ERROR_CODES.emailAccountsFull:
89
+ return t('signin.errors.emailAccountsFull');
88
90
  case EMAIL_VERIFICATION_ERROR_CODES.codeInvalid:
89
91
  return t('emailCode.errors.codeInvalid');
90
92
  case EMAIL_VERIFICATION_ERROR_CODES.tooManyAttempts:
@@ -29,7 +29,6 @@ import type {
29
29
  AccountDialogController,
30
30
  AccountDialogView,
31
31
  } from '@oxy.so/core/session';
32
- import { KeyManager } from '@oxy.so/core/crypto';
33
32
  import {
34
33
  refreshDeviceSecretArm,
35
34
  establishIdentitySession,
@@ -60,6 +59,7 @@ import {
60
59
  type RequestOAuthConsentOptions,
61
60
  } from '../oauth/explicitOAuthConsent';
62
61
  import { isWebBrowser } from '../utils/isWebBrowser';
62
+ import { loadIdentityCrypto } from '../utils/identityCrypto';
63
63
  import { resolveDeliveryPlatform } from '../utils/deliveryPlatform';
64
64
  import { runProviderColdBoot } from '../boot/runProviderColdBoot';
65
65
  import { resolveApplicationSessionLane } from '../oauth/applicationSessionLane';
@@ -696,6 +696,7 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
696
696
  try {
697
697
  // The same keys the refresh handler's second arm signs with: the
698
698
  // identity-bound client's own key, or the cross-app shared identity.
699
+ const { KeyManager } = await loadIdentityCrypto();
699
700
  return identity ? await KeyManager.hasIdentity() : await KeyManager.hasSharedIdentity();
700
701
  } catch {
701
702
  return false;
@@ -1495,9 +1496,12 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
1495
1496
  // — a locked keychain must never be misreported as "no identity". `hasIdentity`
1496
1497
  // still resolves `false` and `getPublicKey` still resolves `null` for a genuine
1497
1498
  // absence.
1498
- const hasIdentity = useCallback(async (): Promise<boolean> => KeyManager.hasIdentity(), []);
1499
+ const hasIdentity = useCallback(
1500
+ async (): Promise<boolean> => (await loadIdentityCrypto()).KeyManager.hasIdentity(),
1501
+ [],
1502
+ );
1499
1503
  const getPublicKey = useCallback(
1500
- async (): Promise<string | null> => KeyManager.getPublicKey(),
1504
+ async (): Promise<string | null> => (await loadIdentityCrypto()).KeyManager.getPublicKey(),
1501
1505
  [],
1502
1506
  );
1503
1507
 
@@ -8,9 +8,9 @@ import type { LogoutResult } from '../oxyContextTypes';
8
8
  import { DeviceManager } from '@oxy.so/core/session';
9
9
  import { fetchSessionsWithFallback } from '../../utils/sessionHelpers';
10
10
  import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
11
+ import { loadIdentityCrypto } from '../../utils/identityCrypto';
11
12
  import type { StorageInterface } from '../../utils/storageHelpers';
12
13
  import type { OxyServices } from '@oxy.so/core';
13
- import { SignatureService } from '@oxy.so/core/crypto';
14
14
 
15
15
  export interface UseAuthOperationsOptions {
16
16
  oxyServices: OxyServices;
@@ -126,6 +126,7 @@ export const useAuthOperations = ({
126
126
  // in local storage as 'oxy_biometric_enabled' and can be checked there.
127
127
 
128
128
  // Sign the challenge
129
+ const { SignatureService } = await loadIdentityCrypto();
129
130
  const { challenge: signature, timestamp } = await SignatureService.signChallenge(challenge);
130
131
 
131
132
  let fullUser: User;
@@ -1,8 +1,8 @@
1
- import { publicApplicationSchema } from '@oxy.so/contracts';
1
+ import { publicApplicationCheck, parseContract } from '@oxy.so/contracts/client';
2
2
  import type { OxyServices } from '@oxy.so/core';
3
3
 
4
4
  export function classifyApplicationSessionLane(metadata: unknown): 'device' | 'oauth' {
5
- const application = publicApplicationSchema.parse(metadata);
5
+ const application = parseContract(publicApplicationCheck, metadata);
6
6
  return application.type === 'third_party' && !application.isOfficial && !application.isInternal
7
7
  ? 'oauth'
8
8
  : 'device';
@@ -30,8 +30,13 @@ const ASSIGNABLE_ROLES: AssignableRole[] = ['admin', 'editor', 'developer', 'bil
30
30
  const errorMessage = (error: unknown, fallback: string): string =>
31
31
  error instanceof Error && error.message ? error.message : fallback;
32
32
 
33
+ const isUserNotFound = (error: unknown): boolean => {
34
+ const message = error instanceof Error ? error.message.toLowerCase() : '';
35
+ return message.includes('user not found') || message.includes('not found');
36
+ };
37
+
33
38
  /**
34
- * Manage the members of an account (invite by email, change roles,
39
+ * Manage the members of an account (invite by username/email, change roles,
35
40
  * remove, transfer ownership). Gated on the caller's `callerMembership`
36
41
  * permissions. Receives the target `accountId` via navigation props.
37
42
  */
@@ -81,27 +86,6 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
81
86
  queryFn: () => oxyServices.accounts.members.list(id),
82
87
  enabled: canUsePrivateApi && id.length > 0 && canRead,
83
88
  });
84
- const invitationsQuery = useQuery({
85
- queryKey: ['account-invitations', id],
86
- queryFn: () => oxyServices.accounts.invitations.list(id),
87
- enabled: canUsePrivateApi && !!id && canRead,
88
- });
89
- const invitationAction = useMutation({
90
- mutationFn: async ({
91
- invitationId,
92
- action,
93
- }: {
94
- invitationId: string;
95
- action: 'resend' | 'cancel';
96
- }) => {
97
- if (action === 'resend') await oxyServices.accounts.invitations.resend(id, invitationId);
98
- else await oxyServices.accounts.invitations.cancel(id, invitationId);
99
- },
100
- onSuccess: () => {
101
- void invitationsQuery.refetch();
102
- },
103
- onError: (error) => toast.error(errorMessage(error, 'Could not update invitation')),
104
- });
105
89
  const members = useMemo<AccountMember[]>(() => membersQuery.data ?? [], [membersQuery.data]);
106
90
  const memberUserIds = useMemo(
107
91
  () => [...new Set(members.map((member) => member.memberUserId).filter((id) => id.length > 0))],
@@ -142,7 +126,6 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
142
126
  // tables — mirror the SDK's per-account prefix sweep so every cached member
143
127
  // list refetches, not only the account that was mutated.
144
128
  queryClient.invalidateQueries({ queryKey: ['accounts', 'members'] });
145
- queryClient.invalidateQueries({ queryKey: ['account-invitations', id] });
146
129
  queryClient.invalidateQueries({ queryKey: ['accounts', 'detail', id] });
147
130
  }, [queryClient, id]);
148
131
 
@@ -154,20 +137,24 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
154
137
  const inviteMutation = useMutation({
155
138
  mutationKey: ['accounts', 'members', 'invite', id],
156
139
  mutationFn: (input: { usernameOrEmail: string; role: AssignableRole }) =>
157
- oxyServices.accounts.invitations.create(id, {
158
- email: input.usernameOrEmail,
159
- role: input.role,
160
- }),
140
+ oxyServices.accounts.members.invite(id, input),
161
141
  onSuccess: () => {
162
142
  setInviteIdentifier('');
163
143
  setInviteRole('editor');
164
144
  setInviteError(null);
165
145
  invalidateMembers();
166
- toast.success('Invitation sent');
146
+ toast.success(t('accounts.members.toasts.added') || 'Member added');
167
147
  },
168
148
  onError: (error) => {
169
- invalidateMembers();
170
- toast.error(errorMessage(error, 'Could not send invitation'));
149
+ if (isUserNotFound(error)) {
150
+ const message = t('accounts.members.errors.userNotFound') || 'User not found';
151
+ setInviteError(message);
152
+ toast.error(message);
153
+ return;
154
+ }
155
+ toast.error(
156
+ errorMessage(error, t('accounts.members.toasts.addFailed') || 'Failed to add member'),
157
+ );
171
158
  },
172
159
  });
173
160
 
@@ -220,12 +207,14 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
220
207
  const handleInvite = useCallback(() => {
221
208
  const usernameOrEmail = inviteIdentifier.trim();
222
209
  if (!usernameOrEmail) {
223
- setInviteError('Enter an email to invite');
210
+ setInviteError(
211
+ t('accounts.members.errors.identifierRequired') || 'Enter a username or email to invite',
212
+ );
224
213
  return;
225
214
  }
226
215
  setInviteError(null);
227
216
  inviteMutation.mutate({ usernameOrEmail, role: inviteRole });
228
- }, [inviteIdentifier, inviteRole, inviteMutation]);
217
+ }, [inviteIdentifier, inviteRole, inviteMutation, t]);
229
218
 
230
219
  const handleChangeRole = useCallback(
231
220
  (member: AccountMember, role: AccountRole) => {
@@ -293,11 +282,13 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
293
282
  {/* Invite form */}
294
283
  {canInvite ? (
295
284
  <View className="gap-space-12 p-space-16 rounded-radius-20 bg-fill">
296
- <Text className="text-body font-semibold text-text">{'Invite a member'}</Text>
285
+ <Text className="text-body font-semibold text-text">
286
+ {t('accounts.members.invite.title') || 'Add a member'}
287
+ </Text>
297
288
  <TextField isInvalid={!!inviteError}>
298
289
  <TextFieldInput
299
290
  floatingLabel
300
- label={'Email'}
291
+ label={t('accounts.members.invite.identifierLabel') || 'Username or email'}
301
292
  value={inviteIdentifier}
302
293
  onChangeText={(value) => {
303
294
  setInviteIdentifier(value);
@@ -341,49 +332,14 @@ const AccountMembersScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, acco
341
332
  onPress={handleInvite}
342
333
  disabled={inviteMutation.isPending || !inviteIdentifier.trim()}
343
334
  loading={inviteMutation.isPending}
344
- accessibilityLabel={'Send invitation'}
335
+ accessibilityLabel={t('accounts.members.invite.submit') || 'Add member'}
345
336
  className="w-full"
346
337
  >
347
- {'Send invitation'}
338
+ {t('accounts.members.invite.submit') || 'Add member'}
348
339
  </Button>
349
340
  </View>
350
341
  ) : null}
351
342
 
352
- {canRead ? (
353
- <View className="gap-space-12">
354
- <Text className="text-body font-semibold text-text">Pending invitations</Text>
355
- {invitationsQuery.isError ? <Text>Could not load invitations.</Text> : null}
356
- {(invitationsQuery.data ?? []).map((invitation) => (
357
- <View key={invitation.id} className="gap-space-8 p-space-16 rounded-radius-20 bg-fill">
358
- <Text>
359
- {invitation.email} · {roleLabel(invitation.role)} · {invitation.status}
360
- </Text>
361
- <Text>Expires {new Date(invitation.expiresAt).toLocaleDateString()}</Text>
362
- {canInvite ? (
363
- <Button
364
- disabled={invitationAction.isPending}
365
- onPress={() =>
366
- invitationAction.mutate({ invitationId: invitation.id, action: 'resend' })
367
- }
368
- >
369
- Resend
370
- </Button>
371
- ) : null}
372
- {canRemove ? (
373
- <Button
374
- disabled={invitationAction.isPending}
375
- onPress={() =>
376
- invitationAction.mutate({ invitationId: invitation.id, action: 'cancel' })
377
- }
378
- >
379
- Cancel invitation
380
- </Button>
381
- ) : null}
382
- </View>
383
- ))}
384
- </View>
385
- ) : null}
386
-
387
343
  {/* Member list */}
388
344
  {canRead ? (
389
345
  <View className="gap-space-8">
@@ -1,5 +1,9 @@
1
1
  import type React from 'react';
2
- import { ORGANIZATION_NAME_MAX_LENGTH, organizationNameSchema } from '@oxy.so/contracts';
2
+ import {
3
+ ORGANIZATION_NAME_MAX_LENGTH,
4
+ organizationNameCheck,
5
+ checkContract,
6
+ } from '@oxy.so/contracts/client';
3
7
  import { OrganizationSettings } from '../components/organizations/OrganizationSettings';
4
8
  import { useCallback, useMemo, useState } from 'react';
5
9
  import { View, ActivityIndicator } from 'react-native';
@@ -152,7 +156,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
152
156
  }
153
157
  if (node?.account.kind === 'organization') {
154
158
  setDisplayNameError(
155
- organizationNameSchema.safeParse(trimmed).success
159
+ checkContract(organizationNameCheck, trimmed) !== null
156
160
  ? ''
157
161
  : t('organizationDirectory.invalidName'),
158
162
  );
@@ -1,9 +1,10 @@
1
1
  import type React from 'react';
2
+ import type { OrganizationType } from '@oxy.so/contracts';
2
3
  import {
3
4
  ORGANIZATION_NAME_MAX_LENGTH,
4
- organizationNameSchema,
5
- type OrganizationType,
6
- } from '@oxy.so/contracts';
5
+ organizationNameCheck,
6
+ checkContract,
7
+ } from '@oxy.so/contracts/client';
7
8
  import { OrganizationDirectorySearch } from '../components/organizations/OrganizationDirectorySearch';
8
9
  import { OrganizationTypePicker } from '../components/organizations/OrganizationTypePicker';
9
10
  import { useState, useCallback, useRef, useEffect } from 'react';
@@ -21,12 +22,12 @@ import {
21
22
  import {
22
23
  applyBotUsernameSuffix,
23
24
  stripDisallowedUsernameCharacters,
24
- usernameSchemaForAccountKind,
25
+ usernameCheckForAccountKind,
25
26
  BOT_USERNAME_INVALID_MESSAGE,
26
27
  USERNAME_INVALID_MESSAGE,
27
28
  USERNAME_MAX_LENGTH,
28
29
  USERNAME_MIN_LENGTH,
29
- } from '@oxy.so/contracts';
30
+ } from '@oxy.so/contracts/client';
30
31
  import type { BaseScreenProps } from '../types/navigation';
31
32
  import { useI18n } from '../hooks/useI18n';
32
33
  import { useSurfaceHeader } from '../hooks/useSurfaceHeader';
@@ -196,9 +197,9 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, paren
196
197
  // somebody who typed `a.b` to append `bot` sends them to be refused a second
197
198
  // time. The issue is read to choose between two LOCALIZED strings rather than
198
199
  // shown directly — the schema's message is English, and this screen is not.
199
- const parsed = usernameSchemaForAccountKind(forKind).safeParse(value);
200
- if (!parsed.success) {
201
- const failedTheLabel = parsed.error.issues[0]?.message === BOT_USERNAME_INVALID_MESSAGE;
200
+ const checked = usernameCheckForAccountKind(forKind)(value, []);
201
+ if (!checked.ok) {
202
+ const failedTheLabel = checked.issue.message === BOT_USERNAME_INVALID_MESSAGE;
202
203
  setUsernameStatus('invalid');
203
204
  setUsernameMessage(
204
205
  failedTheLabel
@@ -282,7 +283,7 @@ const CreateAccountScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, paren
282
283
  }
283
284
  if (kind === 'organization') {
284
285
  setDisplayNameError(
285
- organizationNameSchema.safeParse(trimmed).success
286
+ checkContract(organizationNameCheck, trimmed) !== null
286
287
  ? ''
287
288
  : t('organizationDirectory.invalidName'),
288
289
  );
@@ -5,7 +5,6 @@ import { Switch } from '@oxy.so/bloom/switch';
5
5
  import { useTheme } from '@oxy.so/bloom/theme';
6
6
  import type { UserPreferences } from '@oxy.so/core';
7
7
  import { getNativeLanguageName } from '@oxy.so/core';
8
- import { DisplayCurrencySelector } from '../components/DisplayCurrencySelector';
9
8
  import type { BaseScreenProps } from '../types/navigation';
10
9
  import { SettingsIcon } from '../components/SettingsIcon';
11
10
  import { useI18n } from '../hooks/useI18n';
@@ -159,7 +158,6 @@ const PreferencesScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, navigat
159
158
  </SettingsListGroup>
160
159
 
161
160
  <SettingsListGroup title={t('preferences.sections.region') || 'Region'}>
162
- <DisplayCurrencySelector />
163
161
  <SettingsListItem
164
162
  icon={<SettingsIcon name="clock-outline" color={bloomTheme.colors.info} />}
165
163
  title={t('preferences.items.timezone.title') || 'Timezone'}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The self-custody identity (`@oxy.so/core/crypto`: secp256k1, bip39, the AEAD
3
+ * cipher — ~32 KB gzipped on web), loaded where a key or signature is used.
4
+ *
5
+ * Importing it statically put it in every app's startup JavaScript, although a
6
+ * reader without an identity key never touches it before first paint. Core's
7
+ * `crypto` entry installs its polyfill before anything else, so loading it
8
+ * late keeps the order every key operation relies on.
9
+ */
10
+ export const loadIdentityCrypto = () => import('@oxy.so/core/crypto');