@oxy.so/contracts 4.16.0 → 4.21.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 (135) hide show
  1. package/dist/cjs/accountAccess.js +72 -0
  2. package/dist/cjs/accountEmail.js +12 -34
  3. package/dist/cjs/accountGraph.js +22 -320
  4. package/dist/cjs/agency.js +112 -1
  5. package/dist/cjs/agentKeys.js +11 -13
  6. package/dist/cjs/applicationRoles.js +211 -0
  7. package/dist/cjs/client/accountEmail.js +48 -0
  8. package/dist/cjs/client/accountGraph.js +323 -0
  9. package/dist/cjs/client/agentKeys.js +77 -0
  10. package/dist/cjs/client/check.js +475 -0
  11. package/dist/cjs/client/deviceBoot.js +22 -0
  12. package/dist/cjs/client/deviceDirectory.js +51 -0
  13. package/dist/cjs/client/deviceSession.js +63 -0
  14. package/dist/cjs/client/externalIdentity.js +19 -0
  15. package/dist/cjs/client/identity.js +45 -0
  16. package/dist/cjs/client/identityLink.js +55 -0
  17. package/dist/cjs/client/identityProof.js +109 -0
  18. package/dist/cjs/client/index.js +211 -0
  19. package/dist/cjs/client/organizations.js +114 -0
  20. package/dist/cjs/client/products.js +153 -0
  21. package/dist/cjs/client/recommendations.js +33 -0
  22. package/dist/cjs/client/reputation.js +41 -0
  23. package/dist/cjs/client/safety.js +196 -0
  24. package/dist/cjs/client/sessionStatus.js +23 -0
  25. package/dist/cjs/client/signIn.js +159 -0
  26. package/dist/cjs/client/userResponse.js +15 -0
  27. package/dist/cjs/client/username.js +173 -0
  28. package/dist/cjs/deviceSession.js +3 -16
  29. package/dist/cjs/identityLink.js +13 -34
  30. package/dist/cjs/identityProof.js +18 -106
  31. package/dist/cjs/index.js +13 -3
  32. package/dist/cjs/organizations.js +21 -41
  33. package/dist/cjs/reputation.js +3 -34
  34. package/dist/cjs/safety.js +430 -1
  35. package/dist/cjs/serviceToken.js +14 -0
  36. package/dist/cjs/signIn.js +33 -111
  37. package/dist/cjs/username.js +23 -158
  38. package/dist/esm/accountAccess.js +69 -0
  39. package/dist/esm/accountEmail.js +4 -32
  40. package/dist/esm/accountGraph.js +4 -309
  41. package/dist/esm/agency.js +111 -0
  42. package/dist/esm/agentKeys.js +4 -10
  43. package/dist/esm/applicationRoles.js +207 -0
  44. package/dist/esm/client/accountEmail.js +45 -0
  45. package/dist/esm/client/accountGraph.js +314 -0
  46. package/dist/esm/client/agentKeys.js +74 -0
  47. package/dist/esm/client/check.js +436 -0
  48. package/dist/esm/client/deviceBoot.js +19 -0
  49. package/dist/esm/client/deviceDirectory.js +48 -0
  50. package/dist/esm/client/deviceSession.js +60 -0
  51. package/dist/esm/client/externalIdentity.js +16 -0
  52. package/dist/esm/client/identity.js +42 -0
  53. package/dist/esm/client/identityLink.js +50 -0
  54. package/dist/esm/client/identityProof.js +104 -0
  55. package/dist/esm/client/index.js +37 -0
  56. package/dist/esm/client/organizations.js +111 -0
  57. package/dist/esm/client/products.js +150 -0
  58. package/dist/esm/client/recommendations.js +30 -0
  59. package/dist/esm/client/reputation.js +37 -0
  60. package/dist/esm/client/safety.js +193 -0
  61. package/dist/esm/client/sessionStatus.js +20 -0
  62. package/dist/esm/client/signIn.js +153 -0
  63. package/dist/esm/client/userResponse.js +12 -0
  64. package/dist/esm/client/username.js +167 -0
  65. package/dist/esm/deviceSession.js +1 -15
  66. package/dist/esm/identityLink.js +4 -27
  67. package/dist/esm/identityProof.js +4 -99
  68. package/dist/esm/index.js +4 -1
  69. package/dist/esm/organizations.js +4 -31
  70. package/dist/esm/reputation.js +1 -32
  71. package/dist/esm/safety.js +423 -0
  72. package/dist/esm/serviceToken.js +11 -0
  73. package/dist/esm/signIn.js +4 -96
  74. package/dist/esm/username.js +4 -148
  75. package/dist/types/accountAccess.d.ts +128 -0
  76. package/dist/types/accountEmail.d.ts +4 -36
  77. package/dist/types/accountGraph.d.ts +12 -208
  78. package/dist/types/agency.d.ts +351 -26
  79. package/dist/types/agentKeys.d.ts +14 -12
  80. package/dist/types/applicationRoles.d.ts +438 -0
  81. package/dist/types/client/accountEmail.d.ts +44 -0
  82. package/dist/types/client/accountGraph.d.ts +203 -0
  83. package/dist/types/client/agentKeys.d.ts +113 -0
  84. package/dist/types/client/check.d.ts +187 -0
  85. package/dist/types/client/deviceBoot.d.ts +6 -0
  86. package/dist/types/client/deviceDirectory.d.ts +134 -0
  87. package/dist/types/client/deviceSession.d.ts +91 -0
  88. package/dist/types/client/externalIdentity.d.ts +14 -0
  89. package/dist/types/client/identity.d.ts +8 -0
  90. package/dist/types/client/identityLink.d.ts +21 -0
  91. package/dist/types/client/identityProof.d.ts +73 -0
  92. package/dist/types/client/index.d.ts +37 -0
  93. package/dist/types/client/organizations.d.ts +167 -0
  94. package/dist/types/client/products.d.ts +145 -0
  95. package/dist/types/client/recommendations.d.ts +27 -0
  96. package/dist/types/client/reputation.d.ts +28 -0
  97. package/dist/types/client/safety.d.ts +213 -0
  98. package/dist/types/client/sessionStatus.d.ts +19 -0
  99. package/dist/types/client/signIn.d.ts +107 -0
  100. package/dist/types/client/userResponse.d.ts +7 -0
  101. package/dist/types/client/username.d.ts +126 -0
  102. package/dist/types/deviceDirectory.d.ts +28 -28
  103. package/dist/types/deviceSession.d.ts +5 -16
  104. package/dist/types/email/messages.d.ts +2 -2
  105. package/dist/types/externalIdentity.d.ts +15 -15
  106. package/dist/types/identityLink.d.ts +3 -11
  107. package/dist/types/identityProof.d.ts +2 -65
  108. package/dist/types/index.d.ts +5 -2
  109. package/dist/types/inference/accountBilling.d.ts +2 -2
  110. package/dist/types/inference/catalogue.d.ts +22 -22
  111. package/dist/types/inference/decisions.d.ts +10 -10
  112. package/dist/types/inference/embeddings.d.ts +16 -16
  113. package/dist/types/inference/errors.d.ts +8 -8
  114. package/dist/types/inference/inbox.d.ts +8 -8
  115. package/dist/types/inference/privateAutoExecution.d.ts +6 -6
  116. package/dist/types/inference/providerConnection.d.ts +4 -4
  117. package/dist/types/inference/realtime.d.ts +92 -92
  118. package/dist/types/inference/request.d.ts +48 -48
  119. package/dist/types/inference/streamEvents.d.ts +16 -16
  120. package/dist/types/inference/usage.d.ts +4 -4
  121. package/dist/types/oauth.d.ts +2 -2
  122. package/dist/types/organizations.d.ts +8 -10
  123. package/dist/types/products/access.d.ts +4 -4
  124. package/dist/types/products/billing.d.ts +3 -0
  125. package/dist/types/reputation.d.ts +1 -11
  126. package/dist/types/safety.d.ts +2374 -2
  127. package/dist/types/serviceToken.d.ts +11 -0
  128. package/dist/types/sessionStatus.d.ts +2 -2
  129. package/dist/types/signIn.d.ts +11 -94
  130. package/dist/types/userResponse.d.ts +22 -22
  131. package/dist/types/username.d.ts +1 -96
  132. package/package.json +24 -20
  133. package/dist/cjs/.tsbuildinfo +0 -1
  134. package/dist/esm/.tsbuildinfo +0 -1
  135. package/dist/types/.tsbuildinfo +0 -1
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SIGN_IN_ERROR_CODES = exports.totpBackupCodesResponseSchema = exports.totpReauthRequestSchema = exports.totpConfirmRequestSchema = exports.totpEnrollResponseSchema = exports.signInMethodsSchema = exports.passwordSetRequestSchema = exports.emailReauthProofSchema = exports.reauthProofSchema = exports.reauthEmailStartRequestSchema = exports.REAUTH_ACTIONS = exports.emailSignInChooseRequestSchema = exports.emailSignInChooseAccountSchema = exports.emailSignInAccountOptionSchema = exports.EMAIL_ACCOUNTS_MAX = exports.EMAIL_SIGNIN_CHOICE_TTL_MS = exports.emailSignInPendingSchema = exports.secondFactorRequiredSchema = exports.signUpRequestSchema = exports.secondFactorSignInRequestSchema = exports.passwordSignInRequestSchema = exports.emailSignInLinkResponseSchema = exports.emailSignInLinkRequestSchema = exports.emailSignInCollectRequestSchema = exports.emailSignInConfirmRequestSchema = exports.emailSignInCodeSchema = exports.emailSignInStartResponseSchema = exports.emailSignInStartRequestSchema = exports.secondFactorCodeSchema = exports.newPasswordSchema = exports.passwordInputSchema = exports.signInIdentifierSchema = exports.EMAIL_SIGNIN_LONG_CODE_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET = exports.TOTP_BACKUP_CODE_COUNT = exports.TOTP_PERIOD_SECONDS = exports.TOTP_DIGITS = exports.PASSWORD_MAX_LENGTH = exports.PASSWORD_MIN_LENGTH = exports.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = exports.SIGNIN_SECOND_FACTOR_TTL_MS = exports.EMAIL_SIGNIN_LINK_TTL_MS = void 0;
4
- exports.normalizeEmailSignInCode = normalizeEmailSignInCode;
5
- exports.isSecondFactorRequired = isSecondFactorRequired;
6
- exports.isEmailSignInChooseAccount = isEmailSignInChooseAccount;
3
+ exports.totpBackupCodesResponseSchema = exports.totpReauthRequestSchema = exports.totpConfirmRequestSchema = exports.totpEnrollResponseSchema = exports.signInMethodsSchema = exports.passwordSetRequestSchema = exports.emailReauthProofSchema = exports.reauthProofSchema = exports.reauthEmailStartRequestSchema = exports.emailSignInChooseRequestSchema = exports.emailSignInChooseAccountSchema = exports.emailSignInAccountOptionSchema = exports.emailSignInPendingSchema = exports.secondFactorRequiredSchema = exports.signUpRequestSchema = exports.secondFactorSignInRequestSchema = exports.passwordSignInRequestSchema = exports.emailSignInLinkResponseSchema = exports.emailSignInLinkRequestSchema = exports.emailSignInCollectRequestSchema = exports.emailSignInConfirmRequestSchema = exports.emailSignInCodeSchema = exports.emailSignInStartResponseSchema = exports.emailSignInStartRequestSchema = exports.secondFactorCodeSchema = exports.newPasswordSchema = exports.passwordInputSchema = exports.signInIdentifierSchema = exports.SIGN_IN_ERROR_CODES = exports.REAUTH_ACTIONS = exports.isEmailSignInChooseAccount = exports.isSecondFactorRequired = exports.EMAIL_ACCOUNTS_MAX = exports.EMAIL_SIGNIN_CHOICE_TTL_MS = exports.normalizeEmailSignInCode = exports.EMAIL_SIGNIN_LONG_CODE_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET = exports.TOTP_BACKUP_CODE_COUNT = exports.TOTP_PERIOD_SECONDS = exports.TOTP_DIGITS = exports.PASSWORD_MAX_LENGTH = exports.PASSWORD_MIN_LENGTH = exports.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = exports.SIGNIN_SECOND_FACTOR_TTL_MS = exports.EMAIL_SIGNIN_LINK_TTL_MS = void 0;
7
4
  /**
8
5
  * Signing in: an email code or link, an optional password,
9
6
  * and an optional authenticator app (TOTP) as a second factor.
@@ -38,40 +35,27 @@ exports.isEmailSignInChooseAccount = isEmailSignInChooseAccount;
38
35
  const zod_1 = require("zod");
39
36
  const accountEmail_1 = require("./accountEmail");
40
37
  const deviceSession_1 = require("./deviceSession");
41
- /** How long the link in a sign-in email can be opened. */
42
- exports.EMAIL_SIGNIN_LINK_TTL_MS = 15 * 60 * 1000;
43
- /** How long a second-factor challenge can be answered. */
44
- exports.SIGNIN_SECOND_FACTOR_TTL_MS = 5 * 60 * 1000;
45
- /** Wrong second-factor codes one challenge accepts. */
46
- exports.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = 5;
47
- /** Shortest password Oxy accepts when one is set. */
48
- exports.PASSWORD_MIN_LENGTH = 10;
49
- /** Longest password Oxy accepts (characters). */
50
- exports.PASSWORD_MAX_LENGTH = 256;
51
- /** Digits in an authenticator code. */
52
- exports.TOTP_DIGITS = 6;
53
- /** Seconds each authenticator code lasts. */
54
- exports.TOTP_PERIOD_SECONDS = 30;
55
- /** Backup codes issued when an authenticator is enabled or they are regenerated. */
56
- exports.TOTP_BACKUP_CODE_COUNT = 10;
57
- /**
58
- * The long sign-in code: ten characters of Crockford base32 without the
59
- * look-alikes (no 0, O, 1, I, L, U), shown as `XXXXX-XXXXX`. An account whose
60
- * sign-in codes were guessed at too often today (the per-account ceiling) is
61
- * sent this instead of 6 digits for the rest of the day, so its owner can still
62
- * type a code while guessing one is infeasible.
63
- */
64
- exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET = '23456789ABCDEFGHJKMNPQRSTVWXYZ';
65
- exports.EMAIL_SIGNIN_LONG_CODE_LENGTH = 10;
66
- /**
67
- * Normalise a typed sign-in code: 6 digits stay as they are; a long code is
68
- * upper-cased with its dash and spaces removed. Anything else is returned
69
- * trimmed (and will simply be wrong).
70
- */
71
- function normalizeEmailSignInCode(code) {
72
- const compact = code.trim().replace(/[\s-]/g, '').toUpperCase();
73
- return compact;
74
- }
38
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
39
+ // it is re-exported here so the root entry is unchanged.
40
+ const signIn_1 = require("./client/signIn");
41
+ var signIn_2 = require("./client/signIn");
42
+ Object.defineProperty(exports, "EMAIL_SIGNIN_LINK_TTL_MS", { enumerable: true, get: function () { return signIn_2.EMAIL_SIGNIN_LINK_TTL_MS; } });
43
+ Object.defineProperty(exports, "SIGNIN_SECOND_FACTOR_TTL_MS", { enumerable: true, get: function () { return signIn_2.SIGNIN_SECOND_FACTOR_TTL_MS; } });
44
+ Object.defineProperty(exports, "SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS", { enumerable: true, get: function () { return signIn_2.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS; } });
45
+ Object.defineProperty(exports, "PASSWORD_MIN_LENGTH", { enumerable: true, get: function () { return signIn_2.PASSWORD_MIN_LENGTH; } });
46
+ Object.defineProperty(exports, "PASSWORD_MAX_LENGTH", { enumerable: true, get: function () { return signIn_2.PASSWORD_MAX_LENGTH; } });
47
+ Object.defineProperty(exports, "TOTP_DIGITS", { enumerable: true, get: function () { return signIn_2.TOTP_DIGITS; } });
48
+ Object.defineProperty(exports, "TOTP_PERIOD_SECONDS", { enumerable: true, get: function () { return signIn_2.TOTP_PERIOD_SECONDS; } });
49
+ Object.defineProperty(exports, "TOTP_BACKUP_CODE_COUNT", { enumerable: true, get: function () { return signIn_2.TOTP_BACKUP_CODE_COUNT; } });
50
+ Object.defineProperty(exports, "EMAIL_SIGNIN_LONG_CODE_ALPHABET", { enumerable: true, get: function () { return signIn_2.EMAIL_SIGNIN_LONG_CODE_ALPHABET; } });
51
+ Object.defineProperty(exports, "EMAIL_SIGNIN_LONG_CODE_LENGTH", { enumerable: true, get: function () { return signIn_2.EMAIL_SIGNIN_LONG_CODE_LENGTH; } });
52
+ Object.defineProperty(exports, "normalizeEmailSignInCode", { enumerable: true, get: function () { return signIn_2.normalizeEmailSignInCode; } });
53
+ Object.defineProperty(exports, "EMAIL_SIGNIN_CHOICE_TTL_MS", { enumerable: true, get: function () { return signIn_2.EMAIL_SIGNIN_CHOICE_TTL_MS; } });
54
+ Object.defineProperty(exports, "EMAIL_ACCOUNTS_MAX", { enumerable: true, get: function () { return signIn_2.EMAIL_ACCOUNTS_MAX; } });
55
+ Object.defineProperty(exports, "isSecondFactorRequired", { enumerable: true, get: function () { return signIn_2.isSecondFactorRequired; } });
56
+ Object.defineProperty(exports, "isEmailSignInChooseAccount", { enumerable: true, get: function () { return signIn_2.isEmailSignInChooseAccount; } });
57
+ Object.defineProperty(exports, "REAUTH_ACTIONS", { enumerable: true, get: function () { return signIn_2.REAUTH_ACTIONS; } });
58
+ Object.defineProperty(exports, "SIGN_IN_ERROR_CODES", { enumerable: true, get: function () { return signIn_2.SIGN_IN_ERROR_CODES; } });
75
59
  const sixDigits = zod_1.z
76
60
  .string()
77
61
  .trim()
@@ -84,17 +68,17 @@ const opaqueTokenSchema = accountEmail_1.emailTicketSchema;
84
68
  /** A username or an email. */
85
69
  exports.signInIdentifierSchema = zod_1.z.string().trim().min(1).max(254);
86
70
  /** A password as typed. Its policy applies only when one is set. */
87
- exports.passwordInputSchema = zod_1.z.string().min(1).max(exports.PASSWORD_MAX_LENGTH);
71
+ exports.passwordInputSchema = zod_1.z.string().min(1).max(signIn_1.PASSWORD_MAX_LENGTH);
88
72
  /** A new password. */
89
73
  exports.newPasswordSchema = zod_1.z
90
74
  .string()
91
- .min(exports.PASSWORD_MIN_LENGTH, `password must be at least ${exports.PASSWORD_MIN_LENGTH} characters`)
92
- .max(exports.PASSWORD_MAX_LENGTH, `password must be at most ${exports.PASSWORD_MAX_LENGTH} characters`);
75
+ .min(signIn_1.PASSWORD_MIN_LENGTH, `password must be at least ${signIn_1.PASSWORD_MIN_LENGTH} characters`)
76
+ .max(signIn_1.PASSWORD_MAX_LENGTH, `password must be at most ${signIn_1.PASSWORD_MAX_LENGTH} characters`);
93
77
  /**
94
78
  * An authenticator code (6 digits) or a backup code (10 characters, with or
95
79
  * without its dash). Normalised server-side.
96
80
  */
97
- exports.secondFactorCodeSchema = zod_1.z.string().trim().min(exports.TOTP_DIGITS).max(16);
81
+ exports.secondFactorCodeSchema = zod_1.z.string().trim().min(signIn_1.TOTP_DIGITS).max(16);
98
82
  /** The device-session fields every body that ends in a session may carry. */
99
83
  const sessionEnvelope = {
100
84
  deviceName: zod_1.z.string().trim().min(1).max(120).optional(),
@@ -127,11 +111,11 @@ exports.emailSignInCodeSchema = zod_1.z
127
111
  .string()
128
112
  .trim()
129
113
  .refine((value) => {
130
- const code = normalizeEmailSignInCode(value);
114
+ const code = (0, signIn_1.normalizeEmailSignInCode)(value);
131
115
  return (new RegExp(`^\\d{${accountEmail_1.EMAIL_CODE_LENGTH}}$`).test(code) ||
132
- new RegExp(`^[${exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET}]{${exports.EMAIL_SIGNIN_LONG_CODE_LENGTH}}$`).test(code));
116
+ new RegExp(`^[${signIn_1.EMAIL_SIGNIN_LONG_CODE_ALPHABET}]{${signIn_1.EMAIL_SIGNIN_LONG_CODE_LENGTH}}$`).test(code));
133
117
  }, {
134
- message: `code must be ${accountEmail_1.EMAIL_CODE_LENGTH} digits or the ${exports.EMAIL_SIGNIN_LONG_CODE_LENGTH}-character code from the email`,
118
+ message: `code must be ${accountEmail_1.EMAIL_CODE_LENGTH} digits or the ${signIn_1.EMAIL_SIGNIN_LONG_CODE_LENGTH}-character code from the email`,
135
119
  });
136
120
  /** `POST /auth/signin/email/confirm` — the code from the email. */
137
121
  exports.emailSignInConfirmRequestSchema = zod_1.z
@@ -199,13 +183,6 @@ exports.emailSignInPendingSchema = zod_1.z.object({
199
183
  status: zod_1.z.literal('pending'),
200
184
  expiresAt: zod_1.z.number().int().positive(),
201
185
  });
202
- /**
203
- * How long, once the email is proven, the person has to choose which of its
204
- * accounts signs in.
205
- */
206
- exports.EMAIL_SIGNIN_CHOICE_TTL_MS = 5 * 60 * 1000;
207
- /** Most accounts one email can hold; sign-up refuses another past it. */
208
- exports.EMAIL_ACCOUNTS_MAX = 20;
209
186
  exports.emailSignInAccountOptionSchema = zod_1.z.object({
210
187
  id: zod_1.z.string().min(1).max(64),
211
188
  username: zod_1.z.string().min(1).optional(),
@@ -214,7 +191,7 @@ exports.emailSignInAccountOptionSchema = zod_1.z.object({
214
191
  });
215
192
  exports.emailSignInChooseAccountSchema = zod_1.z.object({
216
193
  chooseAccount: zod_1.z.literal(true),
217
- accounts: zod_1.z.array(exports.emailSignInAccountOptionSchema).min(2).max(exports.EMAIL_ACCOUNTS_MAX),
194
+ accounts: zod_1.z.array(exports.emailSignInAccountOptionSchema).min(2).max(signIn_1.EMAIL_ACCOUNTS_MAX),
218
195
  expiresAt: zod_1.z.number().int().positive(),
219
196
  });
220
197
  /** `POST /auth/signin/email/choose` — the account, from those {@link EmailSignInChooseAccount} listed. */
@@ -226,27 +203,8 @@ exports.emailSignInChooseRequestSchema = zod_1.z
226
203
  ...sessionEnvelope,
227
204
  })
228
205
  .strict();
229
- /** Narrow a {@link SignInStepResult}. */
230
- function isSecondFactorRequired(result) {
231
- return result.secondFactorRequired === true;
232
- }
233
- /** Narrow an {@link EmailSignInStepResult}. */
234
- function isEmailSignInChooseAccount(result) {
235
- return result.chooseAccount === true;
236
- }
237
- /**
238
- * What a re-verification email code confirms. The code is bound to it: a code
239
- * asked for one change never authorises another, and the email names it.
240
- */
241
- exports.REAUTH_ACTIONS = [
242
- 'change_password',
243
- 'totp',
244
- 'link_commons',
245
- 'delete_account',
246
- 'credentials_manage',
247
- ];
248
206
  /** `POST /users/me/reauth/email` */
249
- exports.reauthEmailStartRequestSchema = zod_1.z.object({ action: zod_1.z.enum(exports.REAUTH_ACTIONS) }).strict();
207
+ exports.reauthEmailStartRequestSchema = zod_1.z.object({ action: zod_1.z.enum(signIn_1.REAUTH_ACTIONS) }).strict();
250
208
  /**
251
209
  * A fresh proof that the person, not only their session, is asking: the
252
210
  * current password, or a code just sent to the account's email
@@ -307,7 +265,7 @@ exports.totpConfirmRequestSchema = zod_1.z
307
265
  code: zod_1.z
308
266
  .string()
309
267
  .trim()
310
- .regex(new RegExp(`^\\d{${exports.TOTP_DIGITS}}$`)),
268
+ .regex(new RegExp(`^\\d{${signIn_1.TOTP_DIGITS}}$`)),
311
269
  reauth: exports.reauthProofSchema,
312
270
  })
313
271
  .strict();
@@ -316,41 +274,5 @@ exports.totpReauthRequestSchema = zod_1.z.object({ reauth: exports.reauthProofSc
316
274
  exports.totpBackupCodesResponseSchema = zod_1.z.object({
317
275
  backupCodes: zod_1.z
318
276
  .array(zod_1.z.string().regex(/^[a-z2-9]{5}-[a-z2-9]{5}$/))
319
- .length(exports.TOTP_BACKUP_CODE_COUNT),
277
+ .length(signIn_1.TOTP_BACKUP_CODE_COUNT),
320
278
  });
321
- /**
322
- * Stable error codes (`error.code` in the API error body). Clients map these
323
- * through their localization, never the English message.
324
- */
325
- exports.SIGN_IN_ERROR_CODES = {
326
- /** Wrong identifier or password — never says which. */
327
- invalidCredentials: 'SIGNIN_INVALID_CREDENTIALS',
328
- /** The request is unknown, expired, spent, or its secret is wrong. */
329
- requestInvalid: 'SIGNIN_REQUEST_INVALID',
330
- /** The link is unknown, expired or already used. */
331
- linkInvalid: 'SIGNIN_LINK_INVALID',
332
- /** The link was opened in another browser: type the code in the app instead. */
333
- linkOtherDevice: 'SIGNIN_LINK_OTHER_DEVICE',
334
- /** The second-factor challenge or its code is wrong, expired or spent. */
335
- secondFactorInvalid: 'SECOND_FACTOR_INVALID',
336
- /** Too many failures for this account: wait and try again. */
337
- locked: 'SIGNIN_LOCKED',
338
- /** The step needs a fresh proof (`reauth`). */
339
- reauthRequired: 'REAUTH_REQUIRED',
340
- /** The fresh proof was wrong. */
341
- reauthInvalid: 'REAUTH_INVALID',
342
- /** The account has an authenticator: the proof must carry its code. */
343
- totpRequired: 'TOTP_REQUIRED',
344
- /** Enrolling or confirming an authenticator that is already on. */
345
- totpAlreadyEnabled: 'TOTP_ALREADY_ENABLED',
346
- /** Disabling, confirming or regenerating codes with no authenticator (or no pending one). */
347
- totpNotEnabled: 'TOTP_NOT_ENABLED',
348
- /** The code the authenticator showed at enrolment is wrong. */
349
- totpCodeInvalid: 'TOTP_CODE_INVALID',
350
- /** Only official Oxy apps and auth.oxy.so may sign people in here. */
351
- originNotAllowed: 'SIGNIN_ORIGIN_NOT_ALLOWED',
352
- /** The username is taken. */
353
- usernameTaken: 'USERNAME_TAKEN',
354
- /** The email already holds `EMAIL_ACCOUNTS_MAX` accounts: no sign-up adds another. */
355
- emailAccountsFull: 'EMAIL_ACCOUNTS_FULL',
356
- };
@@ -100,106 +100,25 @@
100
100
  * knows what is taken, and it must not pretend to.
101
101
  */
102
102
  Object.defineProperty(exports, "__esModule", { value: true });
103
- exports.botUsernameSchema = exports.BOT_USERNAME_INVALID_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.usernameSchema = exports.USERNAME_PATTERN_SOURCE = exports.NUMERIC_USERNAME_MESSAGE = exports.RESERVED_USERNAME_MESSAGE = exports.RESERVED_USERNAMES = exports.USERNAME_INVALID_MESSAGE = exports.USERNAME_MAX_LENGTH = exports.USERNAME_MIN_LENGTH = void 0;
103
+ exports.botUsernameSchema = exports.usernameSchema = exports.applyBotUsernameSuffix = exports.BOT_USERNAME_INVALID_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.stripDisallowedUsernameCharacters = exports.USERNAME_PATTERN_SOURCE = exports.NUMERIC_USERNAME_MESSAGE = exports.RESERVED_USERNAME_MESSAGE = exports.RESERVED_USERNAMES = exports.USERNAME_INVALID_MESSAGE = exports.USERNAME_MAX_LENGTH = exports.USERNAME_MIN_LENGTH = void 0;
104
104
  exports.isValidUsername = isValidUsername;
105
- exports.stripDisallowedUsernameCharacters = stripDisallowedUsernameCharacters;
106
105
  exports.usernameSchemaForAccountKind = usernameSchemaForAccountKind;
107
- exports.applyBotUsernameSuffix = applyBotUsernameSuffix;
108
106
  const zod_1 = require("zod");
109
- /** Shortest storable handle. `oxy` sets the floor. */
110
- exports.USERNAME_MIN_LENGTH = 3;
111
- /** Longest storable handle, and the `maxLength` an input field should carry. */
112
- exports.USERNAME_MAX_LENGTH = 30;
113
- /** The 400 / inline-validation copy for every path that rejects a handle. */
114
- exports.USERNAME_INVALID_MESSAGE = 'Username must be 3-30 characters, use only letters, numbers, hyphens and underscores, ' +
115
- 'start and end with a letter or number, and never repeat a separator';
116
- /**
117
- * Exact-match handles nobody may newly claim, regardless of account kind.
118
- *
119
- * This is NOT a namespace tightening like the bot suffix — it is a list of
120
- * names withheld from `users_lower_username_key` before anybody asks for
121
- * them. Compared against the same normalization the unique index applies
122
- * (`lower(btrim(username))`), so `Admin`, ` ADMIN ` and `admin` are the one
123
- * name this list means.
124
- *
125
- * `oxy`, `mention`, `homiio`, `clarity`, `faircoin`, `astro` and `mercaria`
126
- * are deliberately NOT here: each already has an `organization`/`project`
127
- * row, so the unique index already refuses a second one, and `oxy` is the
128
- * name `USERNAME_MIN_LENGTH` is pinned against in
129
- * `__tests__/username.test.ts` — listing it would make
130
- * `usernameSchema.safeParse('oxy')` fail and falsify that comment. A brand
131
- * with no account of its own yet (measured 2026-09-18: Alia, Allo, TNP,
132
- * Kaana, Bloom) has no row to protect it, so it is listed until one exists.
133
- */
134
- exports.RESERVED_USERNAMES = new Set([
135
- // Oxy product lines with no account row of their own yet.
136
- 'alia',
137
- 'allo',
138
- 'tnp',
139
- 'kaana',
140
- 'bloom',
141
- // System / role words a signup impersonating staff or Oxy itself would reach for.
142
- 'admin',
143
- 'administrator',
144
- 'root',
145
- 'superadmin',
146
- 'super',
147
- 'superuser',
148
- 'support',
149
- 'staff',
150
- 'moderator',
151
- 'mod',
152
- 'security',
153
- 'system',
154
- 'official',
155
- 'help',
156
- 'noreply',
157
- 'anonymous',
158
- 'everyone',
159
- 'owner',
160
- ]);
161
- /** The 400 / inline-validation copy for a handle on {@link RESERVED_USERNAMES}. */
162
- exports.RESERVED_USERNAME_MESSAGE = 'This username is reserved and cannot be registered';
163
- /**
164
- * {@link RESERVED_USERNAMES}, minus `alia`, checked against each `-`/`_`
165
- * separated SEGMENT of a candidate rather than the whole string — so
166
- * `official-oxy`, `super-admin` and `team-kaana` are refused the same as the
167
- * bare words, without banning every word that merely CONTAINS one as a
168
- * substring (`superman`, `modern`, `grassroot`, `homeowner` all stay legal:
169
- * none of them separates the reserved word from the rest with `-` or `_`).
170
- *
171
- * `alia` is excluded because `alia-` is the live internal-cost-centre
172
- * namespace: `alia-production-chat` is a minted account and
173
- * `alia-research` / `alia-voice` / `alia-evaluations` are pinned as legal
174
- * slugs in `__tests__/username.test.ts` and `internalCostCenterSpecs.test.ts`.
175
- * Segment-matching `alia` would refuse all four. The bare word `alia` is
176
- * still refused — {@link RESERVED_USERNAMES} above catches it exactly.
177
- */
178
- const RESERVED_USERNAME_SEGMENTS = new Set([...exports.RESERVED_USERNAMES].filter((word) => word !== 'alia'));
179
- /** The 400 / inline-validation copy for a handle that is only digits. */
180
- exports.NUMERIC_USERNAME_MESSAGE = 'Username cannot be only numbers';
181
- /**
182
- * Alphanumeric runs joined by single separators, as a SOURCE string.
183
- *
184
- * A string rather than a literal because the OpenAPI docblocks that publish this
185
- * rule (`POST /auth/register`, `PUT /users/:userId`) must quote it verbatim, and
186
- * `usernamePolicySingleSource.test.ts` compares them against THIS constant. A
187
- * published `pattern:` that drifts from the enforced rule is a lie told to every
188
- * client that generates from the spec, and it is exactly the kind of copy nobody
189
- * notices going stale.
190
- *
191
- * Deliberately NOT re-exported from the package barrel: it exists for the
192
- * schema below and for that one gate. Anything validating a username uses
193
- * {@link usernameSchema}, so there is no second way to ask the question.
194
- *
195
- * Written as an unambiguous alternation rather than a lookahead: every character
196
- * belongs to exactly one branch, so matching is linear and there is no
197
- * backtracking to bound. It also carries no `\p{…}` property escape, which
198
- * mobile Hermes throws on at runtime — this module is reachable from every React
199
- * Native consumer.
200
- */
201
- exports.USERNAME_PATTERN_SOURCE = '^[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*$';
202
- const USERNAME_PATTERN = new RegExp(exports.USERNAME_PATTERN_SOURCE);
107
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
108
+ // it is re-exported here so the root entry is unchanged.
109
+ const username_1 = require("./client/username");
110
+ var username_2 = require("./client/username");
111
+ Object.defineProperty(exports, "USERNAME_MIN_LENGTH", { enumerable: true, get: function () { return username_2.USERNAME_MIN_LENGTH; } });
112
+ Object.defineProperty(exports, "USERNAME_MAX_LENGTH", { enumerable: true, get: function () { return username_2.USERNAME_MAX_LENGTH; } });
113
+ Object.defineProperty(exports, "USERNAME_INVALID_MESSAGE", { enumerable: true, get: function () { return username_2.USERNAME_INVALID_MESSAGE; } });
114
+ Object.defineProperty(exports, "RESERVED_USERNAMES", { enumerable: true, get: function () { return username_2.RESERVED_USERNAMES; } });
115
+ Object.defineProperty(exports, "RESERVED_USERNAME_MESSAGE", { enumerable: true, get: function () { return username_2.RESERVED_USERNAME_MESSAGE; } });
116
+ Object.defineProperty(exports, "NUMERIC_USERNAME_MESSAGE", { enumerable: true, get: function () { return username_2.NUMERIC_USERNAME_MESSAGE; } });
117
+ Object.defineProperty(exports, "USERNAME_PATTERN_SOURCE", { enumerable: true, get: function () { return username_2.USERNAME_PATTERN_SOURCE; } });
118
+ Object.defineProperty(exports, "stripDisallowedUsernameCharacters", { enumerable: true, get: function () { return username_2.stripDisallowedUsernameCharacters; } });
119
+ Object.defineProperty(exports, "BOT_USERNAME_SUFFIX", { enumerable: true, get: function () { return username_2.BOT_USERNAME_SUFFIX; } });
120
+ Object.defineProperty(exports, "BOT_USERNAME_INVALID_MESSAGE", { enumerable: true, get: function () { return username_2.BOT_USERNAME_INVALID_MESSAGE; } });
121
+ Object.defineProperty(exports, "applyBotUsernameSuffix", { enumerable: true, get: function () { return username_2.applyBotUsernameSuffix; } });
203
122
  /**
204
123
  * The one username policy, as a schema.
205
124
  *
@@ -212,14 +131,14 @@ const USERNAME_PATTERN = new RegExp(exports.USERNAME_PATTERN_SOURCE);
212
131
  exports.usernameSchema = zod_1.z
213
132
  .string()
214
133
  .trim()
215
- .min(exports.USERNAME_MIN_LENGTH, exports.USERNAME_INVALID_MESSAGE)
216
- .max(exports.USERNAME_MAX_LENGTH, exports.USERNAME_INVALID_MESSAGE)
217
- .regex(USERNAME_PATTERN, exports.USERNAME_INVALID_MESSAGE)
218
- .refine((username) => !/^[0-9]+$/.test(username), exports.NUMERIC_USERNAME_MESSAGE)
219
- .refine((username) => !exports.RESERVED_USERNAMES.has(username.toLowerCase()), exports.RESERVED_USERNAME_MESSAGE)
134
+ .min(username_1.USERNAME_MIN_LENGTH, username_1.USERNAME_INVALID_MESSAGE)
135
+ .max(username_1.USERNAME_MAX_LENGTH, username_1.USERNAME_INVALID_MESSAGE)
136
+ .regex(username_1.USERNAME_PATTERN, username_1.USERNAME_INVALID_MESSAGE)
137
+ .refine((username) => !/^[0-9]+$/.test(username), username_1.NUMERIC_USERNAME_MESSAGE)
138
+ .refine((username) => !username_1.RESERVED_USERNAMES.has(username.toLowerCase()), username_1.RESERVED_USERNAME_MESSAGE)
220
139
  .refine((username) => !username
221
140
  .split(/[-_]/)
222
- .some((segment) => RESERVED_USERNAME_SEGMENTS.has(segment.toLowerCase())), exports.RESERVED_USERNAME_MESSAGE);
141
+ .some((segment) => username_1.RESERVED_USERNAME_SEGMENTS.has(segment.toLowerCase())), username_1.RESERVED_USERNAME_MESSAGE);
223
142
  /**
224
143
  * Whether a candidate handle is storable — the boolean form, for input surfaces
225
144
  * that show a message as somebody types rather than throwing.
@@ -230,36 +149,6 @@ exports.usernameSchema = zod_1.z
230
149
  function isValidUsername(candidate) {
231
150
  return exports.usernameSchema.safeParse(candidate).success;
232
151
  }
233
- /**
234
- * Drop the characters the policy forbids, for an input field that filters
235
- * keystrokes.
236
- *
237
- * This is a TYPING aid and nothing else — the result still has to pass
238
- * {@link usernameSchema}, which is what decides. It does not lower-case (case is
239
- * preserved, see the header) and it cannot repair a name: a value that is too
240
- * short, edge-separated or doubly-separated comes back unchanged and fails
241
- * validation with a message, which is the outcome the coercing rules this
242
- * replaces used to hide.
243
- */
244
- function stripDisallowedUsernameCharacters(input) {
245
- return input.replace(/[^A-Za-z0-9_-]/g, '');
246
- }
247
- /**
248
- * The label a bot's handle ends in — the ONE exception the policy carries.
249
- *
250
- * Lower-case here because it is what a generator appends; the comparison that
251
- * enforces it folds case, so `MyBot` satisfies it just as `mybot` does.
252
- *
253
- * Deliberately NOT re-exported from the package barrel, for the same reason
254
- * {@link USERNAME_PATTERN_SOURCE} is not: it exists for the schema and the
255
- * generator aid below. A consumer that wants to SAY the rule uses
256
- * {@link BOT_USERNAME_INVALID_MESSAGE}, which already quotes it, and one that
257
- * wants to APPLY it uses {@link applyBotUsernameSuffix} — so there is no second
258
- * way to spell the check.
259
- */
260
- exports.BOT_USERNAME_SUFFIX = 'bot';
261
- /** The 400 / inline-validation copy for a bot handle that carries no label. */
262
- exports.BOT_USERNAME_INVALID_MESSAGE = `A bot account's username must end in "${exports.BOT_USERNAME_SUFFIX}"`;
263
152
  /**
264
153
  * The username policy for an account of kind `bot`: everything above, plus a
265
154
  * handle that ends in {@link BOT_USERNAME_SUFFIX}.
@@ -317,7 +206,7 @@ exports.BOT_USERNAME_INVALID_MESSAGE = `A bot account's username must end in "${
317
206
  * `community-maestro`, `community-pulse`, `garden-helper`, `luna`, `verity`).
318
207
  * They go on loading and resolving; only a NEW handle is held to this.
319
208
  */
320
- exports.botUsernameSchema = exports.usernameSchema.refine((username) => username.toLowerCase().endsWith(exports.BOT_USERNAME_SUFFIX), exports.BOT_USERNAME_INVALID_MESSAGE);
209
+ exports.botUsernameSchema = exports.usernameSchema.refine((username) => username.toLowerCase().endsWith(username_1.BOT_USERNAME_SUFFIX), username_1.BOT_USERNAME_INVALID_MESSAGE);
321
210
  /**
322
211
  * The policy that governs a handle, given the kind of account that will hold it.
323
212
  *
@@ -334,27 +223,3 @@ exports.botUsernameSchema = exports.usernameSchema.refine((username) => username
334
223
  function usernameSchemaForAccountKind(kind) {
335
224
  return kind === 'bot' ? exports.botUsernameSchema : exports.usernameSchema;
336
225
  }
337
- /**
338
- * Label a proposed bot handle, so a generator PROPOSES something the server will
339
- * accept.
340
- *
341
- * Alia builds an agent's handle from its display name; without this it would
342
- * propose `garden-helper` for a bot and be 400ed on submit, which is the same
343
- * "suggest, then refuse" defect the minimum length already caused once.
344
- *
345
- * It appends and never inserts: a separator is the caller's choice, so
346
- * `garden-helper` becomes `garden-helperbot` while a caller who typed
347
- * `garden-helper-` gets `garden-helper-bot`. It truncates to leave room rather
348
- * than overflowing {@link USERNAME_MAX_LENGTH}.
349
- *
350
- * Like {@link stripDisallowedUsernameCharacters} it is an aid, not a decision: it
351
- * cannot repair a handle the base policy refuses, and the result still has to
352
- * pass {@link botUsernameSchema}.
353
- */
354
- function applyBotUsernameSuffix(candidate) {
355
- const trimmed = candidate.trim();
356
- if (trimmed.toLowerCase().endsWith(exports.BOT_USERNAME_SUFFIX)) {
357
- return trimmed;
358
- }
359
- return trimmed.slice(0, exports.USERNAME_MAX_LENGTH - exports.BOT_USERNAME_SUFFIX.length) + exports.BOT_USERNAME_SUFFIX;
360
- }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Wire contract for `GET /internal/accounts/:accountId/access/:userId`: Oxy
3
+ * answers a trusted first-party service's question "may this person act FOR
4
+ * that account, and with what?" with no session of that person in the request.
5
+ *
6
+ * It exists for the account Oxy never lets anybody act AS: a `channel`
7
+ * ({@link isDelegatedActAsEligibleKind} refuses it). An app publishes to a
8
+ * channel with ITS OWN credential, on behalf of a member Oxy vouches for here,
9
+ * so nobody holds a bearer that speaks as the channel. A long-running import
10
+ * (Oxy Move) has only a service token and `X-Oxy-User-Id`, which the
11
+ * user-facing `GET /accounts/:id/members` refuses; this is its answer.
12
+ *
13
+ * Needs a service token whose application is platform-trusted AND holds the
14
+ * privileged {@link ACCOUNTS_MEMBERSHIP_READ_SCOPE}. The answer is always 200:
15
+ * `access` is `null` for no active membership, an archived account, an unknown
16
+ * account and an unclaimed directory account alike. Callers must treat every
17
+ * other status as a refusal (fail closed), and must not cache the answer longer
18
+ * than the action it authorizes.
19
+ *
20
+ * Platform-agnostic — zod only.
21
+ */
22
+ import { z } from 'zod';
23
+ import { accountKindSchema } from './accountGraph.js';
24
+ /**
25
+ * The scope the endpoint requires.
26
+ *
27
+ * Privileged (staff-granted). Channel membership is sensitive because a channel
28
+ * is anonymous: its members are the people behind a voice that is published
29
+ * without their names, and "is this person a member of that channel" is
30
+ * exactly the fact the channel exists to withhold.
31
+ */
32
+ export const ACCOUNTS_MEMBERSHIP_READ_SCOPE = 'accounts:membership:read';
33
+ /**
34
+ * Where the access comes from.
35
+ *
36
+ * - `self` — the person IS the account (a personal account asked about itself).
37
+ * - `direct` — an active membership row on the account.
38
+ * - `inherited` — an active, cascading membership on an ancestor account.
39
+ */
40
+ export const ACCOUNT_ACCESS_SOURCES = ['self', 'direct', 'inherited'];
41
+ export const accountAccessSourceSchema = z.enum(ACCOUNT_ACCESS_SOURCES);
42
+ /**
43
+ * One person's EFFECTIVE access over one account.
44
+ *
45
+ * `role` and `permissions` are strings rather than closed enums on purpose: the
46
+ * role and permission vocabularies are the API's, they grow, and a consumer
47
+ * pinned to an older contracts release must keep reading new ones. Gate on
48
+ * `permissions` (the role's baseline adjusted by per-member grants and
49
+ * revokes), never on `role`.
50
+ */
51
+ export const accountAccessGrantSchema = z.object({
52
+ role: z.string().min(1),
53
+ permissions: z.array(z.string().min(1)),
54
+ /** Only an active membership grants access, so this is the one value. */
55
+ status: z.literal('active'),
56
+ source: accountAccessSourceSchema,
57
+ });
58
+ export const accountAccessVerdictSchema = z
59
+ .object({
60
+ accountId: z.string().min(1),
61
+ /** `null` when no such account exists. */
62
+ kind: accountKindSchema.nullable(),
63
+ /** `null` means: this person may not act for this account. */
64
+ access: accountAccessGrantSchema.nullable(),
65
+ })
66
+ .refine((verdict) => verdict.access === null || verdict.kind !== null, {
67
+ message: 'An account that does not exist grants no access',
68
+ path: ['access'],
69
+ });
@@ -13,22 +13,10 @@
13
13
  * (`reauth`) use the same code, through their own routes (`signIn.ts`).
14
14
  */
15
15
  import { z } from 'zod';
16
- /**
17
- * - `signup`: above.
18
- * - `signin`: the code (and link) of an email sign-in (`POST /auth/signin/email/start`).
19
- * - `reauth`: a signed-in person proving it is them before a sensitive step
20
- * (`POST /users/me/reauth/email`): a password, an authenticator, deleting the
21
- * account, linking Commons.
22
- */
23
- export const EMAIL_VERIFICATION_PURPOSES = ['signup', 'signin', 'reauth'];
24
- /** Digits in a code. */
25
- export const EMAIL_CODE_LENGTH = 6;
26
- /** How long a code can be confirmed. */
27
- export const EMAIL_CODE_TTL_MS = 10 * 60 * 1000;
28
- /** Wrong codes before a verification is spent and a new code is needed. */
29
- export const EMAIL_CODE_MAX_ATTEMPTS = 5;
30
- /** How long a confirmed code's ticket can be spent. */
31
- export const EMAIL_TICKET_TTL_MS = 15 * 60 * 1000;
16
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
17
+ // it is re-exported here so the root entry is unchanged.
18
+ import { EMAIL_CODE_LENGTH } from './client/accountEmail.js';
19
+ export { EMAIL_VERIFICATION_PURPOSES, EMAIL_CODE_LENGTH, EMAIL_CODE_TTL_MS, EMAIL_CODE_MAX_ATTEMPTS, EMAIL_TICKET_TTL_MS, EMAIL_VERIFICATION_ERROR_CODES, } from './client/accountEmail.js';
32
20
  /** An email address as the API stores it: trimmed and lowercase. */
33
21
  export const emailAddressSchema = z.string().trim().toLowerCase().min(3).max(254).email();
34
22
  /** An opaque one-use ticket (32 random bytes, base64url). */
@@ -58,19 +46,3 @@ export const emailVerificationConfirmResponseSchema = z.object({
58
46
  ticket: emailTicketSchema,
59
47
  expiresAt: z.number().int().positive(),
60
48
  });
61
- /**
62
- * Stable error codes (`error.code` in the API error body). Clients map these
63
- * through their localization, never the English message.
64
- */
65
- export const EMAIL_VERIFICATION_ERROR_CODES = {
66
- /** The code is wrong, or its verification expired or was spent. */
67
- codeInvalid: 'EMAIL_CODE_INVALID',
68
- /** Too many wrong codes: request a new one. */
69
- tooManyAttempts: 'EMAIL_CODE_TOO_MANY_ATTEMPTS',
70
- /** The ticket is unknown, expired, spent, or for another email or purpose. */
71
- ticketInvalid: 'EMAIL_TICKET_INVALID',
72
- /** A sign-up without a confirmed email. */
73
- ticketRequired: 'EMAIL_TICKET_REQUIRED',
74
- /** This server cannot send mail. */
75
- unavailable: 'EMAIL_UNAVAILABLE',
76
- };