@oxyhq/contracts 0.1.1 → 0.2.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,59 @@
1
+ /**
2
+ * Canonical contract for the FedCM ID-token JWT payload.
3
+ *
4
+ * SINGLE SOURCE OF TRUTH for the decoded claims of the HS256 ID token the auth
5
+ * IdP (`auth.oxy.so`) signs and `POST /fedcm/exchange` consumes. The API decodes
6
+ * the JWT, verifies its signature, then validates the resulting claim object
7
+ * against this schema before trusting any field — a malformed payload (e.g. a
8
+ * forged token whose signature happened to match but whose body is the wrong
9
+ * shape) is rejected at the boundary instead of being cast and used.
10
+ *
11
+ * Validation philosophy — match the existing exchange behaviour exactly:
12
+ * - This schema validates the STRUCTURAL shape of the decoded claims only
13
+ * (types of the fields, not their presence or business-rule validity).
14
+ * - `sub` / `aud` / `nonce` / `iss` / `exp` presence + value checks remain in
15
+ * `fedcm.service.exchangeIdToken`, which returns the specific
16
+ * `missing_required_fields` / `invalid_issuer` / `token_expired` errors. So
17
+ * every field is `.optional()` here: a token missing `nonce` must still reach
18
+ * the `missing_required_fields` branch, not be rejected as a malformed token.
19
+ * - `.passthrough()` preserves any additional claims the IdP may add without a
20
+ * coordinated contract bump.
21
+ *
22
+ * Faithful to the producer:
23
+ * - `packages/auth/server/index.ts` `mintSessionForClient` — builds the
24
+ * assertion with `iss` (central issuer), `sub` (user id), `aud` (RP origin),
25
+ * `exp` / `iat` (numeric epoch seconds), and `nonce` (the server-minted,
26
+ * origin-bound nonce).
27
+ *
28
+ * Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
29
+ * `require()`).
30
+ */
31
+ import { z } from 'zod';
32
+ /**
33
+ * Decoded FedCM ID-token claims. Every field is optional because presence is
34
+ * enforced downstream (see module doc); the schema's job is to guarantee that
35
+ * any present claim has the correct primitive type before it is read.
36
+ */
37
+ export declare const fedcmTokenPayloadSchema: z.ZodObject<{
38
+ iss: z.ZodOptional<z.ZodString>;
39
+ sub: z.ZodOptional<z.ZodString>;
40
+ aud: z.ZodOptional<z.ZodString>;
41
+ exp: z.ZodOptional<z.ZodNumber>;
42
+ iat: z.ZodOptional<z.ZodNumber>;
43
+ nonce: z.ZodOptional<z.ZodString>;
44
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
45
+ iss: z.ZodOptional<z.ZodString>;
46
+ sub: z.ZodOptional<z.ZodString>;
47
+ aud: z.ZodOptional<z.ZodString>;
48
+ exp: z.ZodOptional<z.ZodNumber>;
49
+ iat: z.ZodOptional<z.ZodNumber>;
50
+ nonce: z.ZodOptional<z.ZodString>;
51
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
52
+ iss: z.ZodOptional<z.ZodString>;
53
+ sub: z.ZodOptional<z.ZodString>;
54
+ aud: z.ZodOptional<z.ZodString>;
55
+ exp: z.ZodOptional<z.ZodNumber>;
56
+ iat: z.ZodOptional<z.ZodNumber>;
57
+ nonce: z.ZodOptional<z.ZodString>;
58
+ }, z.ZodTypeAny, "passthrough">>;
59
+ export type FedcmTokenPayload = z.infer<typeof fedcmTokenPayloadSchema>;
@@ -13,3 +13,7 @@ export { userNameSchema, userResponseSchema, userProfileUpdateSchema, refreshAll
13
13
  export type { UserNameResponse, UserResponse, UserProfileUpdate, RefreshAllAccountResponse, RefreshAllResponseContract, CurrentUserResponseContract, DeviceSessionAccountResponse, DeviceSessionsResponseContract, } from './userResponse';
14
14
  export { applicationTypeSchema, publicApplicationSchema, sessionStatusSchema, } from './sessionStatus';
15
15
  export type { ApplicationTypeContract, PublicApplicationResponse, SessionStatusResponse, } from './sessionStatus';
16
+ export { fedcmTokenPayloadSchema, } from './fedcmToken';
17
+ export type { FedcmTokenPayload, } from './fedcmToken';
18
+ export { recommendationExcludeTypeSchema, recommendationBoostSchema, recommendationSignalWeightsSchema, recommendationRequestSchema, recommendationCountSchema, recommendationItemSchema, recommendationResponseSchema, appEndorsementInputSchema, appInterestInputSchema, appUserSignalIngestSchema, } from './recommendations';
19
+ export type { RecommendationExcludeType, RecommendationBoost, RecommendationSignalWeights, RecommendationRequest, RecommendationCount, RecommendationItem, RecommendationResponse, AppEndorsementInput, AppInterestInput, AppUserSignalIngest, } from './recommendations';
@@ -0,0 +1,535 @@
1
+ /**
2
+ * Recommendation-engine API contracts.
3
+ *
4
+ * SINGLE SOURCE OF TRUTH for the wire shape of the reputation-weighted
5
+ * profile-recommendation surface (`POST /profiles/recommendations`) and the
6
+ * cross-app signal-ingest endpoint (`POST /app-signals/ingest`). The API
7
+ * validates its INPUT/OUTPUT against these schemas; consumer SDKs validate the
8
+ * same definitions, so the producer and every consumer cannot drift.
9
+ *
10
+ * Platform-agnostic — zod is the only runtime dependency (no react / react-native
11
+ * / expo, ESM-safe).
12
+ */
13
+ import { z } from 'zod';
14
+ /** User-type filters a caller may exclude from the recommendation surface. */
15
+ export declare const recommendationExcludeTypeSchema: z.ZodEnum<["federated", "agent", "automated"]>;
16
+ export type RecommendationExcludeType = z.infer<typeof recommendationExcludeTypeSchema>;
17
+ /**
18
+ * A caller-supplied editorial boost. `userIds` are nudged up (or down, for a
19
+ * negative weight) in the ranking; the optional `reason` is for audit/telemetry
20
+ * only and never surfaced to end users. Boost members still pass the eligibility
21
+ * gate — a boost cannot resurrect a private/restricted/ineligible account.
22
+ */
23
+ export declare const recommendationBoostSchema: z.ZodObject<{
24
+ userIds: z.ZodArray<z.ZodString, "many">;
25
+ weight: z.ZodNumber;
26
+ reason: z.ZodOptional<z.ZodString>;
27
+ }, "strip", z.ZodTypeAny, {
28
+ userIds: string[];
29
+ weight: number;
30
+ reason?: string | undefined;
31
+ }, {
32
+ userIds: string[];
33
+ weight: number;
34
+ reason?: string | undefined;
35
+ }>;
36
+ export type RecommendationBoost = z.infer<typeof recommendationBoostSchema>;
37
+ /**
38
+ * Per-request overrides for the scoring signal weights. Every key is optional
39
+ * and clamped server-side to the resolved weight profile's allowed range — a
40
+ * caller can re-weight signals but never escape the profile's bounds.
41
+ */
42
+ export declare const recommendationSignalWeightsSchema: z.ZodObject<{
43
+ graph: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
44
+ completeness: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
45
+ verified: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
46
+ curation: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
47
+ interest: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
48
+ appBoost: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
49
+ repCandidate: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
50
+ }, "strip", z.ZodTypeAny, {
51
+ verified?: number | undefined;
52
+ graph?: number | undefined;
53
+ completeness?: number | undefined;
54
+ curation?: number | undefined;
55
+ interest?: number | undefined;
56
+ appBoost?: number | undefined;
57
+ repCandidate?: number | undefined;
58
+ }, {
59
+ verified?: number | undefined;
60
+ graph?: number | undefined;
61
+ completeness?: number | undefined;
62
+ curation?: number | undefined;
63
+ interest?: number | undefined;
64
+ appBoost?: number | undefined;
65
+ repCandidate?: number | undefined;
66
+ }>;
67
+ export type RecommendationSignalWeights = z.infer<typeof recommendationSignalWeightsSchema>;
68
+ /**
69
+ * Request body for `POST /profiles/recommendations`.
70
+ *
71
+ * `clientId` selects the per-app weight profile (the Application `_id`); when
72
+ * omitted the default profile is used. `excludeIds` removes accounts the caller
73
+ * has already seen/handled; `boosts` and `signalWeights` let the caller bias the
74
+ * ranking within server-enforced bounds.
75
+ */
76
+ export declare const recommendationRequestSchema: z.ZodObject<{
77
+ clientId: z.ZodOptional<z.ZodString>;
78
+ limit: z.ZodOptional<z.ZodNumber>;
79
+ offset: z.ZodOptional<z.ZodNumber>;
80
+ excludeTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["federated", "agent", "automated"]>, "many">>;
81
+ excludeIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
82
+ boosts: z.ZodOptional<z.ZodArray<z.ZodObject<{
83
+ userIds: z.ZodArray<z.ZodString, "many">;
84
+ weight: z.ZodNumber;
85
+ reason: z.ZodOptional<z.ZodString>;
86
+ }, "strip", z.ZodTypeAny, {
87
+ userIds: string[];
88
+ weight: number;
89
+ reason?: string | undefined;
90
+ }, {
91
+ userIds: string[];
92
+ weight: number;
93
+ reason?: string | undefined;
94
+ }>, "many">>;
95
+ signalWeights: z.ZodOptional<z.ZodObject<{
96
+ graph: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
97
+ completeness: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
98
+ verified: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
99
+ curation: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
100
+ interest: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
101
+ appBoost: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
102
+ repCandidate: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
103
+ }, "strip", z.ZodTypeAny, {
104
+ verified?: number | undefined;
105
+ graph?: number | undefined;
106
+ completeness?: number | undefined;
107
+ curation?: number | undefined;
108
+ interest?: number | undefined;
109
+ appBoost?: number | undefined;
110
+ repCandidate?: number | undefined;
111
+ }, {
112
+ verified?: number | undefined;
113
+ graph?: number | undefined;
114
+ completeness?: number | undefined;
115
+ curation?: number | undefined;
116
+ interest?: number | undefined;
117
+ appBoost?: number | undefined;
118
+ repCandidate?: number | undefined;
119
+ }>>;
120
+ }, "strip", z.ZodTypeAny, {
121
+ clientId?: string | undefined;
122
+ limit?: number | undefined;
123
+ offset?: number | undefined;
124
+ excludeTypes?: ("federated" | "agent" | "automated")[] | undefined;
125
+ excludeIds?: string[] | undefined;
126
+ boosts?: {
127
+ userIds: string[];
128
+ weight: number;
129
+ reason?: string | undefined;
130
+ }[] | undefined;
131
+ signalWeights?: {
132
+ verified?: number | undefined;
133
+ graph?: number | undefined;
134
+ completeness?: number | undefined;
135
+ curation?: number | undefined;
136
+ interest?: number | undefined;
137
+ appBoost?: number | undefined;
138
+ repCandidate?: number | undefined;
139
+ } | undefined;
140
+ }, {
141
+ clientId?: string | undefined;
142
+ limit?: number | undefined;
143
+ offset?: number | undefined;
144
+ excludeTypes?: ("federated" | "agent" | "automated")[] | undefined;
145
+ excludeIds?: string[] | undefined;
146
+ boosts?: {
147
+ userIds: string[];
148
+ weight: number;
149
+ reason?: string | undefined;
150
+ }[] | undefined;
151
+ signalWeights?: {
152
+ verified?: number | undefined;
153
+ graph?: number | undefined;
154
+ completeness?: number | undefined;
155
+ curation?: number | undefined;
156
+ interest?: number | undefined;
157
+ appBoost?: number | undefined;
158
+ repCandidate?: number | undefined;
159
+ } | undefined;
160
+ }>;
161
+ export type RecommendationRequest = z.infer<typeof recommendationRequestSchema>;
162
+ /** Follower/following counts attached to a recommendation item. */
163
+ export declare const recommendationCountSchema: z.ZodObject<{
164
+ followers: z.ZodNumber;
165
+ following: z.ZodNumber;
166
+ }, "strip", z.ZodTypeAny, {
167
+ followers: number;
168
+ following: number;
169
+ }, {
170
+ followers: number;
171
+ following: number;
172
+ }>;
173
+ export type RecommendationCount = z.infer<typeof recommendationCountSchema>;
174
+ /**
175
+ * A single recommended profile.
176
+ *
177
+ * `name` reuses the canonical {@link userNameSchema} so `name.displayName` is the
178
+ * already-resolved server-side value. `score` and `matchedSignals` are present
179
+ * only on the scored (v2) path; `mutualCount` and `_count` are always present.
180
+ */
181
+ export declare const recommendationItemSchema: z.ZodObject<{
182
+ id: z.ZodString;
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">>;
200
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
201
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
202
+ verified: z.ZodOptional<z.ZodBoolean>;
203
+ trustTier: z.ZodOptional<z.ZodString>;
204
+ mutualCount: z.ZodNumber;
205
+ score: z.ZodOptional<z.ZodNumber>;
206
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
207
+ isFederated: z.ZodOptional<z.ZodBoolean>;
208
+ isAgent: z.ZodOptional<z.ZodBoolean>;
209
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
210
+ instance: z.ZodOptional<z.ZodString>;
211
+ _count: z.ZodObject<{
212
+ followers: z.ZodNumber;
213
+ following: z.ZodNumber;
214
+ }, "strip", z.ZodTypeAny, {
215
+ followers: number;
216
+ following: number;
217
+ }, {
218
+ followers: number;
219
+ following: number;
220
+ }>;
221
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
222
+ id: z.ZodString;
223
+ 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">>;
240
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
241
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
242
+ verified: z.ZodOptional<z.ZodBoolean>;
243
+ trustTier: z.ZodOptional<z.ZodString>;
244
+ mutualCount: z.ZodNumber;
245
+ score: z.ZodOptional<z.ZodNumber>;
246
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
247
+ isFederated: z.ZodOptional<z.ZodBoolean>;
248
+ isAgent: z.ZodOptional<z.ZodBoolean>;
249
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
250
+ instance: z.ZodOptional<z.ZodString>;
251
+ _count: z.ZodObject<{
252
+ followers: z.ZodNumber;
253
+ following: z.ZodNumber;
254
+ }, "strip", z.ZodTypeAny, {
255
+ followers: number;
256
+ following: number;
257
+ }, {
258
+ followers: number;
259
+ following: number;
260
+ }>;
261
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
262
+ id: z.ZodString;
263
+ 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">>;
280
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
281
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
282
+ verified: z.ZodOptional<z.ZodBoolean>;
283
+ trustTier: z.ZodOptional<z.ZodString>;
284
+ mutualCount: z.ZodNumber;
285
+ score: z.ZodOptional<z.ZodNumber>;
286
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
287
+ isFederated: z.ZodOptional<z.ZodBoolean>;
288
+ isAgent: z.ZodOptional<z.ZodBoolean>;
289
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
290
+ instance: z.ZodOptional<z.ZodString>;
291
+ _count: z.ZodObject<{
292
+ followers: z.ZodNumber;
293
+ following: z.ZodNumber;
294
+ }, "strip", z.ZodTypeAny, {
295
+ followers: number;
296
+ following: number;
297
+ }, {
298
+ followers: number;
299
+ following: number;
300
+ }>;
301
+ }, z.ZodTypeAny, "passthrough">>;
302
+ export type RecommendationItem = z.infer<typeof recommendationItemSchema>;
303
+ /** Wire shape of the recommendation response — an array of items. */
304
+ export declare const recommendationResponseSchema: z.ZodArray<z.ZodObject<{
305
+ id: z.ZodString;
306
+ 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">>;
323
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
324
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
325
+ verified: z.ZodOptional<z.ZodBoolean>;
326
+ trustTier: z.ZodOptional<z.ZodString>;
327
+ mutualCount: z.ZodNumber;
328
+ score: z.ZodOptional<z.ZodNumber>;
329
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
330
+ isFederated: z.ZodOptional<z.ZodBoolean>;
331
+ isAgent: z.ZodOptional<z.ZodBoolean>;
332
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
333
+ instance: z.ZodOptional<z.ZodString>;
334
+ _count: z.ZodObject<{
335
+ followers: z.ZodNumber;
336
+ following: z.ZodNumber;
337
+ }, "strip", z.ZodTypeAny, {
338
+ followers: number;
339
+ following: number;
340
+ }, {
341
+ followers: number;
342
+ following: number;
343
+ }>;
344
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
345
+ id: z.ZodString;
346
+ 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">>;
363
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
364
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
365
+ verified: z.ZodOptional<z.ZodBoolean>;
366
+ trustTier: z.ZodOptional<z.ZodString>;
367
+ mutualCount: z.ZodNumber;
368
+ score: z.ZodOptional<z.ZodNumber>;
369
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
370
+ isFederated: z.ZodOptional<z.ZodBoolean>;
371
+ isAgent: z.ZodOptional<z.ZodBoolean>;
372
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
373
+ instance: z.ZodOptional<z.ZodString>;
374
+ _count: z.ZodObject<{
375
+ followers: z.ZodNumber;
376
+ following: z.ZodNumber;
377
+ }, "strip", z.ZodTypeAny, {
378
+ followers: number;
379
+ following: number;
380
+ }, {
381
+ followers: number;
382
+ following: number;
383
+ }>;
384
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
385
+ id: z.ZodString;
386
+ 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">>;
403
+ avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>;
404
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
405
+ verified: z.ZodOptional<z.ZodBoolean>;
406
+ trustTier: z.ZodOptional<z.ZodString>;
407
+ mutualCount: z.ZodNumber;
408
+ score: z.ZodOptional<z.ZodNumber>;
409
+ matchedSignals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
410
+ isFederated: z.ZodOptional<z.ZodBoolean>;
411
+ isAgent: z.ZodOptional<z.ZodBoolean>;
412
+ isAutomated: z.ZodOptional<z.ZodBoolean>;
413
+ instance: z.ZodOptional<z.ZodString>;
414
+ _count: z.ZodObject<{
415
+ followers: z.ZodNumber;
416
+ following: z.ZodNumber;
417
+ }, "strip", z.ZodTypeAny, {
418
+ followers: number;
419
+ following: number;
420
+ }, {
421
+ followers: number;
422
+ following: number;
423
+ }>;
424
+ }, z.ZodTypeAny, "passthrough">>, "many">;
425
+ export type RecommendationResponse = z.infer<typeof recommendationResponseSchema>;
426
+ /** One endorsement edge an app reports: `ownerId` endorses `memberId`. */
427
+ export declare const appEndorsementInputSchema: z.ZodObject<{
428
+ ownerId: z.ZodString;
429
+ memberId: z.ZodString;
430
+ op: z.ZodDefault<z.ZodEnum<["add", "remove"]>>;
431
+ sourceId: z.ZodOptional<z.ZodString>;
432
+ }, "strip", z.ZodTypeAny, {
433
+ ownerId: string;
434
+ memberId: string;
435
+ op: "add" | "remove";
436
+ sourceId?: string | undefined;
437
+ }, {
438
+ ownerId: string;
439
+ memberId: string;
440
+ op?: "add" | "remove" | undefined;
441
+ sourceId?: string | undefined;
442
+ }>;
443
+ export type AppEndorsementInput = z.infer<typeof appEndorsementInputSchema>;
444
+ /** One interest signal an app reports: how interested `userId` is in a topic. */
445
+ export declare const appInterestInputSchema: z.ZodObject<{
446
+ userId: z.ZodString;
447
+ interestScore: z.ZodNumber;
448
+ }, "strip", z.ZodTypeAny, {
449
+ userId: string;
450
+ interestScore: number;
451
+ }, {
452
+ userId: string;
453
+ interestScore: number;
454
+ }>;
455
+ export type AppInterestInput = z.infer<typeof appInterestInputSchema>;
456
+ /**
457
+ * Request body for `POST /app-signals/ingest` (service token, `signals:write`).
458
+ *
459
+ * At least one of `endorsements` / `interests` must be non-empty — an ingest
460
+ * with neither is a no-op and rejected so a misconfigured caller is surfaced
461
+ * rather than silently succeeding.
462
+ */
463
+ export declare const appUserSignalIngestSchema: z.ZodEffects<z.ZodObject<{
464
+ endorsements: z.ZodOptional<z.ZodArray<z.ZodObject<{
465
+ ownerId: z.ZodString;
466
+ memberId: z.ZodString;
467
+ op: z.ZodDefault<z.ZodEnum<["add", "remove"]>>;
468
+ sourceId: z.ZodOptional<z.ZodString>;
469
+ }, "strip", z.ZodTypeAny, {
470
+ ownerId: string;
471
+ memberId: string;
472
+ op: "add" | "remove";
473
+ sourceId?: string | undefined;
474
+ }, {
475
+ ownerId: string;
476
+ memberId: string;
477
+ op?: "add" | "remove" | undefined;
478
+ sourceId?: string | undefined;
479
+ }>, "many">>;
480
+ interests: z.ZodOptional<z.ZodArray<z.ZodObject<{
481
+ userId: z.ZodString;
482
+ interestScore: z.ZodNumber;
483
+ }, "strip", z.ZodTypeAny, {
484
+ userId: string;
485
+ interestScore: number;
486
+ }, {
487
+ userId: string;
488
+ interestScore: number;
489
+ }>, "many">>;
490
+ }, "strip", z.ZodTypeAny, {
491
+ endorsements?: {
492
+ ownerId: string;
493
+ memberId: string;
494
+ op: "add" | "remove";
495
+ sourceId?: string | undefined;
496
+ }[] | undefined;
497
+ interests?: {
498
+ userId: string;
499
+ interestScore: number;
500
+ }[] | undefined;
501
+ }, {
502
+ endorsements?: {
503
+ ownerId: string;
504
+ memberId: string;
505
+ op?: "add" | "remove" | undefined;
506
+ sourceId?: string | undefined;
507
+ }[] | undefined;
508
+ interests?: {
509
+ userId: string;
510
+ interestScore: number;
511
+ }[] | undefined;
512
+ }>, {
513
+ endorsements?: {
514
+ ownerId: string;
515
+ memberId: string;
516
+ op: "add" | "remove";
517
+ sourceId?: string | undefined;
518
+ }[] | undefined;
519
+ interests?: {
520
+ userId: string;
521
+ interestScore: number;
522
+ }[] | undefined;
523
+ }, {
524
+ endorsements?: {
525
+ ownerId: string;
526
+ memberId: string;
527
+ op?: "add" | "remove" | undefined;
528
+ sourceId?: string | undefined;
529
+ }[] | undefined;
530
+ interests?: {
531
+ userId: string;
532
+ interestScore: number;
533
+ }[] | undefined;
534
+ }>;
535
+ export type AppUserSignalIngest = z.infer<typeof appUserSignalIngestSchema>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/contracts",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
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
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",