@oxy.so/contracts 1.0.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 (147) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/dist/cjs/.tsbuildinfo +1 -0
  4. package/dist/cjs/accountGraph.js +489 -0
  5. package/dist/cjs/agency.js +439 -0
  6. package/dist/cjs/browserHub.js +215 -0
  7. package/dist/cjs/civic.js +163 -0
  8. package/dist/cjs/commonsSignIn.js +59 -0
  9. package/dist/cjs/deviceBoot.js +50 -0
  10. package/dist/cjs/deviceDirectory.js +189 -0
  11. package/dist/cjs/devicePairing.js +138 -0
  12. package/dist/cjs/deviceSession.js +164 -0
  13. package/dist/cjs/emailAgentContext.js +32 -0
  14. package/dist/cjs/followGraph.js +28 -0
  15. package/dist/cjs/identity.js +258 -0
  16. package/dist/cjs/inboxPush.js +24 -0
  17. package/dist/cjs/index.js +618 -0
  18. package/dist/cjs/inference/accountBilling.js +334 -0
  19. package/dist/cjs/inference/aliaModelRelease.js +262 -0
  20. package/dist/cjs/inference/attribution.js +106 -0
  21. package/dist/cjs/inference/catalogue.js +487 -0
  22. package/dist/cjs/inference/entitlement.js +217 -0
  23. package/dist/cjs/inference/errors.js +309 -0
  24. package/dist/cjs/inference/identifiers.js +224 -0
  25. package/dist/cjs/inference/inbox.js +105 -0
  26. package/dist/cjs/inference/modelDocumentation.js +433 -0
  27. package/dist/cjs/inference/money.js +188 -0
  28. package/dist/cjs/inference/priceVersion.js +110 -0
  29. package/dist/cjs/inference/providerConnection.js +455 -0
  30. package/dist/cjs/inference/request.js +477 -0
  31. package/dist/cjs/inference/routingPolicy.js +318 -0
  32. package/dist/cjs/inference/streamEvents.js +258 -0
  33. package/dist/cjs/inference/usage.js +329 -0
  34. package/dist/cjs/inference/version.js +105 -0
  35. package/dist/cjs/keyRecovery.js +91 -0
  36. package/dist/cjs/keyRotation.js +75 -0
  37. package/dist/cjs/links.js +68 -0
  38. package/dist/cjs/moderationReputation.js +298 -0
  39. package/dist/cjs/oauth.js +66 -0
  40. package/dist/cjs/oxyRecordTypes.js +71 -0
  41. package/dist/cjs/protocol.js +53 -0
  42. package/dist/cjs/recommendations.js +168 -0
  43. package/dist/cjs/reputation.js +297 -0
  44. package/dist/cjs/sessionStatus.js +121 -0
  45. package/dist/cjs/transparency.js +89 -0
  46. package/dist/cjs/updates.js +252 -0
  47. package/dist/cjs/userInvalidation.js +89 -0
  48. package/dist/cjs/userResponse.js +245 -0
  49. package/dist/cjs/username.js +290 -0
  50. package/dist/cjs/webauthn.js +71 -0
  51. package/dist/esm/.tsbuildinfo +1 -0
  52. package/dist/esm/accountGraph.js +480 -0
  53. package/dist/esm/agency.js +436 -0
  54. package/dist/esm/browserHub.js +212 -0
  55. package/dist/esm/civic.js +160 -0
  56. package/dist/esm/commonsSignIn.js +56 -0
  57. package/dist/esm/deviceBoot.js +47 -0
  58. package/dist/esm/deviceDirectory.js +186 -0
  59. package/dist/esm/devicePairing.js +135 -0
  60. package/dist/esm/deviceSession.js +161 -0
  61. package/dist/esm/emailAgentContext.js +29 -0
  62. package/dist/esm/followGraph.js +27 -0
  63. package/dist/esm/identity.js +255 -0
  64. package/dist/esm/inboxPush.js +21 -0
  65. package/dist/esm/index.js +172 -0
  66. package/dist/esm/inference/accountBilling.js +331 -0
  67. package/dist/esm/inference/aliaModelRelease.js +259 -0
  68. package/dist/esm/inference/attribution.js +103 -0
  69. package/dist/esm/inference/catalogue.js +484 -0
  70. package/dist/esm/inference/entitlement.js +214 -0
  71. package/dist/esm/inference/errors.js +306 -0
  72. package/dist/esm/inference/identifiers.js +221 -0
  73. package/dist/esm/inference/inbox.js +102 -0
  74. package/dist/esm/inference/modelDocumentation.js +430 -0
  75. package/dist/esm/inference/money.js +185 -0
  76. package/dist/esm/inference/priceVersion.js +107 -0
  77. package/dist/esm/inference/providerConnection.js +452 -0
  78. package/dist/esm/inference/request.js +474 -0
  79. package/dist/esm/inference/routingPolicy.js +315 -0
  80. package/dist/esm/inference/streamEvents.js +255 -0
  81. package/dist/esm/inference/usage.js +326 -0
  82. package/dist/esm/inference/version.js +102 -0
  83. package/dist/esm/keyRecovery.js +88 -0
  84. package/dist/esm/keyRotation.js +72 -0
  85. package/dist/esm/links.js +65 -0
  86. package/dist/esm/moderationReputation.js +295 -0
  87. package/dist/esm/oauth.js +63 -0
  88. package/dist/esm/oxyRecordTypes.js +68 -0
  89. package/dist/esm/protocol.js +50 -0
  90. package/dist/esm/recommendations.js +165 -0
  91. package/dist/esm/reputation.js +293 -0
  92. package/dist/esm/sessionStatus.js +118 -0
  93. package/dist/esm/transparency.js +86 -0
  94. package/dist/esm/updates.js +249 -0
  95. package/dist/esm/userInvalidation.js +85 -0
  96. package/dist/esm/userResponse.js +240 -0
  97. package/dist/esm/username.js +283 -0
  98. package/dist/esm/webauthn.js +68 -0
  99. package/dist/types/.tsbuildinfo +1 -0
  100. package/dist/types/accountGraph.d.ts +378 -0
  101. package/dist/types/agency.d.ts +2162 -0
  102. package/dist/types/browserHub.d.ts +856 -0
  103. package/dist/types/civic.d.ts +338 -0
  104. package/dist/types/commonsSignIn.d.ts +58 -0
  105. package/dist/types/deviceBoot.d.ts +74 -0
  106. package/dist/types/deviceDirectory.d.ts +1317 -0
  107. package/dist/types/devicePairing.d.ts +130 -0
  108. package/dist/types/deviceSession.d.ts +411 -0
  109. package/dist/types/emailAgentContext.d.ts +248 -0
  110. package/dist/types/followGraph.d.ts +150 -0
  111. package/dist/types/identity.d.ts +402 -0
  112. package/dist/types/inboxPush.d.ts +30 -0
  113. package/dist/types/index.d.ts +100 -0
  114. package/dist/types/inference/accountBilling.d.ts +738 -0
  115. package/dist/types/inference/aliaModelRelease.d.ts +609 -0
  116. package/dist/types/inference/attribution.d.ts +176 -0
  117. package/dist/types/inference/catalogue.d.ts +1618 -0
  118. package/dist/types/inference/entitlement.d.ts +519 -0
  119. package/dist/types/inference/errors.d.ts +242 -0
  120. package/dist/types/inference/identifiers.d.ts +182 -0
  121. package/dist/types/inference/inbox.d.ts +374 -0
  122. package/dist/types/inference/modelDocumentation.d.ts +1603 -0
  123. package/dist/types/inference/money.d.ts +185 -0
  124. package/dist/types/inference/priceVersion.d.ts +182 -0
  125. package/dist/types/inference/providerConnection.d.ts +968 -0
  126. package/dist/types/inference/request.d.ts +2800 -0
  127. package/dist/types/inference/routingPolicy.d.ts +616 -0
  128. package/dist/types/inference/streamEvents.d.ts +950 -0
  129. package/dist/types/inference/usage.d.ts +1164 -0
  130. package/dist/types/inference/version.d.ts +102 -0
  131. package/dist/types/keyRecovery.d.ts +138 -0
  132. package/dist/types/keyRotation.d.ts +103 -0
  133. package/dist/types/links.d.ts +96 -0
  134. package/dist/types/moderationReputation.d.ts +487 -0
  135. package/dist/types/oauth.d.ts +86 -0
  136. package/dist/types/oxyRecordTypes.d.ts +62 -0
  137. package/dist/types/protocol.d.ts +86 -0
  138. package/dist/types/recommendations.d.ts +542 -0
  139. package/dist/types/reputation.d.ts +457 -0
  140. package/dist/types/sessionStatus.d.ts +231 -0
  141. package/dist/types/transparency.d.ts +392 -0
  142. package/dist/types/updates.d.ts +545 -0
  143. package/dist/types/userInvalidation.d.ts +94 -0
  144. package/dist/types/userResponse.d.ts +1706 -0
  145. package/dist/types/username.d.ts +265 -0
  146. package/dist/types/webauthn.d.ts +77 -0
  147. package/package.json +87 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Username policy — the ONE rule, for every kind of account.
3
+ *
4
+ * A username is a HANDLE: the routing key of a profile URL (`/@alice`), the
5
+ * local part of a webfinger `acct:`, and a login identifier. `users.username`
6
+ * carries a single unique index, `lower(btrim(username))`, and people, bots,
7
+ * organizations, projects and channels all draw from it. There is no per-kind
8
+ * NAMESPACE, so no kind may LOOSEN the rule — a bot that could reserve a name a
9
+ * person cannot ask for is a disagreement inside one index, not a variant.
10
+ *
11
+ * One kind TIGHTENS it, and only in that direction: a `bot` handle must also end
12
+ * in `bot` ({@link botUsernameSchema}, at the bottom of this file). That is a
13
+ * label a bot wears, not a name it takes off anybody — everything it accepts,
14
+ * {@link usernameSchema} already accepted. {@link usernameSchemaForAccountKind}
15
+ * is the single place that branch is written.
16
+ *
17
+ * ## Why this file exists
18
+ *
19
+ * Seven rules governed this one namespace: four validators (this package's
20
+ * predecessor in `@oxy.so/api`, `@oxy.so/core`, `@oxy.so/commons`, and one written
21
+ * inline in `AccountService.resolveUniqueUsername`) and three that COERCED —
22
+ * silently deleting the characters they disliked, which hands somebody an
23
+ * account under a name they never chose. They lived in five packages and no test
24
+ * asserted they agreed. `contracts` is where the single declaration can actually
25
+ * live: `api`, `core`, `commons`, `services` and `auth` all already depend on it,
26
+ * so every write path can IMPORT the rule instead of restating it.
27
+ * `__tests__/usernamePolicySingleSource.test.ts` fails if a second one appears.
28
+ *
29
+ * ## The rule, and why each part of it
30
+ *
31
+ * ```
32
+ * 3–30 characters
33
+ * first and last character: [A-Za-z0-9]
34
+ * interior: [A-Za-z0-9_-]
35
+ * never two separators in a row
36
+ * ```
37
+ *
38
+ * - **Hyphens are admitted because the DATABASE already admits them.**
39
+ * `internal_cost_centers_slug_check` is a CHECK constraint —
40
+ * `^[a-z0-9][a-z0-9-]{0,62}$` — and `seed-internal-cost-centers` mints a
41
+ * `project` account whose username IS the slug; four of the five declared
42
+ * centres contain a hyphen. An alphanumeric-only handle rule would contradict
43
+ * a constraint written to permit them, and would make those centres
44
+ * unmintable. This is the argument, not the four hyphenated accounts that
45
+ * happen to exist — they are a symptom.
46
+ * - **Dots are NOT admitted.** A dot is the delimiter that separates handle from
47
+ * domain in the federated form this same column stores for remote actors
48
+ * (`alice@mastodon.social`), it collides with extension-style routing
49
+ * (`/@alice.json`), and `n.ate` beside `nate` is the strongest confusable pair
50
+ * an ASCII handle can produce. Only the inline account rule ever accepted one,
51
+ * and it accepted it by accident: its `[\w.-]` was written for a SLUG.
52
+ * - **A length bound, always.** The account path had none — the only ceiling was
53
+ * a `.max(100)` on the wire schema. 3 is the floor because `oxy`, the platform
54
+ * owner's own organization, is three characters. 30 is the ceiling four of the
55
+ * seven rules and the availability endpoint already published.
56
+ * - **First and last character alphanumeric, and no `--` / `__` / `-_` run.**
57
+ * Both are free — no account uses such a name — and they remove the
58
+ * confusable shapes that admitting two separators would otherwise introduce.
59
+ *
60
+ * ## Case, and what this schema deliberately does not do
61
+ *
62
+ * **Case is PRESERVED.** Uniqueness is decided by the database's
63
+ * `lower(btrim(username))` index, so `Alice` and `alice` cannot coexist, but a
64
+ * name that was typed with a capital keeps it. This schema therefore never
65
+ * lower-cases: rewriting a caller's input is how `resolveUniqueUsername` used to
66
+ * return `mybot` to somebody who asked for `MyBot`.
67
+ *
68
+ * **This is a WRITE-path rule.** It states what may be newly stored, not what may
69
+ * be read. Rows that predate it — including 11 with no username at all — must go
70
+ * on loading, resolving and rendering; validating on a read turns an existing
71
+ * account into a 500.
72
+ *
73
+ * **It does not govern remote actors.** The same column holds ~73k federated
74
+ * rows in `handle@domain` form, written by `POST /users/resolve` through its own
75
+ * normalizer. Those are another server's namespace; this rule would reject every
76
+ * one of them and must never be pointed at that path.
77
+ *
78
+ * ## Usable by a handle GENERATOR, deliberately
79
+ *
80
+ * Slug generators are how the eighth copy of this rule appears. Alia's
81
+ * `suggestAgentUsername` builds one from an agent's name and re-derives a subset
82
+ * of these rules by hand — its own docblock admits it ("A leading digit or an
83
+ * empty slug both fail Oxy's username rules") — and, having no minimum, proposes
84
+ * `al` for an agent called "Al", which the server then refuses.
85
+ *
86
+ * So this module answers a generator's three questions without dragging a server
87
+ * dependency along. It is zod and nothing else, so it imports cleanly into a
88
+ * React Native bundle or another repo's backend:
89
+ *
90
+ * - *Does this candidate pass?* {@link isValidUsername}, or `safeParse` when the
91
+ * reason matters.
92
+ * - *How short is too short, how long is too long?* {@link USERNAME_MIN_LENGTH}
93
+ * and {@link USERNAME_MAX_LENGTH}, so a generator can pad or truncate instead
94
+ * of guessing and being 400ed.
95
+ * - *Which characters survive?* {@link stripDisallowedUsernameCharacters}.
96
+ *
97
+ * A generator PROPOSES; only `POST /accounts` decides, and a taken handle comes
98
+ * back as a 409 for the client to retry with a fresh suggestion. Nothing here
99
+ * knows what is taken, and it must not pretend to.
100
+ */
101
+ import { z } from 'zod';
102
+ import type { AccountKind } from './accountGraph';
103
+ /** Shortest storable handle. `oxy` sets the floor. */
104
+ export declare const USERNAME_MIN_LENGTH = 3;
105
+ /** Longest storable handle, and the `maxLength` an input field should carry. */
106
+ export declare const USERNAME_MAX_LENGTH = 30;
107
+ /** The 400 / inline-validation copy for every path that rejects a handle. */
108
+ export declare const USERNAME_INVALID_MESSAGE: string;
109
+ /**
110
+ * Alphanumeric runs joined by single separators, as a SOURCE string.
111
+ *
112
+ * A string rather than a literal because the OpenAPI docblocks that publish this
113
+ * rule (`POST /auth/register`, `PUT /users/:userId`) must quote it verbatim, and
114
+ * `usernamePolicySingleSource.test.ts` compares them against THIS constant. A
115
+ * published `pattern:` that drifts from the enforced rule is a lie told to every
116
+ * client that generates from the spec, and it is exactly the kind of copy nobody
117
+ * notices going stale.
118
+ *
119
+ * Deliberately NOT re-exported from the package barrel: it exists for the
120
+ * schema below and for that one gate. Anything validating a username uses
121
+ * {@link usernameSchema}, so there is no second way to ask the question.
122
+ *
123
+ * Written as an unambiguous alternation rather than a lookahead: every character
124
+ * belongs to exactly one branch, so matching is linear and there is no
125
+ * backtracking to bound. It also carries no `\p{…}` property escape, which
126
+ * mobile Hermes throws on at runtime — this module is reachable from every React
127
+ * Native consumer.
128
+ */
129
+ export declare const USERNAME_PATTERN_SOURCE = "^[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*$";
130
+ /**
131
+ * The one username policy, as a schema.
132
+ *
133
+ * `.trim()` first, so surrounding whitespace is a typo rather than a rejection —
134
+ * but interior whitespace is NOT removed. It falls to the pattern, because
135
+ * squashing `"al ice"` into `"alice"` would hand the user an account under a name
136
+ * they never chose. Every write path validates through THIS object; nothing
137
+ * re-implements it.
138
+ */
139
+ export declare const usernameSchema: z.ZodString;
140
+ /**
141
+ * Whether a candidate handle is storable — the boolean form, for input surfaces
142
+ * that show a message as somebody types rather than throwing.
143
+ *
144
+ * Answers from {@link usernameSchema}, so a client's inline check and the
145
+ * server's 400 cannot disagree.
146
+ */
147
+ export declare function isValidUsername(candidate: string): boolean;
148
+ /**
149
+ * Drop the characters the policy forbids, for an input field that filters
150
+ * keystrokes.
151
+ *
152
+ * This is a TYPING aid and nothing else — the result still has to pass
153
+ * {@link usernameSchema}, which is what decides. It does not lower-case (case is
154
+ * preserved, see the header) and it cannot repair a name: a value that is too
155
+ * short, edge-separated or doubly-separated comes back unchanged and fails
156
+ * validation with a message, which is the outcome the coercing rules this
157
+ * replaces used to hide.
158
+ */
159
+ export declare function stripDisallowedUsernameCharacters(input: string): string;
160
+ /**
161
+ * The label a bot's handle ends in — the ONE exception the policy carries.
162
+ *
163
+ * Lower-case here because it is what a generator appends; the comparison that
164
+ * enforces it folds case, so `MyBot` satisfies it just as `mybot` does.
165
+ *
166
+ * Deliberately NOT re-exported from the package barrel, for the same reason
167
+ * {@link USERNAME_PATTERN_SOURCE} is not: it exists for the schema and the
168
+ * generator aid below. A consumer that wants to SAY the rule uses
169
+ * {@link BOT_USERNAME_INVALID_MESSAGE}, which already quotes it, and one that
170
+ * wants to APPLY it uses {@link applyBotUsernameSuffix} — so there is no second
171
+ * way to spell the check.
172
+ */
173
+ export declare const BOT_USERNAME_SUFFIX = "bot";
174
+ /** The 400 / inline-validation copy for a bot handle that carries no label. */
175
+ export declare const BOT_USERNAME_INVALID_MESSAGE = "A bot account's username must end in \"bot\"";
176
+ /**
177
+ * The username policy for an account of kind `bot`: everything above, plus a
178
+ * handle that ends in {@link BOT_USERNAME_SUFFIX}.
179
+ *
180
+ * ## Why a suffix, and why THIS one
181
+ *
182
+ * A bot is not a person, and the handle is the only part of an account that
183
+ * travels — into a URL, a mention, a webfinger `acct:`, a screenshot. Telegram
184
+ * settled this question the same way for the same reason: whatever the
185
+ * surrounding UI shows, `@somethingbot` says what it is at the point of contact.
186
+ * Nothing else in Oxy makes the distinction visible where it is actually read.
187
+ *
188
+ * The form is `bot` at the END, folded for case. Each half of that is decided by
189
+ * what THIS policy already admits, not by copying Telegram:
190
+ *
191
+ * - **The bare label, not `_bot`.** Telegram's common form uses an underscore,
192
+ * but Oxy admits `-` and `_` as equal separators — hyphens are load-bearing
193
+ * (`internal_cost_centers_slug_check`, and the accounts minted from those
194
+ * slugs), so a rule naming one of them would refuse `garden-helper-bot` while
195
+ * accepting `garden_helper_bot`, a distinction no other line of this policy
196
+ * makes. Requiring only the label leaves the separator to whoever chooses the
197
+ * name: `aliabot`, `alia-bot` and `alia_bot` all conform.
198
+ * - **Case-INSENSITIVE.** Case is preserved but uniqueness folds it
199
+ * (`lower(btrim(username))`), so a case-sensitive test would accept `mybot`
200
+ * and refuse `MyBot` — two names the index considers the same one. A rule that
201
+ * disagrees with the index about identity is a rule with two answers.
202
+ * - **The label is all ASCII alphanumerics**, so appending it to any handle that
203
+ * passes {@link usernameSchema} yields another one. That is what makes
204
+ * {@link applyBotUsernameSuffix} safe and a generator's suggestion honest.
205
+ *
206
+ * It is checked LAST, after the base policy: a handle that is illegal for
207
+ * everybody is reported as illegal rather than as a bot that forgot its label,
208
+ * so nobody appends `bot` to `a.b` and is refused twice.
209
+ *
210
+ * ## What it is NOT
211
+ *
212
+ * **Not a namespace.** `users.username` is still one unique index. A bot must be
213
+ * labelled; the label is not RESERVED, so `abbot` and `robot` stay available to
214
+ * anybody — the rule says what a bot's handle must look like, not what everyone
215
+ * else's may not.
216
+ *
217
+ * **Not a rule about the other four kinds.** `personal`, `organization`,
218
+ * `project` and `channel` are governed by {@link usernameSchema}, unchanged.
219
+ * {@link usernameSchemaForAccountKind} is the only branch, and
220
+ * `__tests__/username.test.ts` asserts by identity that the other kinds get the
221
+ * base schema back.
222
+ *
223
+ * **Not a rule about remote actors.** The same column holds ~74k federated rows
224
+ * in `handle@domain` form, some of them bots on their own server. This schema
225
+ * rejects every one of them and must never be pointed at that path — that
226
+ * namespace belongs to another server.
227
+ *
228
+ * **A WRITE-path rule**, like everything else here. Six bot accounts exist and
229
+ * not one of them is labelled (measured 2026-08-26: `community-guide`,
230
+ * `community-maestro`, `community-pulse`, `garden-helper`, `luna`, `verity`).
231
+ * They go on loading and resolving; only a NEW handle is held to this.
232
+ */
233
+ export declare const botUsernameSchema: z.ZodEffects<z.ZodString, string, string>;
234
+ /**
235
+ * The policy that governs a handle, given the kind of account that will hold it.
236
+ *
237
+ * The ONE place the exception branches. A call site that writes
238
+ * `kind === 'bot' ? … : …` for itself is the eighth copy of the rule arriving by
239
+ * the usual route, so every write path — account creation, account rename, the
240
+ * profile update — asks this instead.
241
+ *
242
+ * An unknown kind is the BASE policy, never the stricter one: `users.kind`
243
+ * defaults to `personal`, rows predate the column being filled in, and a rename
244
+ * refused because the server could not tell what it was reading is a 400 nobody
245
+ * can act on.
246
+ */
247
+ export declare function usernameSchemaForAccountKind(kind: AccountKind | null | undefined): z.ZodType<string, z.ZodTypeDef, string>;
248
+ /**
249
+ * Label a proposed bot handle, so a generator PROPOSES something the server will
250
+ * accept.
251
+ *
252
+ * Alia builds an agent's handle from its display name; without this it would
253
+ * propose `garden-helper` for a bot and be 400ed on submit, which is the same
254
+ * "suggest, then refuse" defect the minimum length already caused once.
255
+ *
256
+ * It appends and never inserts: a separator is the caller's choice, so
257
+ * `garden-helper` becomes `garden-helperbot` while a caller who typed
258
+ * `garden-helper-` gets `garden-helper-bot`. It truncates to leave room rather
259
+ * than overflowing {@link USERNAME_MAX_LENGTH}.
260
+ *
261
+ * Like {@link stripDisallowedUsernameCharacters} it is an aid, not a decision: it
262
+ * cannot repair a handle the base policy refuses, and the result still has to
263
+ * pass {@link botUsernameSchema}.
264
+ */
265
+ export declare function applyBotUsernameSuffix(candidate: string): string;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * WebAuthn / passkey ceremony contracts (Fase B/b1).
3
+ *
4
+ * These schemas describe ONLY the outer Oxy envelope that wraps a WebAuthn
5
+ * ceremony request — the username the client is registering/authenticating as,
6
+ * plus the device-session options every first-party sign-in accepts. The browser
7
+ * `RegistrationResponseJSON` / `AuthenticationResponseJSON` payloads are NOT
8
+ * mirrored here: they are validated by `@simplewebauthn/server` inside the route
9
+ * (`verifyRegistrationResponse` / `verifyAuthenticationResponse`), which is the
10
+ * single source of truth for their structure. Re-encoding them in Zod would just
11
+ * create a second, drift-prone definition of a shape we do not own.
12
+ */
13
+ import { z } from 'zod';
14
+ /**
15
+ * `POST /webauthn/register/options` — request registration options. With a bearer
16
+ * token the caller links a passkey to their signed-in account and `username` is
17
+ * ignored; without one it is a prospective signup and `username` is the desired
18
+ * (not-yet-created) handle.
19
+ */
20
+ export declare const webauthnRegisterOptionsRequestSchema: z.ZodObject<{
21
+ username: z.ZodOptional<z.ZodString>;
22
+ }, "strip", z.ZodTypeAny, {
23
+ username?: string | undefined;
24
+ }, {
25
+ username?: string | undefined;
26
+ }>;
27
+ export type WebauthnRegisterOptionsRequest = z.infer<typeof webauthnRegisterOptionsRequestSchema>;
28
+ /**
29
+ * `POST /webauthn/login/options` — request authentication options. When
30
+ * `username` is present the server scopes `allowCredentials` to that user's
31
+ * passkeys (username-first); when omitted it returns an empty allow-list for the
32
+ * usernameless / discoverable-credential flow (the default).
33
+ */
34
+ export declare const webauthnLoginOptionsRequestSchema: z.ZodObject<{
35
+ username: z.ZodOptional<z.ZodString>;
36
+ }, "strip", z.ZodTypeAny, {
37
+ username?: string | undefined;
38
+ }, {
39
+ username?: string | undefined;
40
+ }>;
41
+ export type WebauthnLoginOptionsRequest = z.infer<typeof webauthnLoginOptionsRequestSchema>;
42
+ /**
43
+ * `POST /webauthn/register/verify` — the outer envelope. The browser
44
+ * `RegistrationResponseJSON` travels alongside these fields under `response` and
45
+ * is validated by `@simplewebauthn/server`, not here. `username` is required only
46
+ * for the prospective-signup branch (no bearer); the linking branch ignores it.
47
+ */
48
+ export declare const webauthnRegisterVerifyRequestSchema: z.ZodObject<{
49
+ deviceName: z.ZodOptional<z.ZodString>;
50
+ deviceFingerprint: z.ZodOptional<z.ZodString>;
51
+ username: z.ZodOptional<z.ZodString>;
52
+ }, "strip", z.ZodTypeAny, {
53
+ username?: string | undefined;
54
+ deviceName?: string | undefined;
55
+ deviceFingerprint?: string | undefined;
56
+ }, {
57
+ username?: string | undefined;
58
+ deviceName?: string | undefined;
59
+ deviceFingerprint?: string | undefined;
60
+ }>;
61
+ export type WebauthnRegisterVerifyRequest = z.infer<typeof webauthnRegisterVerifyRequestSchema>;
62
+ /**
63
+ * `POST /webauthn/login/verify` — the outer envelope. The browser
64
+ * `AuthenticationResponseJSON` travels alongside these fields under `response`
65
+ * and is validated by `@simplewebauthn/server`, not here.
66
+ */
67
+ export declare const webauthnLoginVerifyRequestSchema: z.ZodObject<{
68
+ deviceName: z.ZodOptional<z.ZodString>;
69
+ deviceFingerprint: z.ZodOptional<z.ZodString>;
70
+ }, "strip", z.ZodTypeAny, {
71
+ deviceName?: string | undefined;
72
+ deviceFingerprint?: string | undefined;
73
+ }, {
74
+ deviceName?: string | undefined;
75
+ deviceFingerprint?: string | undefined;
76
+ }>;
77
+ export type WebauthnLoginVerifyRequest = z.infer<typeof webauthnLoginVerifyRequestSchema>;
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@oxy.so/contracts",
3
+ "version": "1.0.0",
4
+ "description": "OxyHQ API contracts — single source of truth for request/response Zod schemas and inferred types, shared by the backend and the client SDKs",
5
+ "main": "dist/cjs/index.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/types/index.d.ts",
8
+ "source": "src/index.ts",
9
+ "sideEffects": false,
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "react-native": "./dist/esm/index.js",
16
+ "import": {
17
+ "types": "./dist/types/index.d.ts",
18
+ "default": "./dist/esm/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/types/index.d.ts",
22
+ "default": "./dist/cjs/index.js"
23
+ },
24
+ "default": "./dist/esm/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "NOTICE",
30
+ "dist"
31
+ ],
32
+ "keywords": [
33
+ "oxyhq",
34
+ "sdk",
35
+ "contracts",
36
+ "zod",
37
+ "schemas",
38
+ "api"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/oxyhq/sdk",
43
+ "directory": "packages/contracts"
44
+ },
45
+ "author": "OxyHQ",
46
+ "license": "Apache-2.0",
47
+ "homepage": "https://oxy.so",
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "scripts": {
52
+ "build": "bun run build:cjs && bun run build:esm && bun run build:types",
53
+ "build:cjs": "tsc -p tsconfig.cjs.json",
54
+ "build:esm": "tsc -p tsconfig.esm.json && node scripts/fix-esm-imports.mjs",
55
+ "build:types": "tsc -p tsconfig.types.json",
56
+ "clean": "rm -rf dist",
57
+ "typescript": "tsc --noEmit",
58
+ "test": "jest --passWithNoTests",
59
+ "lint": "biome lint --error-on-warnings ./src",
60
+ "prepublishOnly": "node ../../scripts/assert-bun-publish.mjs && bun run clean && bun run build",
61
+ "release": "rm -rf dist && bun run build && release-it"
62
+ },
63
+ "release-it": {
64
+ "git": {
65
+ "tagName": "@oxy.so/contracts@${version}",
66
+ "tagAnnotation": "Release @oxy.so/contracts@${version}",
67
+ "commitMessage": "chore(contracts): release @oxy.so/contracts@${version}"
68
+ },
69
+ "github": {
70
+ "release": true,
71
+ "releaseName": "@oxy.so/contracts@${version}"
72
+ },
73
+ "npm": {
74
+ "publish": true
75
+ }
76
+ },
77
+ "dependencies": {
78
+ "zod": "^3.25.64"
79
+ },
80
+ "devDependencies": {
81
+ "@biomejs/biome": "^1.9.4",
82
+ "@types/node": "^20.19.43",
83
+ "jest": "^30.5.1",
84
+ "release-it": "^19.0.6",
85
+ "typescript": "^5.9.2"
86
+ }
87
+ }