@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,378 @@
1
+ /**
2
+ * Account graph wire contracts — the account-kind vocabulary, the account
3
+ * category taxonomy, and the create-account input.
4
+ *
5
+ * `accountCategories` classifies a NON-PERSONAL account — what it is about, what
6
+ * it does — without polluting `User.kind`. See the block above
7
+ * {@link ACCOUNT_CATEGORY_IDS} for the four rules that govern it.
8
+ */
9
+ import { z } from 'zod';
10
+ /**
11
+ * Account-graph classification — the ONE authority for the kind vocabulary.
12
+ *
13
+ * `personal` is the only kind minted by signup and the only one that carries
14
+ * its own credentials; every other kind is a child account created under a
15
+ * parent and operated through `account_members`. The API schema, the Drizzle
16
+ * table and the SDK all derive from this list rather than restating it, so a
17
+ * new kind is one edit here instead of four literals that can drift.
18
+ */
19
+ export type AccountKind = 'personal' | 'organization' | 'project' | 'bot' | 'channel';
20
+ /**
21
+ * The union is spelled out above and the array proves coverage BOTH ways
22
+ * (`satisfies` here, the `Gap` alias below) — the same shape this package's
23
+ * `ACCOUNT_CATEGORY_IDS` / `TRUST_TIERS` pairs use, and the one
24
+ * `db/schema/users.ts` mirrors to keep the `users_kind_check` CHECK honest.
25
+ *
26
+ * Deriving the union from the array instead would cost nothing here and be paid
27
+ * by consumers: `kind` travels into `@oxy.so/services` on every device-directory
28
+ * context (`deviceContextSchema.kind` → `DeviceContext` → the switcher rows),
29
+ * and an indexed-access type is materially more expensive to check there than a
30
+ * literal union.
31
+ */
32
+ export declare const ACCOUNT_KINDS: readonly ["personal", "organization", "project", "bot", "channel"];
33
+ /** `never` while `ACCOUNT_KINDS` covers the union. */
34
+ export type AccountKindGap = Exclude<AccountKind, (typeof ACCOUNT_KINDS)[number]>;
35
+ export declare const accountKindSchema: z.ZodEnum<["personal", "organization", "project", "bot", "channel"]>;
36
+ /**
37
+ * Kinds that may be CREATED as children of another account. Exactly
38
+ * `ACCOUNT_KINDS` minus `personal`, which is always a tree root.
39
+ */
40
+ export type ChildAccountKind = Exclude<AccountKind, 'personal'>;
41
+ export declare const CHILD_ACCOUNT_KINDS: readonly ["organization", "project", "bot", "channel"];
42
+ /** `never` while `CHILD_ACCOUNT_KINDS` covers the child union. */
43
+ export type ChildAccountKindGap = Exclude<ChildAccountKind, (typeof CHILD_ACCOUNT_KINDS)[number]>;
44
+ export declare const childAccountKindSchema: z.ZodEnum<["organization", "project", "bot", "channel"]>;
45
+ /**
46
+ * Whether an account of this kind may be the SUBJECT OF A DELEGATION — an
47
+ * application acting as it on some person's authority. `POST /internal/accounts/
48
+ * :id/service-switch` and the OAuth delegated subject both gate on this.
49
+ *
50
+ * It is NOT the question an account switcher asks. See
51
+ * {@link isOperatorSwitchTargetKind}, and the note below on why the difference
52
+ * is `bot`.
53
+ *
54
+ * Two kinds are refused, for opposite reasons:
55
+ *
56
+ * - `personal` is a human login, so assuming it would be impersonation.
57
+ * - `channel` is a CONTENT identity, not an operating one. A channel exists so
58
+ * that posts can be authored BY it; it is never a seat anybody occupies. Its
59
+ * operators act on it through their own membership, and an application
60
+ * publishes to it with its own credential. Refusing act-as is what makes
61
+ * "no login, ever" structural rather than incidental: no session can be
62
+ * minted whose subject is a channel, so no bearer exists that could add an
63
+ * auth method to one (every auth-method write resolves its target from the
64
+ * authenticated subject, never from a parameter).
65
+ *
66
+ * Consumers must gate on this predicate rather than testing `kind === 'personal'`,
67
+ * which silently admits every kind added after it was written.
68
+ */
69
+ export declare function isDelegatedActAsEligibleKind(kind: AccountKind | null | undefined): boolean;
70
+ /**
71
+ * Whether a PERSON may switch into an account of this kind — become it, in an
72
+ * account switcher, for the rest of their session.
73
+ *
74
+ * ## Why this is not the same question as {@link isDelegatedActAsEligibleKind}
75
+ *
76
+ * The two differ on exactly one kind, `bot`, and that difference is the whole
77
+ * reason both exist.
78
+ *
79
+ * **A bot is not something you become. It is something that operates on your
80
+ * behalf.** Its whole purpose is to act while nobody is present: an application
81
+ * holds a credential, names the human whose authority it borrows, and speaks as
82
+ * the bot. That is delegation, and it is what
83
+ * {@link isDelegatedActAsEligibleKind} admits it for.
84
+ *
85
+ * Handing a person the bot's seat instead inverts that. It puts a human inside
86
+ * the identity that exists to act without one, and it does so on the human's own
87
+ * device, next to their personal login — which is precisely what happened: a
88
+ * `bot` account held a live session on a person's device, offered to them by a
89
+ * switcher that had asked the delegation question by mistake.
90
+ *
91
+ * `channel` is refused here as well, for the reason set out above, and
92
+ * `personal` because assuming somebody else's login is impersonation.
93
+ *
94
+ * ## This is the narrower predicate, deliberately
95
+ *
96
+ * Everything a person may become, a service may also act as; the reverse does
97
+ * not hold. A caller that is unsure which question it is asking wants THIS one:
98
+ * being wrong here withholds an affordance, while being wrong the other way
99
+ * hands out a seat.
100
+ */
101
+ export declare function isOperatorSwitchTargetKind(kind: AccountKind | null | undefined): boolean;
102
+ /**
103
+ * Narrow an unknown value to an {@link AccountKind}.
104
+ *
105
+ * The user-DTO serializers read from structurally-permissive `unknown` sources
106
+ * (a Drizzle row, a Mongo document, an already-formatted object), so each one
107
+ * would otherwise hand-roll this check and they would drift on what counts.
108
+ */
109
+ export declare function isAccountKind(value: unknown): value is AccountKind;
110
+ /**
111
+ * Every account category, by stable id.
112
+ *
113
+ * Grouped by comment for readability only; the storage, the wire and the picker
114
+ * all treat this as one flat list. `other` is the escape hatch for an account
115
+ * that fits nothing here.
116
+ *
117
+ * TO ADD ONE: append an id (lowercase ASCII, `snake_case`) here, publish
118
+ * `@oxy.so/contracts`, then ship a migration that widens
119
+ * `users_account_categories_check` — never edit an existing migration — and add
120
+ * an `accounts.accountCategory.<id>` label to each client's locales.
121
+ *
122
+ * TO WITHDRAW ONE: leave the id here and add it to
123
+ * {@link RETIRED_ACCOUNT_CATEGORY_IDS}. See rule 3 above.
124
+ */
125
+ export declare const ACCOUNT_CATEGORY_IDS: readonly ["news", "politics", "business", "startup", "finance", "crypto", "marketplace", "retail", "real_estate", "agency", "landlord", "cooperative", "architecture", "technology", "software", "ai", "security", "automation", "science", "education", "books", "health", "fitness", "sports", "gaming", "music", "film", "podcast", "art", "photography", "comedy", "food", "travel", "fashion", "home_garden", "diy", "automotive", "animals", "family", "nonprofit", "government", "community", "activism", "environment", "religion", "other"];
126
+ export type AccountCategoryId = (typeof ACCOUNT_CATEGORY_IDS)[number];
127
+ /**
128
+ * Accepts EVERY id, withdrawn ones included — see rule 3.
129
+ *
130
+ * A schema that rejected a withdrawn id would 400 the whole request whenever a
131
+ * client round-trips the categories it was served, so an account that had
132
+ * picked one could no longer save its bio either. That is the same failure the
133
+ * nullable `bio` / `avatar` fix addressed, wearing a different hat.
134
+ */
135
+ export declare const accountCategoryIdSchema: z.ZodEnum<["news", "politics", "business", "startup", "finance", "crypto", "marketplace", "retail", "real_estate", "agency", "landlord", "cooperative", "architecture", "technology", "software", "ai", "security", "automation", "science", "education", "books", "health", "fitness", "sports", "gaming", "music", "film", "podcast", "art", "photography", "comedy", "food", "travel", "fashion", "home_garden", "diy", "automotive", "animals", "family", "nonprofit", "government", "community", "activism", "environment", "religion", "other"]>;
136
+ /**
137
+ * Ids withdrawn from the picker. Empty today.
138
+ *
139
+ * A withdrawn id keeps working everywhere it is already stored: it validates,
140
+ * it survives a round-trip save, it still renders from its label key, and it
141
+ * stays PRIMARY if it was primary. Nothing rewrites a stored list — a read-time
142
+ * or migration-time demotion would silently replace a choice its owner made,
143
+ * which is precisely what stable ids exist to prevent. The owner drops it on
144
+ * their next edit; until then it is honoured.
145
+ *
146
+ * What withdrawal changes is only this: the id leaves
147
+ * {@link SELECTABLE_ACCOUNT_CATEGORY_IDS}, so no picker offers it, and
148
+ * {@link newlyAddedRetiredCategories} refuses to let a write ADD it to an
149
+ * account that did not already have it.
150
+ */
151
+ export declare const RETIRED_ACCOUNT_CATEGORY_IDS: readonly AccountCategoryId[];
152
+ /** Whether a category may still be OFFERED. A stored one is readable either way. */
153
+ export declare function isSelectableAccountCategoryId(id: AccountCategoryId): boolean;
154
+ /** The ids a picker may offer, in declaration order. */
155
+ export declare const SELECTABLE_ACCOUNT_CATEGORY_IDS: readonly AccountCategoryId[];
156
+ /**
157
+ * Which of `next` are withdrawn ids the account did not already carry — i.e.
158
+ * the ones a write must be refused for.
159
+ *
160
+ * `retired` is a parameter rather than a module read so the rule can be
161
+ * exercised against a non-empty set while the production one is empty; a test
162
+ * over `RETIRED_ACCOUNT_CATEGORY_IDS` alone would pass vacuously today and stay
163
+ * passing if the rule were deleted.
164
+ */
165
+ export declare function newlyAddedRetiredCategories(next: readonly AccountCategoryId[], previous: readonly AccountCategoryId[], retired: readonly AccountCategoryId[]): AccountCategoryId[];
166
+ /**
167
+ * How many categories one account may carry.
168
+ *
169
+ * Four, not "as many as you like". Three reasons, in the order they bind:
170
+ *
171
+ * - The primary has to MEAN something. At ten categories the first element
172
+ * reads as a sort artifact rather than a choice, and rule 2 above is the
173
+ * entire mechanism by which a primary exists.
174
+ * - The profile RENDERS them as a row of chips; four labels of this length is
175
+ * what fits a phone-width profile header before the row wraps or truncates.
176
+ * - Four is enough to place a genuinely compound account without a tag cloud:
177
+ * a housing cooperative that is also a non-profit serving a local community
178
+ * spends `cooperative`, `nonprofit`, `community`, `real_estate` — and is the
179
+ * most compound real example in the ecosystem.
180
+ *
181
+ * One constant, read by the wire schema, the database CHECK and the picker, so
182
+ * changing it is one edit plus a migration.
183
+ */
184
+ export declare const MAX_ACCOUNT_CATEGORIES = 4;
185
+ /**
186
+ * An account's categories on the wire. ORDER IS MEANINGFUL — index 0 is the
187
+ * primary (rule 2).
188
+ *
189
+ * A duplicate is REJECTED rather than silently collapsed. De-duplicating would
190
+ * rewrite the caller's list, and any rewrite of this list can move which id sits
191
+ * at index 0 — so the one repair available here is the one that would break the
192
+ * property the list exists to carry. A duplicate only ever comes from a client
193
+ * bug, and a 400 naming the index is how that bug gets found.
194
+ */
195
+ export declare const accountCategoriesSchema: z.ZodEffects<z.ZodArray<z.ZodEnum<["news", "politics", "business", "startup", "finance", "crypto", "marketplace", "retail", "real_estate", "agency", "landlord", "cooperative", "architecture", "technology", "software", "ai", "security", "automation", "science", "education", "books", "health", "fitness", "sports", "gaming", "music", "film", "podcast", "art", "photography", "comedy", "food", "travel", "fashion", "home_garden", "diy", "automotive", "animals", "family", "nonprofit", "government", "community", "activism", "environment", "religion", "other"]>, "many">, ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[], ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[]>;
196
+ /**
197
+ * Kinds that may carry categories: every kind EXCEPT `personal`.
198
+ *
199
+ * A person has interests, not a sector — and their interests are not a
200
+ * classification anybody else gets to read off their profile. Spelled out
201
+ * positively, like {@link isDelegatedActAsEligibleKind} and for the same reason: a `kind
202
+ * !== 'personal'` test silently admits every kind invented after it was
203
+ * written, whereas this list forces whoever adds one to decide.
204
+ */
205
+ export declare const ACCOUNT_CATEGORY_KINDS: readonly ["organization", "project", "bot", "channel"];
206
+ export type AccountCategoryKind = (typeof ACCOUNT_CATEGORY_KINDS)[number];
207
+ /**
208
+ * Whether an account of this kind may carry categories.
209
+ *
210
+ * The API refuses the write and the `users_account_categories_kind_check`
211
+ * constraint makes it unrepresentable; both derive from
212
+ * {@link ACCOUNT_CATEGORY_KINDS}, so they cannot disagree.
213
+ */
214
+ export declare function kindAcceptsAccountCategories(kind: AccountKind | null | undefined): boolean;
215
+ /**
216
+ * POST /accounts — create a non-personal account under the caller's tree.
217
+ *
218
+ * No cross-field refinement guards `accountCategories`, and that is not an
219
+ * omission: `kind` here is a CHILD kind, and every child kind is in
220
+ * {@link ACCOUNT_CATEGORY_KINDS}, so `personal` is already unrepresentable on
221
+ * this route. The refinement the single-valued predecessor needed disappeared
222
+ * along with the restriction that made it necessary. A child kind that does NOT
223
+ * accept categories would break that reasoning silently, so
224
+ * `__tests__/accountGraph.test.ts` asserts the two lists agree.
225
+ */
226
+ export declare const createAccountRequestSchema: z.ZodEffects<z.ZodObject<{
227
+ parentAccountId: z.ZodOptional<z.ZodString>;
228
+ kind: z.ZodEnum<["organization", "project", "bot", "channel"]>;
229
+ /**
230
+ * The SAME policy a person's handle is held to. `users.username` is one unique
231
+ * index, so a managed account may not reserve a name a person could not ask
232
+ * for — and this route's predecessor (`.min(1).max(100)` here, `^[\w.-]+$`
233
+ * with no ceiling in the service) is how a one-character or dotted or
234
+ * 100-character handle became reachable for bots alone.
235
+ *
236
+ * A `bot` is held to that AND to the label its handle must end in. That half
237
+ * cannot live on this field — it depends on `kind`, a sibling — so it is in the
238
+ * `superRefine` below, which reports its issue against this path.
239
+ */
240
+ username: z.ZodString;
241
+ name: z.ZodOptional<z.ZodObject<{
242
+ first: z.ZodOptional<z.ZodString>;
243
+ last: z.ZodOptional<z.ZodString>;
244
+ displayName: z.ZodOptional<z.ZodString>;
245
+ }, "strip", z.ZodTypeAny, {
246
+ first?: string | undefined;
247
+ last?: string | undefined;
248
+ displayName?: string | undefined;
249
+ }, {
250
+ first?: string | undefined;
251
+ last?: string | undefined;
252
+ displayName?: string | undefined;
253
+ }>>;
254
+ bio: z.ZodOptional<z.ZodString>;
255
+ avatar: z.ZodOptional<z.ZodString>;
256
+ description: z.ZodOptional<z.ZodString>;
257
+ /**
258
+ * A named color preset KEY (`"blue"`, `"mint"`, …), never a hex value.
259
+ *
260
+ * Here at CREATION for the reason `isPrivateAccount` is, in miniature: for a
261
+ * managed account the color is a visual identity, and an account that is
262
+ * discoverable without one and acquires it on a second request is a face that
263
+ * changes by itself. One statement, one row, born looking like what its owner
264
+ * chose.
265
+ *
266
+ * The VALUE is checked in the API rather than here. The vocabulary is
267
+ * `USER_COLOR_PRESETS`, which is declared next to the `users_color_check` CHECK
268
+ * that is rendered from it — pinning the list a second time in this package
269
+ * would be a second source of truth for what the database accepts, and the two
270
+ * would drift apart silently. What this shape does is keep an over-long or
271
+ * non-string value from reaching the service at all.
272
+ */
273
+ color: z.ZodOptional<z.ZodString>;
274
+ /** Ordered, PRIMARY FIRST — see rule 2 above {@link ACCOUNT_CATEGORY_IDS}. */
275
+ accountCategories: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodEnum<["news", "politics", "business", "startup", "finance", "crypto", "marketplace", "retail", "real_estate", "agency", "landlord", "cooperative", "architecture", "technology", "software", "ai", "security", "automation", "science", "education", "books", "health", "fitness", "sports", "gaming", "music", "film", "podcast", "art", "photography", "comedy", "food", "travel", "fashion", "home_garden", "diy", "automotive", "animals", "family", "nonprofit", "government", "community", "activism", "environment", "religion", "other"]>, "many">, ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[], ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[]>>;
276
+ /**
277
+ * Create the account already opted OUT of discovery.
278
+ *
279
+ * ## Why this belongs at CREATION and not only on the privacy route
280
+ *
281
+ * Every account is born discoverable: the column defaults to `false` and
282
+ * nothing on the create path wrote it, so a new account appears in people
283
+ * search the instant it exists. For a human signing themselves up that is the
284
+ * right default and it is NOT changed here. For an account a program creates
285
+ * on someone's behalf — an agent, an unlaunched project, an organization for
286
+ * something not yet announced — it publishes the thing before its owner ever
287
+ * decided to.
288
+ *
289
+ * The alternative is a second call right after create, which is a window in
290
+ * which the account IS public, and a window whose closing depends on a second
291
+ * request succeeding. A field here has neither: one statement, one row, born
292
+ * in the state the caller asked for.
293
+ *
294
+ * ## It reuses the existing flag deliberately
295
+ *
296
+ * This is `privacy_is_private_account`, the same one `PUT /users/:id/privacy`
297
+ * toggles — not a new "published" column. A second visibility flag would be a
298
+ * second source of truth for one question, and the two would disagree.
299
+ *
300
+ * Inherited semantics, stated because reusing a flag means inheriting ALL of
301
+ * it: the account is kept out of people search, out of the follow-graph lists
302
+ * (`followers` / `following` / `mutuals`), out of `/similar` and out of the
303
+ * recommendation candidate pools, and its non-public, non-unlisted media
304
+ * becomes follower-gated. It does NOT hide the profile from someone who knows
305
+ * the handle, and it carries NO follow-approval flow — following is immediate
306
+ * and unilateral whatever this says, so nothing here creates a request queue
307
+ * nobody attends.
308
+ *
309
+ * ## Not conditioned on `kind`, on purpose
310
+ *
311
+ * The same reasoning as `accountCategories` above: this object does not
312
+ * refine on kind, and an unlaunched organization has exactly the problem an
313
+ * unpublished agent does. The discovery predicate never reads `kind`, so the
314
+ * remedy must not either.
315
+ */
316
+ isPrivateAccount: z.ZodOptional<z.ZodBoolean>;
317
+ }, "strip", z.ZodTypeAny, {
318
+ kind: "bot" | "organization" | "project" | "channel";
319
+ username: string;
320
+ parentAccountId?: string | undefined;
321
+ name?: {
322
+ first?: string | undefined;
323
+ last?: string | undefined;
324
+ displayName?: string | undefined;
325
+ } | undefined;
326
+ bio?: string | undefined;
327
+ avatar?: string | undefined;
328
+ description?: string | undefined;
329
+ color?: string | undefined;
330
+ accountCategories?: ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[] | undefined;
331
+ isPrivateAccount?: boolean | undefined;
332
+ }, {
333
+ kind: "bot" | "organization" | "project" | "channel";
334
+ username: string;
335
+ parentAccountId?: string | undefined;
336
+ name?: {
337
+ first?: string | undefined;
338
+ last?: string | undefined;
339
+ displayName?: string | undefined;
340
+ } | undefined;
341
+ bio?: string | undefined;
342
+ avatar?: string | undefined;
343
+ description?: string | undefined;
344
+ color?: string | undefined;
345
+ accountCategories?: ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[] | undefined;
346
+ isPrivateAccount?: boolean | undefined;
347
+ }>, {
348
+ kind: "bot" | "organization" | "project" | "channel";
349
+ username: string;
350
+ parentAccountId?: string | undefined;
351
+ name?: {
352
+ first?: string | undefined;
353
+ last?: string | undefined;
354
+ displayName?: string | undefined;
355
+ } | undefined;
356
+ bio?: string | undefined;
357
+ avatar?: string | undefined;
358
+ description?: string | undefined;
359
+ color?: string | undefined;
360
+ accountCategories?: ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[] | undefined;
361
+ isPrivateAccount?: boolean | undefined;
362
+ }, {
363
+ kind: "bot" | "organization" | "project" | "channel";
364
+ username: string;
365
+ parentAccountId?: string | undefined;
366
+ name?: {
367
+ first?: string | undefined;
368
+ last?: string | undefined;
369
+ displayName?: string | undefined;
370
+ } | undefined;
371
+ bio?: string | undefined;
372
+ avatar?: string | undefined;
373
+ description?: string | undefined;
374
+ color?: string | undefined;
375
+ accountCategories?: ("news" | "politics" | "business" | "startup" | "finance" | "crypto" | "marketplace" | "retail" | "real_estate" | "agency" | "landlord" | "cooperative" | "architecture" | "technology" | "software" | "ai" | "security" | "automation" | "science" | "education" | "books" | "health" | "fitness" | "sports" | "gaming" | "music" | "film" | "podcast" | "art" | "photography" | "comedy" | "food" | "travel" | "fashion" | "home_garden" | "diy" | "automotive" | "animals" | "family" | "nonprofit" | "government" | "community" | "activism" | "environment" | "religion" | "other")[] | undefined;
376
+ isPrivateAccount?: boolean | undefined;
377
+ }>;
378
+ export type CreateAccountRequest = z.infer<typeof createAccountRequestSchema>;