@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
@@ -41,6 +41,7 @@
41
41
  import { z } from 'zod';
42
42
  import { userNameSchema } from './userResponse.js';
43
43
  import { reputationConductSchema, reputationContextualInfluenceSchema, reputationContributionSchema, reputationPersonhoodSchema, reputationReportingSchema, reputationReviewingSchema, } from './moderationReputation.js';
44
+ export { isFullReputationBalance } from './client/reputation.js';
44
45
  /* -------------------------------------------------------------------------- */
45
46
  /* Closed value sets */
46
47
  /* -------------------------------------------------------------------------- */
@@ -170,38 +171,6 @@ export const reputationBalanceSchema = z.object({
170
171
  reviewing: reputationReviewingSchema.optional(),
171
172
  contextualInfluence: reputationContextualInfluenceSchema.optional(),
172
173
  });
173
- /**
174
- * The fields the full {@link ReputationBalance} carries beyond the public
175
- * {@link ReputationBalanceSummary} that the API sends ALL-OR-NOTHING. The
176
- * runtime discriminant between the two views.
177
- *
178
- * The V2 blocks (`conduct`, `contribution`, …) are deliberately NOT listed:
179
- * they are optional on the wire, so requiring them here would make a balance
180
- * from a server that predates them fail to narrow, hiding the whole private
181
- * view. Read a V2 block by checking that block.
182
- */
183
- const FULL_BALANCE_FIELDS = [
184
- 'positive',
185
- 'negative',
186
- 'breakdown',
187
- 'influence',
188
- 'reliability',
189
- 'recalculatedAt',
190
- 'updatedAt',
191
- ];
192
- /**
193
- * Whether a balance came back as the SUBJECT view, and so carries the
194
- * breakdown / influence / reliability blocks.
195
- *
196
- * Checks every extra field rather than one representative: the point of the
197
- * guard is that the caller then dereferences those blocks, so a partial payload
198
- * must not narrow.
199
- *
200
- * @param balance - A balance from `getReputationBalance`.
201
- */
202
- export function isFullReputationBalance(balance) {
203
- return FULL_BALANCE_FIELDS.every((field) => field in balance);
204
- }
205
174
  export const reputationRuleSchema = z.object({
206
175
  actionType: z.string(),
207
176
  points: z.number(),
@@ -1,4 +1,12 @@
1
1
  import { z } from 'zod';
2
+ import { accountActorChainSchema } from './accountSubject.js';
3
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
4
+ // its vocabularies are re-exported here so the root entry is unchanged.
5
+ import { PURCHASE_APPROVAL_STATUSES, SAFETY_POLICY_VERSION, SUPERVISED_ACTIONS, SUPERVISION_CONSENT_VERSION, SUPERVISION_INVITATION_STATUSES, SUPERVISION_STATUSES, } from './client/safety.js';
6
+ import { ORGANIZATION_TYPES } from './organizations.js';
7
+ import { reauthProofSchema } from './signIn.js';
8
+ import { userResponseSchema } from './userResponse.js';
9
+ export { PURCHASE_APPROVAL_STATUSES, SAFETY_POLICY_VERSION, SUPERVISED_ACTIONS, SUPERVISION_CONSENT_VERSION, SUPERVISION_INVITATION_STATUSES, SUPERVISION_STATUSES, };
2
10
  /** The requester is authenticated by the bearer; applications cannot submit viewer IDs. */
3
11
  export const profileAccessInputSchema = z
4
12
  .object({ subjectAccountId: z.string().min(1).max(128) })
@@ -10,3 +18,418 @@ export const profileAccessDecisionSchema = z
10
18
  policyVersion: z.literal('1'),
11
19
  })
12
20
  .strict();
21
+ export const SAFETY_DECISION_REASONS = [
22
+ 'allowed',
23
+ 'owner',
24
+ 'public',
25
+ 'private',
26
+ 'blocked',
27
+ 'unavailable',
28
+ 'unsupported_consumer',
29
+ 'contact_not_approved',
30
+ 'mature_content',
31
+ 'unrated_content',
32
+ 'approval_required',
33
+ ];
34
+ const accountId = z.string().min(1).max(128);
35
+ const resourceId = z.string().trim().min(1).max(256);
36
+ const uniqueIds = (max, min = 1) => z
37
+ .array(accountId)
38
+ .min(min)
39
+ .max(max)
40
+ .refine((ids) => new Set(ids).size === ids.length, 'Account IDs must be unique');
41
+ export const safetyDecisionSchema = z
42
+ .object({
43
+ allowed: z.boolean(),
44
+ reason: z.enum(SAFETY_DECISION_REASONS),
45
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
46
+ })
47
+ .strict();
48
+ export const profileAccessBatchInputSchema = z
49
+ .object({ subjectAccountIds: uniqueIds(100) })
50
+ .strict();
51
+ export const profileAccessBatchResponseSchema = z
52
+ .object({
53
+ items: z
54
+ .array(z
55
+ .object({
56
+ subjectAccountId: accountId,
57
+ decision: safetyDecisionSchema,
58
+ })
59
+ .strict())
60
+ .max(100),
61
+ })
62
+ .strict();
63
+ export const MESSAGE_ACCESS_MAX_RESOURCES = 100;
64
+ export const MESSAGE_ACCESS_MAX_PARTICIPANTS = 1024;
65
+ export const MESSAGE_ACCESS_MAX_SUPERVISED_PARTICIPANTS = 256;
66
+ export const MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS = 4096;
67
+ /** Leaves room for the original requester proof in a 512 KiB request. */
68
+ export const MESSAGE_ACCESS_MAX_INPUT_BYTES = 448 * 1024;
69
+ export const messageAccessInputSchema = z
70
+ .object({ participantAccountIds: uniqueIds(MESSAGE_ACCESS_MAX_PARTICIPANTS), resourceId })
71
+ .strict();
72
+ export const contactAccessInputSchema = z.object({ targetAccountIds: uniqueIds(1000) }).strict();
73
+ export const contactAccessResponseSchema = z
74
+ .object({
75
+ items: z
76
+ .array(z
77
+ .object({
78
+ targetAccountId: accountId,
79
+ decision: safetyDecisionSchema,
80
+ })
81
+ .strict())
82
+ .max(1000),
83
+ })
84
+ .strict();
85
+ export const CONTENT_CLASSIFICATIONS = ['general', 'mature', 'unrated'];
86
+ export const contentAccessInputSchema = z
87
+ .object({
88
+ resourceId,
89
+ classification: z.enum(CONTENT_CLASSIFICATIONS),
90
+ authorAccountId: accountId.optional(),
91
+ audience: z.enum(['public', 'private', 'followers']).optional(),
92
+ applicationAudienceAllowed: z.boolean().optional(),
93
+ applicationResourceGrant: z.boolean().optional(),
94
+ operation: z.enum(['read', 'manage_own']).optional(),
95
+ })
96
+ .strict()
97
+ .refine((item) => !item.audience ||
98
+ item.audience === 'public' ||
99
+ (!!item.authorAccountId && item.applicationAudienceAllowed !== undefined), 'Nonpublic content requires its authoritative author and application audience decision')
100
+ .refine((item) => item.operation !== 'manage_own' || !!item.authorAccountId, 'Own management requires its authoritative author');
101
+ export const publicationPolicySchema = z
102
+ .object({
103
+ publicationAllowed: z.boolean(),
104
+ requiredAudience: z.literal('private').nullable(),
105
+ collaborationAllowed: z.boolean(),
106
+ federationAllowed: z.boolean(),
107
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
108
+ })
109
+ .strict();
110
+ export const publicationPoliciesInputSchema = z
111
+ .object({
112
+ authorAccountIds: z
113
+ .array(accountId)
114
+ .min(1)
115
+ .max(100)
116
+ .refine((ids) => new Set(ids).size === ids.length, 'Author IDs must be unique'),
117
+ })
118
+ .strict();
119
+ export const publicationPoliciesResponseSchema = z
120
+ .object({
121
+ items: z
122
+ .array(z.object({ authorAccountId: accountId, policy: publicationPolicySchema }).strict())
123
+ .max(100),
124
+ })
125
+ .strict();
126
+ export const contentAccessBatchInputSchema = z
127
+ .object({
128
+ items: z
129
+ .array(contentAccessInputSchema)
130
+ .min(1)
131
+ .max(100)
132
+ .refine((items) => new Set(items.map((item) => item.resourceId)).size === items.length, 'Resource IDs must be unique'),
133
+ })
134
+ .strict();
135
+ export const contentAccessBatchResponseSchema = z
136
+ .object({
137
+ items: z
138
+ .array(z
139
+ .object({
140
+ resourceId,
141
+ decision: safetyDecisionSchema,
142
+ })
143
+ .strict())
144
+ .max(100),
145
+ })
146
+ .strict();
147
+ /** A reviewed background consumer may decide delivery, but never authenticate as the sender. */
148
+ export const deliveryAccessInputSchema = messageAccessInputSchema
149
+ .extend({ senderAccountId: accountId })
150
+ .strict();
151
+ /** Count repeated participants across resources too: this bounds both graph and pair evaluations. */
152
+ const messageBatch = (schema) => z
153
+ .object({ items: z.array(schema).min(1).max(MESSAGE_ACCESS_MAX_RESOURCES) })
154
+ .strict()
155
+ .refine((value) => new Set(value.items.map((item) => item.resourceId)).size === value.items.length, 'Resource IDs must be unique')
156
+ .refine((value) => value.items.reduce((total, item) => total + item.participantAccountIds.length, 0) <=
157
+ MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS, 'Participant budget exceeded')
158
+ .refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <=
159
+ MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Message batch byte budget exceeded');
160
+ export const messageAccessBatchInputSchema = messageBatch(messageAccessInputSchema);
161
+ export const deliveryAccessBatchInputSchema = messageBatch(deliveryAccessInputSchema);
162
+ export const messageAccessBatchResponseSchema = contentAccessBatchResponseSchema;
163
+ /** Live socket-recipient proof; a request ID correlates the result and never identifies an account. */
164
+ export const messagingSessionsInputSchema = z
165
+ .object({
166
+ items: z
167
+ .array(z
168
+ .object({
169
+ requestId: resourceId,
170
+ accessToken: z.string().min(1).max(16384),
171
+ })
172
+ .strict())
173
+ .min(1)
174
+ .max(100),
175
+ })
176
+ .strict()
177
+ .refine((value) => new Set(value.items.map((item) => item.requestId)).size === value.items.length, 'Request IDs must be unique')
178
+ .refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <= MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Session proof byte budget exceeded');
179
+ export const messagingSessionsResponseSchema = z
180
+ .object({
181
+ items: z
182
+ .array(z.discriminatedUnion('allowed', [
183
+ z.object({ requestId: resourceId, allowed: z.literal(true), accountId }).strict(),
184
+ z.object({ requestId: resourceId, allowed: z.literal(false) }).strict(),
185
+ ]))
186
+ .max(100),
187
+ })
188
+ .strict();
189
+ export const safetyRequesterSchema = z.union([
190
+ z.object({ accessToken: z.string().min(1).max(16384).nullable() }).strict(),
191
+ z.object({ connectionToken: z.string().min(1).max(16384) }).strict(),
192
+ ]);
193
+ export const supervisionSchema = z
194
+ .object({
195
+ childAccountId: accountId,
196
+ guardianAccountId: accountId.nullable(),
197
+ status: z.enum(SUPERVISION_STATUSES),
198
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
199
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
200
+ createdAt: z.string().datetime(),
201
+ })
202
+ .strict();
203
+ export const supervisionInvitationSchema = z
204
+ .object({
205
+ id: accountId,
206
+ childAccountId: accountId,
207
+ guardianAccountId: accountId,
208
+ status: z.enum(SUPERVISION_INVITATION_STATUSES),
209
+ expiresAt: z.string().datetime(),
210
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
211
+ createdAt: z.string().datetime(),
212
+ })
213
+ .strict();
214
+ export const supervisionContactSchema = z
215
+ .object({
216
+ childAccountId: accountId,
217
+ contactAccountId: accountId,
218
+ approvedAt: z.string().datetime(),
219
+ })
220
+ .strict();
221
+ export const supervisionStateSchema = z
222
+ .object({
223
+ self: supervisionSchema.nullable(),
224
+ children: z.array(supervisionSchema).max(100),
225
+ invitations: z.array(supervisionInvitationSchema).max(100),
226
+ })
227
+ .strict();
228
+ export const supervisionContactsResponseSchema = z
229
+ .object({ items: z.array(supervisionContactSchema).max(1000) })
230
+ .strict();
231
+ const fingerprint = z.string().regex(/^[a-f0-9]{64}$/);
232
+ const minorUnits = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
233
+ export const purchaseApprovalLineSchema = z
234
+ .object({
235
+ itemReference: resourceId,
236
+ title: z.string().trim().min(1).max(300),
237
+ optionsText: z.string().trim().max(300),
238
+ merchantAccountId: accountId,
239
+ quantity: z.number().int().min(1).max(1000000),
240
+ unitAmountMinor: minorUnits,
241
+ lineAmountMinor: minorUnits,
242
+ })
243
+ .strict();
244
+ const purchaseEffectsObject = z
245
+ .object({
246
+ operationId: resourceId,
247
+ resourceId,
248
+ cartFingerprint: fingerprint,
249
+ summary: z.string().trim().min(1).max(300),
250
+ merchantAccountIds: z
251
+ .array(accountId)
252
+ .min(1)
253
+ .max(100)
254
+ .refine((ids) => new Set(ids).size === ids.length, 'Merchant accounts must be unique')
255
+ .transform((ids) => [...ids].sort()),
256
+ lineItems: z.array(purchaseApprovalLineSchema).min(1).max(200),
257
+ shippingMinor: minorUnits,
258
+ taxMinor: minorUnits,
259
+ discountMinor: minorUnits,
260
+ roundingAdjustmentMinor: z
261
+ .number()
262
+ .int()
263
+ .min(-Number.MAX_SAFE_INTEGER)
264
+ .max(Number.MAX_SAFE_INTEGER),
265
+ paymentFingerprint: fingerprint,
266
+ amountMinor: minorUnits,
267
+ currency: z.string().regex(/^[A-Z]{3}$/),
268
+ expiresAt: z.string().datetime(),
269
+ })
270
+ .strict();
271
+ function validatePurchaseEvidence(value, ctx) {
272
+ const merchants = new Set(value.merchantAccountIds);
273
+ const references = new Set();
274
+ for (const line of value.lineItems) {
275
+ const key = `${line.merchantAccountId}:${line.itemReference}`;
276
+ if (!merchants.has(line.merchantAccountId) || references.has(key))
277
+ ctx.addIssue({
278
+ code: z.ZodIssueCode.custom,
279
+ message: 'Purchase lines must name an included merchant and unique item reference',
280
+ });
281
+ references.add(key);
282
+ }
283
+ if (merchants.size !== new Set(value.lineItems.map((line) => line.merchantAccountId)).size)
284
+ ctx.addIssue({
285
+ code: z.ZodIssueCode.custom,
286
+ message: 'Every merchant must have an approval line',
287
+ });
288
+ const total = value.lineItems.reduce((sum, line) => sum + BigInt(line.lineAmountMinor), 0n) +
289
+ BigInt(value.shippingMinor) +
290
+ BigInt(value.taxMinor) -
291
+ BigInt(value.discountMinor) +
292
+ BigInt(value.roundingAdjustmentMinor);
293
+ if (total < 0n || total > BigInt(Number.MAX_SAFE_INTEGER) || total !== BigInt(value.amountMinor))
294
+ ctx.addIssue({
295
+ code: z.ZodIssueCode.custom,
296
+ message: 'Approval evidence must equal the exact payment total',
297
+ });
298
+ if (Math.abs(value.roundingAdjustmentMinor) >
299
+ value.lineItems.length + 3 * value.merchantAccountIds.length)
300
+ ctx.addIssue({
301
+ code: z.ZodIssueCode.custom,
302
+ message: 'Rounding adjustment exceeds the bounded conversion stages',
303
+ });
304
+ }
305
+ export const purchaseEffectsSchema = purchaseEffectsObject.superRefine(validatePurchaseEvidence);
306
+ const purchaseApprovalFields = {
307
+ id: accountId,
308
+ childAccountId: accountId,
309
+ applicationId: accountId,
310
+ status: z.enum(PURCHASE_APPROVAL_STATUSES),
311
+ createdAt: z.string().datetime(),
312
+ receiptReference: z.string().max(512).nullable(),
313
+ };
314
+ export const purchaseApprovalSchema = purchaseEffectsObject
315
+ .extend(purchaseApprovalFields)
316
+ .strict()
317
+ .superRefine(validatePurchaseEvidence);
318
+ export const purchaseApprovalSummarySchema = purchaseEffectsObject
319
+ .omit({ lineItems: true })
320
+ .extend(purchaseApprovalFields)
321
+ .strict();
322
+ export const purchaseApprovalListSchema = z
323
+ .object({
324
+ items: z.array(purchaseApprovalSummarySchema).max(100),
325
+ nextCursor: z.string().nullable(),
326
+ })
327
+ .strict();
328
+ export const reservePurchaseSchema = purchaseEffectsObject
329
+ .extend({ approvalId: accountId })
330
+ .strict()
331
+ .superRefine(validatePurchaseEvidence);
332
+ export const confirmPurchaseSchema = z
333
+ .object({
334
+ approvalId: accountId,
335
+ operationId: resourceId,
336
+ receiptReference: z.string().trim().min(1).max(512),
337
+ })
338
+ .strict();
339
+ export const cancelPurchaseSchema = confirmPurchaseSchema.omit({ receiptReference: true });
340
+ export const SAFETY_ERROR_CODES = [
341
+ 'bad_request',
342
+ 'validation_failed',
343
+ 'unauthorized',
344
+ 'forbidden',
345
+ 'not_found',
346
+ 'conflict',
347
+ 'reauth_required',
348
+ 'limit_reached',
349
+ 'service_unavailable',
350
+ 'rate_limited',
351
+ ];
352
+ export const safetyErrorSchema = z
353
+ .object({ error: z.object({ code: z.enum(SAFETY_ERROR_CODES), message: z.string() }).strict() })
354
+ .strict();
355
+ export const supervisionInviteInputSchema = z
356
+ .object({
357
+ guardianAccountId: accountId,
358
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
359
+ reauth: reauthProofSchema,
360
+ })
361
+ .strict();
362
+ export const supervisionAcceptInputSchema = z
363
+ .object({ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION), reauth: reauthProofSchema })
364
+ .strict();
365
+ export const supervisionContactInputSchema = z
366
+ .object({ childAccountId: accountId, contactAccountId: accountId, reauth: reauthProofSchema })
367
+ .strict();
368
+ export const supervisionReviewInputSchema = z
369
+ .object({
370
+ childAccountId: accountId,
371
+ action: z.enum(['end', 'transfer']),
372
+ guardianAccountId: accountId.optional(),
373
+ evidenceReference: z.string().trim().min(1).max(2048),
374
+ reauth: reauthProofSchema,
375
+ })
376
+ .strict();
377
+ export const purchaseReviewInputSchema = z
378
+ .object({ decision: z.enum(['approved', 'rejected']), reauth: reauthProofSchema })
379
+ .strict();
380
+ export const safetySessionInputSchema = z
381
+ .object({
382
+ sessionId: accountId,
383
+ accessToken: z.string().min(1).max(16384),
384
+ action: z.enum(SUPERVISED_ACTIONS),
385
+ })
386
+ .strict();
387
+ export const safetySessionValidationSchema = z
388
+ .object({
389
+ valid: z.literal(true),
390
+ sessionId: accountId,
391
+ expiresAt: z.string().datetime(),
392
+ lastActivity: z.string().datetime(),
393
+ user: userResponseSchema,
394
+ actor: accountActorChainSchema,
395
+ })
396
+ .strict();
397
+ /** Minimal identity for the authenticated account's supervision management, never profile details. */
398
+ export const safetyPersonSchema = z
399
+ .object({ accountId, username: z.string().nullable(), displayName: z.string().nullable() })
400
+ .strict();
401
+ export const safetyPeopleInputSchema = z.union([
402
+ z.object({ query: z.string().trim().min(2).max(100) }).strict(),
403
+ z.object({ accountIds: uniqueIds(100) }).strict(),
404
+ ]);
405
+ export const safetyPeopleResponseSchema = z
406
+ .object({ items: z.array(safetyPersonSchema).max(100) })
407
+ .strict();
408
+ /** Existing public employer identity only; never directory content or management authority. */
409
+ export const safetyOrganizationIdentitySchema = z
410
+ .object({
411
+ accountId,
412
+ username: z.string().nullable(),
413
+ name: z.string(),
414
+ organizationType: z.enum(ORGANIZATION_TYPES),
415
+ })
416
+ .strict();
417
+ export const safetyOrganizationsInputSchema = z.union([
418
+ z
419
+ .object({
420
+ query: z.string().trim().min(1).max(100),
421
+ limit: z.number().int().min(1).max(20).optional(),
422
+ })
423
+ .strict(),
424
+ z.object({ ids: uniqueIds(100) }).strict(),
425
+ ]);
426
+ export const safetyOrganizationsResponseSchema = z
427
+ .object({
428
+ items: z
429
+ .array(z
430
+ .object({ requestedAccountId: accountId, organization: safetyOrganizationIdentitySchema })
431
+ .strict())
432
+ .max(100),
433
+ missingIds: z.array(accountId).max(100),
434
+ })
435
+ .strict();
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ /** Omitted means the existing grant; an explicit set only narrows that authority. */
3
+ export const serviceTokenScopesSchema = z
4
+ .array(z.string().trim().min(1).max(128))
5
+ .min(1)
6
+ .max(100)
7
+ .refine((scopes) => new Set(scopes).size === scopes.length, 'Requested scopes must be unique')
8
+ .transform((scopes) => [...scopes].sort());
9
+ export const serviceTokenOptionsSchema = z
10
+ .object({ scopes: serviceTokenScopesSchema.optional() })
11
+ .strict();
@@ -32,40 +32,10 @@
32
32
  import { z } from 'zod';
33
33
  import { EMAIL_CODE_LENGTH, emailAddressSchema, emailTicketSchema } from './accountEmail.js';
34
34
  import { deviceProofSchema } from './deviceSession.js';
35
- /** How long the link in a sign-in email can be opened. */
36
- export const EMAIL_SIGNIN_LINK_TTL_MS = 15 * 60 * 1000;
37
- /** How long a second-factor challenge can be answered. */
38
- export const SIGNIN_SECOND_FACTOR_TTL_MS = 5 * 60 * 1000;
39
- /** Wrong second-factor codes one challenge accepts. */
40
- export const SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = 5;
41
- /** Shortest password Oxy accepts when one is set. */
42
- export const PASSWORD_MIN_LENGTH = 10;
43
- /** Longest password Oxy accepts (characters). */
44
- export const PASSWORD_MAX_LENGTH = 256;
45
- /** Digits in an authenticator code. */
46
- export const TOTP_DIGITS = 6;
47
- /** Seconds each authenticator code lasts. */
48
- export const TOTP_PERIOD_SECONDS = 30;
49
- /** Backup codes issued when an authenticator is enabled or they are regenerated. */
50
- export const TOTP_BACKUP_CODE_COUNT = 10;
51
- /**
52
- * The long sign-in code: ten characters of Crockford base32 without the
53
- * look-alikes (no 0, O, 1, I, L, U), shown as `XXXXX-XXXXX`. An account whose
54
- * sign-in codes were guessed at too often today (the per-account ceiling) is
55
- * sent this instead of 6 digits for the rest of the day, so its owner can still
56
- * type a code while guessing one is infeasible.
57
- */
58
- export const EMAIL_SIGNIN_LONG_CODE_ALPHABET = '23456789ABCDEFGHJKMNPQRSTVWXYZ';
59
- export const EMAIL_SIGNIN_LONG_CODE_LENGTH = 10;
60
- /**
61
- * Normalise a typed sign-in code: 6 digits stay as they are; a long code is
62
- * upper-cased with its dash and spaces removed. Anything else is returned
63
- * trimmed (and will simply be wrong).
64
- */
65
- export function normalizeEmailSignInCode(code) {
66
- const compact = code.trim().replace(/[\s-]/g, '').toUpperCase();
67
- return compact;
68
- }
35
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
36
+ // it is re-exported here so the root entry is unchanged.
37
+ import { PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH, TOTP_DIGITS, TOTP_BACKUP_CODE_COUNT, EMAIL_SIGNIN_LONG_CODE_ALPHABET, EMAIL_SIGNIN_LONG_CODE_LENGTH, normalizeEmailSignInCode, EMAIL_ACCOUNTS_MAX, REAUTH_ACTIONS, } from './client/signIn.js';
38
+ export { EMAIL_SIGNIN_LINK_TTL_MS, SIGNIN_SECOND_FACTOR_TTL_MS, SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS, PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH, TOTP_DIGITS, TOTP_PERIOD_SECONDS, TOTP_BACKUP_CODE_COUNT, EMAIL_SIGNIN_LONG_CODE_ALPHABET, EMAIL_SIGNIN_LONG_CODE_LENGTH, normalizeEmailSignInCode, EMAIL_SIGNIN_CHOICE_TTL_MS, EMAIL_ACCOUNTS_MAX, isSecondFactorRequired, isEmailSignInChooseAccount, REAUTH_ACTIONS, SIGN_IN_ERROR_CODES, } from './client/signIn.js';
69
39
  const sixDigits = z
70
40
  .string()
71
41
  .trim()
@@ -193,13 +163,6 @@ export const emailSignInPendingSchema = z.object({
193
163
  status: z.literal('pending'),
194
164
  expiresAt: z.number().int().positive(),
195
165
  });
196
- /**
197
- * How long, once the email is proven, the person has to choose which of its
198
- * accounts signs in.
199
- */
200
- export const EMAIL_SIGNIN_CHOICE_TTL_MS = 5 * 60 * 1000;
201
- /** Most accounts one email can hold; sign-up refuses another past it. */
202
- export const EMAIL_ACCOUNTS_MAX = 20;
203
166
  export const emailSignInAccountOptionSchema = z.object({
204
167
  id: z.string().min(1).max(64),
205
168
  username: z.string().min(1).optional(),
@@ -220,25 +183,6 @@ export const emailSignInChooseRequestSchema = z
220
183
  ...sessionEnvelope,
221
184
  })
222
185
  .strict();
223
- /** Narrow a {@link SignInStepResult}. */
224
- export function isSecondFactorRequired(result) {
225
- return result.secondFactorRequired === true;
226
- }
227
- /** Narrow an {@link EmailSignInStepResult}. */
228
- export function isEmailSignInChooseAccount(result) {
229
- return result.chooseAccount === true;
230
- }
231
- /**
232
- * What a re-verification email code confirms. The code is bound to it: a code
233
- * asked for one change never authorises another, and the email names it.
234
- */
235
- export const REAUTH_ACTIONS = [
236
- 'change_password',
237
- 'totp',
238
- 'link_commons',
239
- 'delete_account',
240
- 'credentials_manage',
241
- ];
242
186
  /** `POST /users/me/reauth/email` */
243
187
  export const reauthEmailStartRequestSchema = z.object({ action: z.enum(REAUTH_ACTIONS) }).strict();
244
188
  /**
@@ -312,39 +256,3 @@ export const totpBackupCodesResponseSchema = z.object({
312
256
  .array(z.string().regex(/^[a-z2-9]{5}-[a-z2-9]{5}$/))
313
257
  .length(TOTP_BACKUP_CODE_COUNT),
314
258
  });
315
- /**
316
- * Stable error codes (`error.code` in the API error body). Clients map these
317
- * through their localization, never the English message.
318
- */
319
- export const SIGN_IN_ERROR_CODES = {
320
- /** Wrong identifier or password — never says which. */
321
- invalidCredentials: 'SIGNIN_INVALID_CREDENTIALS',
322
- /** The request is unknown, expired, spent, or its secret is wrong. */
323
- requestInvalid: 'SIGNIN_REQUEST_INVALID',
324
- /** The link is unknown, expired or already used. */
325
- linkInvalid: 'SIGNIN_LINK_INVALID',
326
- /** The link was opened in another browser: type the code in the app instead. */
327
- linkOtherDevice: 'SIGNIN_LINK_OTHER_DEVICE',
328
- /** The second-factor challenge or its code is wrong, expired or spent. */
329
- secondFactorInvalid: 'SECOND_FACTOR_INVALID',
330
- /** Too many failures for this account: wait and try again. */
331
- locked: 'SIGNIN_LOCKED',
332
- /** The step needs a fresh proof (`reauth`). */
333
- reauthRequired: 'REAUTH_REQUIRED',
334
- /** The fresh proof was wrong. */
335
- reauthInvalid: 'REAUTH_INVALID',
336
- /** The account has an authenticator: the proof must carry its code. */
337
- totpRequired: 'TOTP_REQUIRED',
338
- /** Enrolling or confirming an authenticator that is already on. */
339
- totpAlreadyEnabled: 'TOTP_ALREADY_ENABLED',
340
- /** Disabling, confirming or regenerating codes with no authenticator (or no pending one). */
341
- totpNotEnabled: 'TOTP_NOT_ENABLED',
342
- /** The code the authenticator showed at enrolment is wrong. */
343
- totpCodeInvalid: 'TOTP_CODE_INVALID',
344
- /** Only official Oxy apps and auth.oxy.so may sign people in here. */
345
- originNotAllowed: 'SIGNIN_ORIGIN_NOT_ALLOWED',
346
- /** The username is taken. */
347
- usernameTaken: 'USERNAME_TAKEN',
348
- /** The email already holds `EMAIL_ACCOUNTS_MAX` accounts: no sign-up adds another. */
349
- emailAccountsFull: 'EMAIL_ACCOUNTS_FULL',
350
- };