@oxyhq/contracts 0.2.0 → 0.3.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.
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Self-sovereign identity API contracts.
3
+ *
4
+ * SINGLE SOURCE OF TRUTH for the wire shape of Oxy's AtProto/Bluesky-flavoured
5
+ * identity & portability layer: the W3C DID document the API derives on demand,
6
+ * the signed-record envelope clients sign with their cryptographic key (and the
7
+ * server verifies), the verified-domain badge, the auth-method ↔ DID
8
+ * verification-method mapping, and the signed data-export ("credible exit")
9
+ * bundle. The API validates its OUTPUT against these schemas; every consumer
10
+ * (the Commons vault app, `@oxyhq/core`'s identity mixin) validates its INPUT
11
+ * against the same definitions, so producer and consumers cannot drift.
12
+ *
13
+ * Design anchors (from the identity-layer plan):
14
+ * - DID = `did:web:oxy.so:u:<userId>` — anchored on the stable account id, NOT
15
+ * the keypair. The keypair is a *verification method* that maps 1:1 to the
16
+ * existing `authMethods[]`. Custodial (password-only) users get a DID
17
+ * controlled solely by Oxy (`OXY_DID`); creating a Commons key upgrades them
18
+ * to self-sovereign (`controller = [userDid, OXY_DID]`); fully reversible.
19
+ * - Verification methods use the secp256k1 `EcdsaSecp256k1VerificationKey2019`
20
+ * type with `publicKeyHex` for now (a `Multikey`/`publicKeyMultibase` form may
21
+ * be added later — see the plan's open risks).
22
+ * - Signed records carry an envelope whose signing input is the canonical-JSON
23
+ * of every field EXCEPT `publicKey` and `signature`; `alg` is
24
+ * `ES256K-DER-SHA256` (secp256k1 over the SHA-256 of the canonical bytes,
25
+ * DER-encoded signature) — the same scheme `SignatureService` uses.
26
+ *
27
+ * Explicit-`interface` exports (DidDocument, SignedRecordEnvelope, ExportBundle,
28
+ * VerifiedDomain, AuthMethodsResponse and their sub-parts) follow the same
29
+ * rationale as `UserNameResponse` in `./userResponse`: a `z.infer<>` of a nested
30
+ * object schema can degrade under a consumer's `moduleResolution: "node"`
31
+ * (node10) resolution, so the load-bearing response shapes are declared as
32
+ * literal interfaces and the runtime schemas are annotated `z.ZodType<Interface>`
33
+ * — the emitted `.d.ts` then states the field types verbatim and survives BOTH
34
+ * `node` and `bundler` resolution. Request schemas (no nested-object hazard) are
35
+ * inferred via `z.infer<>`.
36
+ *
37
+ * Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
38
+ * `require()`).
39
+ */
40
+ import { z } from 'zod';
41
+ /**
42
+ * A single DID verification method. Mirrors the secp256k1 key entries the API
43
+ * derives from `User.publicKey` + each `authMethods[]` of type `identity`.
44
+ * `id` is a fragment reference within the DID document (e.g.
45
+ * `did:web:oxy.so:u:<id>#key-1`); `controller` is the controlling DID;
46
+ * `publicKeyHex` is the uncompressed/compressed secp256k1 public key in hex.
47
+ */
48
+ export interface VerificationMethod {
49
+ id: string;
50
+ type: 'EcdsaSecp256k1VerificationKey2019';
51
+ controller: string;
52
+ publicKeyHex: string;
53
+ }
54
+ export declare const verificationMethodSchema: z.ZodType<VerificationMethod>;
55
+ /**
56
+ * A DID service entry (the `service[]` array). Oxy publishes its API root and
57
+ * profile endpoints here so a resolver can discover where to fetch the user's
58
+ * data. `serviceEndpoint` is a URL string.
59
+ */
60
+ export interface DidService {
61
+ id: string;
62
+ type: string;
63
+ serviceEndpoint: string;
64
+ }
65
+ export declare const didServiceSchema: z.ZodType<DidService>;
66
+ /**
67
+ * A W3C DID document derived on demand by the API (no stored document).
68
+ *
69
+ * - `controller` is `[userDid, OXY_DID]` for a self-sovereign account (it holds
70
+ * at least one `identity` verification method) or `[OXY_DID]` for a custodial
71
+ * (password-only) account.
72
+ * - `verificationMethod[]` is composed from the account's secp256k1 keys.
73
+ * - `authentication` / `assertionMethod` reference verification-method ids.
74
+ * - `alsoKnownAs[]` carries the account's other identifiers (`acct:` handle,
75
+ * profile URL, any verified-domain URLs).
76
+ */
77
+ export interface DidDocument {
78
+ '@context': string[];
79
+ id: string;
80
+ controller: string[];
81
+ verificationMethod: VerificationMethod[];
82
+ authentication: string[];
83
+ assertionMethod: string[];
84
+ alsoKnownAs: string[];
85
+ service: DidService[];
86
+ }
87
+ export declare const didDocumentSchema: z.ZodType<DidDocument>;
88
+ /**
89
+ * A signed record envelope. `record` is the arbitrary payload; the signing
90
+ * input is the canonical-JSON of every envelope field EXCEPT `publicKey` and
91
+ * `signature`. `subject` and `issuer` are DIDs (the subject the record is about
92
+ * and the signer's DID — equal for self-issued records, `OXY_DID` for a
93
+ * custodial provenance attestation). `issuedAt` is epoch milliseconds.
94
+ */
95
+ export interface SignedRecordEnvelope {
96
+ version: 1;
97
+ type: 'identity' | 'profile';
98
+ subject: string;
99
+ issuer: string;
100
+ record: Record<string, unknown>;
101
+ issuedAt: number;
102
+ publicKey: string;
103
+ alg: 'ES256K-DER-SHA256';
104
+ signature: string;
105
+ }
106
+ export declare const signedRecordEnvelopeSchema: z.ZodType<SignedRecordEnvelope>;
107
+ /**
108
+ * A proven domain ownership badge. `method` records how ownership was proven —
109
+ * a DNS-TXT record (`_oxy-identity.<domain>`) or a `/.well-known/oxy-domain`
110
+ * HTTP file. `verifiedAt` is a string on the wire (ISO timestamp) but accepts a
111
+ * `Date` so the API can validate its own pre-serialization model objects.
112
+ */
113
+ export interface VerifiedDomain {
114
+ domain: string;
115
+ verifiedAt: string | Date;
116
+ method: 'dns-txt' | 'well-known';
117
+ }
118
+ export declare const verifiedDomainSchema: z.ZodType<VerifiedDomain>;
119
+ /** Request body for `POST /identity/domains` — the domain to start verifying. */
120
+ export declare const domainVerificationRequestSchema: z.ZodObject<{
121
+ domain: z.ZodString;
122
+ }, "strip", z.ZodTypeAny, {
123
+ domain: string;
124
+ }, {
125
+ domain: string;
126
+ }>;
127
+ export type DomainVerificationRequest = z.infer<typeof domainVerificationRequestSchema>;
128
+ /**
129
+ * The instructions the API returns when a domain verification is requested. The
130
+ * caller may prove ownership EITHER by publishing the `dns` TXT record OR by
131
+ * serving the `wellKnown` file; either path then satisfies
132
+ * `POST /identity/domains/:domain/verify`.
133
+ */
134
+ export declare const domainVerificationInstructionsSchema: z.ZodObject<{
135
+ domain: z.ZodString;
136
+ token: z.ZodString;
137
+ dns: z.ZodObject<{
138
+ name: z.ZodString;
139
+ value: z.ZodString;
140
+ }, "strip", z.ZodTypeAny, {
141
+ value: string;
142
+ name: string;
143
+ }, {
144
+ value: string;
145
+ name: string;
146
+ }>;
147
+ wellKnown: z.ZodObject<{
148
+ url: z.ZodString;
149
+ body: z.ZodString;
150
+ }, "strip", z.ZodTypeAny, {
151
+ url: string;
152
+ body: string;
153
+ }, {
154
+ url: string;
155
+ body: string;
156
+ }>;
157
+ }, "strip", z.ZodTypeAny, {
158
+ domain: string;
159
+ token: string;
160
+ dns: {
161
+ value: string;
162
+ name: string;
163
+ };
164
+ wellKnown: {
165
+ url: string;
166
+ body: string;
167
+ };
168
+ }, {
169
+ domain: string;
170
+ token: string;
171
+ dns: {
172
+ value: string;
173
+ name: string;
174
+ };
175
+ wellKnown: {
176
+ url: string;
177
+ body: string;
178
+ };
179
+ }>;
180
+ export type DomainVerificationInstructions = z.infer<typeof domainVerificationInstructionsSchema>;
181
+ /**
182
+ * One linked authentication method. Mirrors a `User.authMethods[]` entry.
183
+ * `verificationMethodId` is present for `identity` methods (a key) and absent
184
+ * for `password`/social methods, linking the auth method to its DID
185
+ * verification-method fragment.
186
+ */
187
+ export interface AuthMethodEntry {
188
+ type: 'identity' | 'password' | 'google' | 'apple' | 'github';
189
+ linkedAt: string | Date;
190
+ verificationMethodId?: string;
191
+ }
192
+ export declare const authMethodEntrySchema: z.ZodType<AuthMethodEntry>;
193
+ /**
194
+ * Wire shape of `GET /auth/methods` — the account's DID plus every linked
195
+ * authentication method.
196
+ */
197
+ export interface AuthMethodsResponse {
198
+ did: string;
199
+ methods: AuthMethodEntry[];
200
+ }
201
+ export declare const authMethodsResponseSchema: z.ZodType<AuthMethodsResponse>;
202
+ /**
203
+ * A cryptographic attestation over the canonical-JSON of an export bundle.
204
+ * Reused for both the mandatory Oxy provenance `attestation` (signed with the
205
+ * Oxy custodial key) and the optional client `proof` (signed with the user's
206
+ * own key when they hold one). `signedAt` is epoch milliseconds.
207
+ */
208
+ export interface ExportAttestation {
209
+ issuer: string;
210
+ publicKey: string;
211
+ alg: 'ES256K-DER-SHA256';
212
+ signature: string;
213
+ signedAt: number;
214
+ }
215
+ export declare const exportAttestationSchema: z.ZodType<ExportAttestation>;
216
+ /**
217
+ * The signed, open-format data-export bundle from `GET /users/me/export`. A
218
+ * portable snapshot of the account: its DID document, profile, verified
219
+ * domains, auth methods (no secrets), published signed records, per-app data,
220
+ * and social graph.
221
+ *
222
+ * `attestation` is the Oxy custodial provenance signature. It is `null` only
223
+ * when the Oxy custodial signing key (`OXY_PRIVATE_KEY`) is unset (dev /
224
+ * pre-prod); in production it is always present. Carries an optional client
225
+ * `proof` when the user signed the bundle with their own key.
226
+ */
227
+ export interface ExportBundle {
228
+ '$schema': string;
229
+ exportedAt: string;
230
+ did: string;
231
+ didDocument: DidDocument;
232
+ profile: Record<string, unknown>;
233
+ verifiedDomains: VerifiedDomain[];
234
+ authMethods: AuthMethodEntry[];
235
+ signedRecords: SignedRecordEnvelope[];
236
+ appData: Record<string, unknown>[];
237
+ social: {
238
+ following: string[];
239
+ followers: string[];
240
+ };
241
+ attestation: ExportAttestation | null;
242
+ proof?: ExportAttestation;
243
+ }
244
+ export declare const exportBundleSchema: z.ZodType<ExportBundle>;
@@ -17,3 +17,5 @@ export { fedcmTokenPayloadSchema, } from './fedcmToken';
17
17
  export type { FedcmTokenPayload, } from './fedcmToken';
18
18
  export { recommendationExcludeTypeSchema, recommendationBoostSchema, recommendationSignalWeightsSchema, recommendationRequestSchema, recommendationCountSchema, recommendationItemSchema, recommendationResponseSchema, appEndorsementInputSchema, appInterestInputSchema, appUserSignalIngestSchema, } from './recommendations';
19
19
  export type { RecommendationExcludeType, RecommendationBoost, RecommendationSignalWeights, RecommendationRequest, RecommendationCount, RecommendationItem, RecommendationResponse, AppEndorsementInput, AppInterestInput, AppUserSignalIngest, } from './recommendations';
20
+ export { verificationMethodSchema, didServiceSchema, didDocumentSchema, signedRecordEnvelopeSchema, verifiedDomainSchema, domainVerificationRequestSchema, domainVerificationInstructionsSchema, authMethodEntrySchema, authMethodsResponseSchema, exportAttestationSchema, exportBundleSchema, } from './identity';
21
+ export type { VerificationMethod, DidService, DidDocument, SignedRecordEnvelope, VerifiedDomain, DomainVerificationRequest, DomainVerificationInstructions, AuthMethodEntry, AuthMethodsResponse, ExportAttestation, ExportBundle, } from './identity';
@@ -164,11 +164,11 @@ export declare const recommendationCountSchema: z.ZodObject<{
164
164
  followers: z.ZodNumber;
165
165
  following: z.ZodNumber;
166
166
  }, "strip", z.ZodTypeAny, {
167
- followers: number;
168
167
  following: number;
169
- }, {
170
168
  followers: number;
169
+ }, {
171
170
  following: number;
171
+ followers: number;
172
172
  }>;
173
173
  export type RecommendationCount = z.infer<typeof recommendationCountSchema>;
174
174
  /**
@@ -181,22 +181,7 @@ export type RecommendationCount = z.infer<typeof recommendationCountSchema>;
181
181
  export declare const recommendationItemSchema: z.ZodObject<{
182
182
  id: z.ZodString;
183
183
  username: z.ZodOptional<z.ZodString>;
184
- name: z.ZodObject<{
185
- first: z.ZodOptional<z.ZodString>;
186
- last: z.ZodOptional<z.ZodString>;
187
- full: z.ZodOptional<z.ZodString>;
188
- displayName: z.ZodString;
189
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
190
- first: z.ZodOptional<z.ZodString>;
191
- last: z.ZodOptional<z.ZodString>;
192
- full: z.ZodOptional<z.ZodString>;
193
- displayName: z.ZodString;
194
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
195
- first: z.ZodOptional<z.ZodString>;
196
- last: z.ZodOptional<z.ZodString>;
197
- full: z.ZodOptional<z.ZodString>;
198
- displayName: z.ZodString;
199
- }, z.ZodTypeAny, "passthrough">>;
184
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
200
185
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
201
186
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
202
187
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -212,31 +197,16 @@ export declare const recommendationItemSchema: z.ZodObject<{
212
197
  followers: z.ZodNumber;
213
198
  following: z.ZodNumber;
214
199
  }, "strip", z.ZodTypeAny, {
215
- followers: number;
216
200
  following: number;
217
- }, {
218
201
  followers: number;
202
+ }, {
219
203
  following: number;
204
+ followers: number;
220
205
  }>;
221
206
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
222
207
  id: z.ZodString;
223
208
  username: z.ZodOptional<z.ZodString>;
224
- name: z.ZodObject<{
225
- first: z.ZodOptional<z.ZodString>;
226
- last: z.ZodOptional<z.ZodString>;
227
- full: z.ZodOptional<z.ZodString>;
228
- displayName: z.ZodString;
229
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
230
- first: z.ZodOptional<z.ZodString>;
231
- last: z.ZodOptional<z.ZodString>;
232
- full: z.ZodOptional<z.ZodString>;
233
- displayName: z.ZodString;
234
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
235
- first: z.ZodOptional<z.ZodString>;
236
- last: z.ZodOptional<z.ZodString>;
237
- full: z.ZodOptional<z.ZodString>;
238
- displayName: z.ZodString;
239
- }, z.ZodTypeAny, "passthrough">>;
209
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
240
210
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
241
211
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
242
212
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -252,31 +222,16 @@ export declare const recommendationItemSchema: z.ZodObject<{
252
222
  followers: z.ZodNumber;
253
223
  following: z.ZodNumber;
254
224
  }, "strip", z.ZodTypeAny, {
255
- followers: number;
256
225
  following: number;
257
- }, {
258
226
  followers: number;
227
+ }, {
259
228
  following: number;
229
+ followers: number;
260
230
  }>;
261
231
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
262
232
  id: z.ZodString;
263
233
  username: z.ZodOptional<z.ZodString>;
264
- name: z.ZodObject<{
265
- first: z.ZodOptional<z.ZodString>;
266
- last: z.ZodOptional<z.ZodString>;
267
- full: z.ZodOptional<z.ZodString>;
268
- displayName: z.ZodString;
269
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
270
- first: z.ZodOptional<z.ZodString>;
271
- last: z.ZodOptional<z.ZodString>;
272
- full: z.ZodOptional<z.ZodString>;
273
- displayName: z.ZodString;
274
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
275
- first: z.ZodOptional<z.ZodString>;
276
- last: z.ZodOptional<z.ZodString>;
277
- full: z.ZodOptional<z.ZodString>;
278
- displayName: z.ZodString;
279
- }, z.ZodTypeAny, "passthrough">>;
234
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
280
235
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
281
236
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
282
237
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -292,11 +247,11 @@ export declare const recommendationItemSchema: z.ZodObject<{
292
247
  followers: z.ZodNumber;
293
248
  following: z.ZodNumber;
294
249
  }, "strip", z.ZodTypeAny, {
295
- followers: number;
296
250
  following: number;
297
- }, {
298
251
  followers: number;
252
+ }, {
299
253
  following: number;
254
+ followers: number;
300
255
  }>;
301
256
  }, z.ZodTypeAny, "passthrough">>;
302
257
  export type RecommendationItem = z.infer<typeof recommendationItemSchema>;
@@ -304,22 +259,7 @@ export type RecommendationItem = z.infer<typeof recommendationItemSchema>;
304
259
  export declare const recommendationResponseSchema: z.ZodArray<z.ZodObject<{
305
260
  id: z.ZodString;
306
261
  username: z.ZodOptional<z.ZodString>;
307
- name: z.ZodObject<{
308
- first: z.ZodOptional<z.ZodString>;
309
- last: z.ZodOptional<z.ZodString>;
310
- full: z.ZodOptional<z.ZodString>;
311
- displayName: z.ZodString;
312
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
313
- first: z.ZodOptional<z.ZodString>;
314
- last: z.ZodOptional<z.ZodString>;
315
- full: z.ZodOptional<z.ZodString>;
316
- displayName: z.ZodString;
317
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
318
- first: z.ZodOptional<z.ZodString>;
319
- last: z.ZodOptional<z.ZodString>;
320
- full: z.ZodOptional<z.ZodString>;
321
- displayName: z.ZodString;
322
- }, z.ZodTypeAny, "passthrough">>;
262
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
323
263
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
324
264
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
325
265
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -335,31 +275,16 @@ export declare const recommendationResponseSchema: z.ZodArray<z.ZodObject<{
335
275
  followers: z.ZodNumber;
336
276
  following: z.ZodNumber;
337
277
  }, "strip", z.ZodTypeAny, {
338
- followers: number;
339
278
  following: number;
340
- }, {
341
279
  followers: number;
280
+ }, {
342
281
  following: number;
282
+ followers: number;
343
283
  }>;
344
284
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
345
285
  id: z.ZodString;
346
286
  username: z.ZodOptional<z.ZodString>;
347
- name: z.ZodObject<{
348
- first: z.ZodOptional<z.ZodString>;
349
- last: z.ZodOptional<z.ZodString>;
350
- full: z.ZodOptional<z.ZodString>;
351
- displayName: z.ZodString;
352
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
353
- first: z.ZodOptional<z.ZodString>;
354
- last: z.ZodOptional<z.ZodString>;
355
- full: z.ZodOptional<z.ZodString>;
356
- displayName: z.ZodString;
357
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
358
- first: z.ZodOptional<z.ZodString>;
359
- last: z.ZodOptional<z.ZodString>;
360
- full: z.ZodOptional<z.ZodString>;
361
- displayName: z.ZodString;
362
- }, z.ZodTypeAny, "passthrough">>;
287
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
363
288
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
364
289
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
365
290
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -375,31 +300,16 @@ export declare const recommendationResponseSchema: z.ZodArray<z.ZodObject<{
375
300
  followers: z.ZodNumber;
376
301
  following: z.ZodNumber;
377
302
  }, "strip", z.ZodTypeAny, {
378
- followers: number;
379
303
  following: number;
380
- }, {
381
304
  followers: number;
305
+ }, {
382
306
  following: number;
307
+ followers: number;
383
308
  }>;
384
309
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
385
310
  id: z.ZodString;
386
311
  username: z.ZodOptional<z.ZodString>;
387
- name: z.ZodObject<{
388
- first: z.ZodOptional<z.ZodString>;
389
- last: z.ZodOptional<z.ZodString>;
390
- full: z.ZodOptional<z.ZodString>;
391
- displayName: z.ZodString;
392
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
393
- first: z.ZodOptional<z.ZodString>;
394
- last: z.ZodOptional<z.ZodString>;
395
- full: z.ZodOptional<z.ZodString>;
396
- displayName: z.ZodString;
397
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
398
- first: z.ZodOptional<z.ZodString>;
399
- last: z.ZodOptional<z.ZodString>;
400
- full: z.ZodOptional<z.ZodString>;
401
- displayName: z.ZodString;
402
- }, z.ZodTypeAny, "passthrough">>;
312
+ name: z.ZodType<import("./userResponse").UserNameResponse, z.ZodTypeDef, import("./userResponse").UserNameResponse>;
403
313
  avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
404
314
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
405
315
  verified: z.ZodOptional<z.ZodBoolean>;
@@ -415,11 +325,11 @@ export declare const recommendationResponseSchema: z.ZodArray<z.ZodObject<{
415
325
  followers: z.ZodNumber;
416
326
  following: z.ZodNumber;
417
327
  }, "strip", z.ZodTypeAny, {
418
- followers: number;
419
328
  following: number;
420
- }, {
421
329
  followers: number;
330
+ }, {
422
331
  following: number;
332
+ followers: number;
423
333
  }>;
424
334
  }, z.ZodTypeAny, "passthrough">>, "many">;
425
335
  export type RecommendationResponse = z.infer<typeof recommendationResponseSchema>;