@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.
- package/LICENSE +202 -0
- package/NOTICE +16 -0
- package/dist/cjs/.tsbuildinfo +1 -0
- package/dist/cjs/accountGraph.js +489 -0
- package/dist/cjs/agency.js +439 -0
- package/dist/cjs/browserHub.js +215 -0
- package/dist/cjs/civic.js +163 -0
- package/dist/cjs/commonsSignIn.js +59 -0
- package/dist/cjs/deviceBoot.js +50 -0
- package/dist/cjs/deviceDirectory.js +189 -0
- package/dist/cjs/devicePairing.js +138 -0
- package/dist/cjs/deviceSession.js +164 -0
- package/dist/cjs/emailAgentContext.js +32 -0
- package/dist/cjs/followGraph.js +28 -0
- package/dist/cjs/identity.js +258 -0
- package/dist/cjs/inboxPush.js +24 -0
- package/dist/cjs/index.js +618 -0
- package/dist/cjs/inference/accountBilling.js +334 -0
- package/dist/cjs/inference/aliaModelRelease.js +262 -0
- package/dist/cjs/inference/attribution.js +106 -0
- package/dist/cjs/inference/catalogue.js +487 -0
- package/dist/cjs/inference/entitlement.js +217 -0
- package/dist/cjs/inference/errors.js +309 -0
- package/dist/cjs/inference/identifiers.js +224 -0
- package/dist/cjs/inference/inbox.js +105 -0
- package/dist/cjs/inference/modelDocumentation.js +433 -0
- package/dist/cjs/inference/money.js +188 -0
- package/dist/cjs/inference/priceVersion.js +110 -0
- package/dist/cjs/inference/providerConnection.js +455 -0
- package/dist/cjs/inference/request.js +477 -0
- package/dist/cjs/inference/routingPolicy.js +318 -0
- package/dist/cjs/inference/streamEvents.js +258 -0
- package/dist/cjs/inference/usage.js +329 -0
- package/dist/cjs/inference/version.js +105 -0
- package/dist/cjs/keyRecovery.js +91 -0
- package/dist/cjs/keyRotation.js +75 -0
- package/dist/cjs/links.js +68 -0
- package/dist/cjs/moderationReputation.js +298 -0
- package/dist/cjs/oauth.js +66 -0
- package/dist/cjs/oxyRecordTypes.js +71 -0
- package/dist/cjs/protocol.js +53 -0
- package/dist/cjs/recommendations.js +168 -0
- package/dist/cjs/reputation.js +297 -0
- package/dist/cjs/sessionStatus.js +121 -0
- package/dist/cjs/transparency.js +89 -0
- package/dist/cjs/updates.js +252 -0
- package/dist/cjs/userInvalidation.js +89 -0
- package/dist/cjs/userResponse.js +245 -0
- package/dist/cjs/username.js +290 -0
- package/dist/cjs/webauthn.js +71 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/accountGraph.js +480 -0
- package/dist/esm/agency.js +436 -0
- package/dist/esm/browserHub.js +212 -0
- package/dist/esm/civic.js +160 -0
- package/dist/esm/commonsSignIn.js +56 -0
- package/dist/esm/deviceBoot.js +47 -0
- package/dist/esm/deviceDirectory.js +186 -0
- package/dist/esm/devicePairing.js +135 -0
- package/dist/esm/deviceSession.js +161 -0
- package/dist/esm/emailAgentContext.js +29 -0
- package/dist/esm/followGraph.js +27 -0
- package/dist/esm/identity.js +255 -0
- package/dist/esm/inboxPush.js +21 -0
- package/dist/esm/index.js +172 -0
- package/dist/esm/inference/accountBilling.js +331 -0
- package/dist/esm/inference/aliaModelRelease.js +259 -0
- package/dist/esm/inference/attribution.js +103 -0
- package/dist/esm/inference/catalogue.js +484 -0
- package/dist/esm/inference/entitlement.js +214 -0
- package/dist/esm/inference/errors.js +306 -0
- package/dist/esm/inference/identifiers.js +221 -0
- package/dist/esm/inference/inbox.js +102 -0
- package/dist/esm/inference/modelDocumentation.js +430 -0
- package/dist/esm/inference/money.js +185 -0
- package/dist/esm/inference/priceVersion.js +107 -0
- package/dist/esm/inference/providerConnection.js +452 -0
- package/dist/esm/inference/request.js +474 -0
- package/dist/esm/inference/routingPolicy.js +315 -0
- package/dist/esm/inference/streamEvents.js +255 -0
- package/dist/esm/inference/usage.js +326 -0
- package/dist/esm/inference/version.js +102 -0
- package/dist/esm/keyRecovery.js +88 -0
- package/dist/esm/keyRotation.js +72 -0
- package/dist/esm/links.js +65 -0
- package/dist/esm/moderationReputation.js +295 -0
- package/dist/esm/oauth.js +63 -0
- package/dist/esm/oxyRecordTypes.js +68 -0
- package/dist/esm/protocol.js +50 -0
- package/dist/esm/recommendations.js +165 -0
- package/dist/esm/reputation.js +293 -0
- package/dist/esm/sessionStatus.js +118 -0
- package/dist/esm/transparency.js +86 -0
- package/dist/esm/updates.js +249 -0
- package/dist/esm/userInvalidation.js +85 -0
- package/dist/esm/userResponse.js +240 -0
- package/dist/esm/username.js +283 -0
- package/dist/esm/webauthn.js +68 -0
- package/dist/types/.tsbuildinfo +1 -0
- package/dist/types/accountGraph.d.ts +378 -0
- package/dist/types/agency.d.ts +2162 -0
- package/dist/types/browserHub.d.ts +856 -0
- package/dist/types/civic.d.ts +338 -0
- package/dist/types/commonsSignIn.d.ts +58 -0
- package/dist/types/deviceBoot.d.ts +74 -0
- package/dist/types/deviceDirectory.d.ts +1317 -0
- package/dist/types/devicePairing.d.ts +130 -0
- package/dist/types/deviceSession.d.ts +411 -0
- package/dist/types/emailAgentContext.d.ts +248 -0
- package/dist/types/followGraph.d.ts +150 -0
- package/dist/types/identity.d.ts +402 -0
- package/dist/types/inboxPush.d.ts +30 -0
- package/dist/types/index.d.ts +100 -0
- package/dist/types/inference/accountBilling.d.ts +738 -0
- package/dist/types/inference/aliaModelRelease.d.ts +609 -0
- package/dist/types/inference/attribution.d.ts +176 -0
- package/dist/types/inference/catalogue.d.ts +1618 -0
- package/dist/types/inference/entitlement.d.ts +519 -0
- package/dist/types/inference/errors.d.ts +242 -0
- package/dist/types/inference/identifiers.d.ts +182 -0
- package/dist/types/inference/inbox.d.ts +374 -0
- package/dist/types/inference/modelDocumentation.d.ts +1603 -0
- package/dist/types/inference/money.d.ts +185 -0
- package/dist/types/inference/priceVersion.d.ts +182 -0
- package/dist/types/inference/providerConnection.d.ts +968 -0
- package/dist/types/inference/request.d.ts +2800 -0
- package/dist/types/inference/routingPolicy.d.ts +616 -0
- package/dist/types/inference/streamEvents.d.ts +950 -0
- package/dist/types/inference/usage.d.ts +1164 -0
- package/dist/types/inference/version.d.ts +102 -0
- package/dist/types/keyRecovery.d.ts +138 -0
- package/dist/types/keyRotation.d.ts +103 -0
- package/dist/types/links.d.ts +96 -0
- package/dist/types/moderationReputation.d.ts +487 -0
- package/dist/types/oauth.d.ts +86 -0
- package/dist/types/oxyRecordTypes.d.ts +62 -0
- package/dist/types/protocol.d.ts +86 -0
- package/dist/types/recommendations.d.ts +542 -0
- package/dist/types/reputation.d.ts +457 -0
- package/dist/types/sessionStatus.d.ts +231 -0
- package/dist/types/transparency.d.ts +392 -0
- package/dist/types/updates.d.ts +545 -0
- package/dist/types/userInvalidation.d.ts +94 -0
- package/dist/types/userResponse.d.ts +1706 -0
- package/dist/types/username.d.ts +265 -0
- package/dist/types/webauthn.d.ts +77 -0
- package/package.json +87 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Oxy Trust — reputation API contracts.
|
|
4
|
+
*
|
|
5
|
+
* SINGLE SOURCE OF TRUTH for the reputation ledger's wire shapes: the closed
|
|
6
|
+
* value sets (`REPUTATION_CATEGORIES`, `TRUST_TIERS`, …), the response entities
|
|
7
|
+
* (`ReputationTransaction`, the two balance views, `ReputationDispute`,
|
|
8
|
+
* `ReputationRule`, the leaderboard entry) and the request bodies the write
|
|
9
|
+
* endpoints accept. The API validates its OUTPUT against these schemas and its
|
|
10
|
+
* INPUT with the same request schemas the SDK's input types are derived from;
|
|
11
|
+
* `@oxy.so/core`'s reputation mixin imports every type from here rather than
|
|
12
|
+
* declaring its own.
|
|
13
|
+
*
|
|
14
|
+
* Why this module exists: the balance endpoint was view-split server-side
|
|
15
|
+
* without the SDK type moving with it, and for hours the SDK affirmatively
|
|
16
|
+
* type-checked a read of `balance.reliability.reportAccuracyScore` against a
|
|
17
|
+
* response that no longer carried `reliability`. Nothing structural connected
|
|
18
|
+
* the API's hand-written serializers (which returned `Record<string, unknown>`)
|
|
19
|
+
* to the SDK's interfaces — only human attention. With the serializers
|
|
20
|
+
* annotated against these definitions, that divergence is a build failure.
|
|
21
|
+
*
|
|
22
|
+
* Design anchors:
|
|
23
|
+
* - **Ids are strings, timestamps are ISO 8601 strings.** The server holds
|
|
24
|
+
* `ObjectId`s and `Date`s; every serializer converts at the boundary, so a
|
|
25
|
+
* `Date` leaking into a field this module types as `string` fails to compile.
|
|
26
|
+
* - **The balance has two views, and the union is the contract.** See
|
|
27
|
+
* {@link ReputationBalanceView} — the compile-time assertions below are what
|
|
28
|
+
* stop the private view's fields becoming reachable on a stranger's balance.
|
|
29
|
+
* - **The closed value sets live here, not beside the mongoose models.** The
|
|
30
|
+
* API's model enums and the SDK's unions are the same `as const` tuple, so a
|
|
31
|
+
* seventh category cannot be added on one side only.
|
|
32
|
+
*
|
|
33
|
+
* The response entities are declared as explicit `interface`s with their runtime
|
|
34
|
+
* schemas annotated `z.ZodType<Interface>`, following `./links` and
|
|
35
|
+
* `./userResponse`: a `z.infer<>` of a nested-object schema can degrade to `{}`
|
|
36
|
+
* under a consumer's `moduleResolution: "node"` (node10) resolution, while a
|
|
37
|
+
* literal interface emits the field types verbatim in the `.d.ts` and survives
|
|
38
|
+
* both `node` and `bundler`.
|
|
39
|
+
*
|
|
40
|
+
* Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
|
|
41
|
+
* `require()`).
|
|
42
|
+
*/
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.reverseReputationTransactionSchema = exports.upsertReputationRuleSchema = exports.resolveReputationDisputeSchema = exports.createReputationDisputeSchema = exports.awardReputationSchema = exports.reverseReputationTransactionResultSchema = exports.reputationInfluenceResultSchema = exports.reputationLeaderboardEntrySchema = exports.reputationLeaderboardUserSchema = exports.reputationRuleSchema = exports.reputationDisputeSchema = exports.reputationBalanceSchema = exports.reputationBalanceSummarySchema = exports.reputationReliabilitySchema = exports.reputationInfluenceSchema = exports.reputationBalanceBreakdownSchema = exports.reputationTransactionSchema = exports.reputationInfluenceContextSchema = exports.REPUTATION_INFLUENCE_CONTEXTS = exports.reputationDisputeStatusSchema = exports.REPUTATION_DISPUTE_STATUSES = exports.reputationTargetEntityTypeSchema = exports.REPUTATION_TARGET_ENTITY_TYPES = exports.trustTierSchema = exports.TRUST_TIERS = exports.reputationTransactionStatusSchema = exports.REPUTATION_TRANSACTION_STATUSES = exports.reputationCategorySchema = exports.REPUTATION_CATEGORIES = void 0;
|
|
45
|
+
exports.isFullReputationBalance = isFullReputationBalance;
|
|
46
|
+
const zod_1 = require("zod");
|
|
47
|
+
const userResponse_1 = require("./userResponse");
|
|
48
|
+
const moderationReputation_1 = require("./moderationReputation");
|
|
49
|
+
/* -------------------------------------------------------------------------- */
|
|
50
|
+
/* Closed value sets */
|
|
51
|
+
/* -------------------------------------------------------------------------- */
|
|
52
|
+
/**
|
|
53
|
+
* Category bucket a reputation transaction falls into. Drives the per-category
|
|
54
|
+
* balance breakdown; every rule and transaction carries exactly one.
|
|
55
|
+
*
|
|
56
|
+
* - `content` — posts, comments, media a user authored.
|
|
57
|
+
* - `social` — follows, likes, social interactions.
|
|
58
|
+
* - `trust` — identity / verification / trust-graph signals.
|
|
59
|
+
* - `moderation` — reports filed, moderation actions, review outcomes.
|
|
60
|
+
* - `physical` — real-world signals (event check-ins, verified purchases).
|
|
61
|
+
* - `penalty` — negative adjustments for abuse / policy violations.
|
|
62
|
+
* - `other` — anything that does not fit the buckets above.
|
|
63
|
+
*/
|
|
64
|
+
exports.REPUTATION_CATEGORIES = [
|
|
65
|
+
'content',
|
|
66
|
+
'social',
|
|
67
|
+
'trust',
|
|
68
|
+
'moderation',
|
|
69
|
+
'physical',
|
|
70
|
+
'penalty',
|
|
71
|
+
'other',
|
|
72
|
+
];
|
|
73
|
+
exports.reputationCategorySchema = zod_1.z.enum(exports.REPUTATION_CATEGORIES);
|
|
74
|
+
/**
|
|
75
|
+
* Transaction lifecycle status.
|
|
76
|
+
*
|
|
77
|
+
* - `active` — counts toward the balance.
|
|
78
|
+
* - `disputed` — under dispute; still counts until the dispute resolves.
|
|
79
|
+
* - `reversed` — superseded by a compensating reversal transaction; excluded.
|
|
80
|
+
* - `voided` — administratively excluded with no compensating entry.
|
|
81
|
+
*/
|
|
82
|
+
exports.REPUTATION_TRANSACTION_STATUSES = [
|
|
83
|
+
'active',
|
|
84
|
+
'disputed',
|
|
85
|
+
'reversed',
|
|
86
|
+
'voided',
|
|
87
|
+
];
|
|
88
|
+
exports.reputationTransactionStatusSchema = zod_1.z.enum(exports.REPUTATION_TRANSACTION_STATUSES);
|
|
89
|
+
/**
|
|
90
|
+
* Trust tiers, lowest → highest trust, plus the punitive `restricted`.
|
|
91
|
+
*
|
|
92
|
+
* Publicly visible: this is the contribution ladder the reputation system
|
|
93
|
+
* exists to publish. Note it doubles as the sanction marker — a `restricted`
|
|
94
|
+
* account is publicly identifiable as such.
|
|
95
|
+
*/
|
|
96
|
+
exports.TRUST_TIERS = ['restricted', 'new', 'trusted', 'high_trust', 'verified'];
|
|
97
|
+
exports.trustTierSchema = zod_1.z.enum(exports.TRUST_TIERS);
|
|
98
|
+
/** Kind of entity a transaction may target. */
|
|
99
|
+
exports.REPUTATION_TARGET_ENTITY_TYPES = [
|
|
100
|
+
'post',
|
|
101
|
+
'comment',
|
|
102
|
+
'report',
|
|
103
|
+
'purchase',
|
|
104
|
+
'event',
|
|
105
|
+
'check_in',
|
|
106
|
+
'manual_review',
|
|
107
|
+
'user',
|
|
108
|
+
'other',
|
|
109
|
+
];
|
|
110
|
+
exports.reputationTargetEntityTypeSchema = zod_1.z.enum(exports.REPUTATION_TARGET_ENTITY_TYPES);
|
|
111
|
+
/** Dispute lifecycle status. */
|
|
112
|
+
exports.REPUTATION_DISPUTE_STATUSES = [
|
|
113
|
+
'open',
|
|
114
|
+
'accepted',
|
|
115
|
+
'rejected',
|
|
116
|
+
'needs_review',
|
|
117
|
+
];
|
|
118
|
+
exports.reputationDisputeStatusSchema = zod_1.z.enum(exports.REPUTATION_DISPUTE_STATUSES);
|
|
119
|
+
/** Influence context selecting which capped weight axis to read. */
|
|
120
|
+
exports.REPUTATION_INFLUENCE_CONTEXTS = [
|
|
121
|
+
'default',
|
|
122
|
+
'report',
|
|
123
|
+
'moderation',
|
|
124
|
+
'ranking',
|
|
125
|
+
];
|
|
126
|
+
exports.reputationInfluenceContextSchema = zod_1.z.enum(exports.REPUTATION_INFLUENCE_CONTEXTS);
|
|
127
|
+
exports.reputationTransactionSchema = zod_1.z.object({
|
|
128
|
+
id: zod_1.z.string(),
|
|
129
|
+
userId: zod_1.z.string(),
|
|
130
|
+
points: zod_1.z.number(),
|
|
131
|
+
actionType: zod_1.z.string(),
|
|
132
|
+
category: exports.reputationCategorySchema,
|
|
133
|
+
applicationId: zod_1.z.string().optional(),
|
|
134
|
+
credentialId: zod_1.z.string().optional(),
|
|
135
|
+
sourceActionId: zod_1.z.string().optional(),
|
|
136
|
+
sourceActionType: zod_1.z.string().optional(),
|
|
137
|
+
targetEntityId: zod_1.z.string().optional(),
|
|
138
|
+
targetEntityType: exports.reputationTargetEntityTypeSchema.optional(),
|
|
139
|
+
status: exports.reputationTransactionStatusSchema,
|
|
140
|
+
reversedTransactionId: zod_1.z.string().optional(),
|
|
141
|
+
reason: zod_1.z.string().optional(),
|
|
142
|
+
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
143
|
+
createdByUserId: zod_1.z.string().optional(),
|
|
144
|
+
reviewedByUserId: zod_1.z.string().optional(),
|
|
145
|
+
reviewedAt: zod_1.z.string().optional(),
|
|
146
|
+
createdAt: zod_1.z.string(),
|
|
147
|
+
updatedAt: zod_1.z.string(),
|
|
148
|
+
});
|
|
149
|
+
exports.reputationBalanceBreakdownSchema = zod_1.z.object({
|
|
150
|
+
content: zod_1.z.number(),
|
|
151
|
+
social: zod_1.z.number(),
|
|
152
|
+
trust: zod_1.z.number(),
|
|
153
|
+
moderation: zod_1.z.number(),
|
|
154
|
+
physical: zod_1.z.number(),
|
|
155
|
+
penalties: zod_1.z.number(),
|
|
156
|
+
});
|
|
157
|
+
exports.reputationInfluenceSchema = zod_1.z.object({
|
|
158
|
+
defaultWeight: zod_1.z.number(),
|
|
159
|
+
reportWeight: zod_1.z.number(),
|
|
160
|
+
moderationWeight: zod_1.z.number(),
|
|
161
|
+
rankingFeedbackWeight: zod_1.z.number(),
|
|
162
|
+
});
|
|
163
|
+
exports.reputationReliabilitySchema = zod_1.z.object({
|
|
164
|
+
accurateReports: zod_1.z.number(),
|
|
165
|
+
rejectedReports: zod_1.z.number(),
|
|
166
|
+
reportAccuracyScore: zod_1.z.number(),
|
|
167
|
+
abuseScore: zod_1.z.number(),
|
|
168
|
+
});
|
|
169
|
+
/** The fields both balance views share. Kept as a shape so the full view can spread it. */
|
|
170
|
+
const balanceSummaryShape = {
|
|
171
|
+
userId: zod_1.z.string(),
|
|
172
|
+
total: zod_1.z.number(),
|
|
173
|
+
trustTier: exports.trustTierSchema,
|
|
174
|
+
};
|
|
175
|
+
exports.reputationBalanceSummarySchema = zod_1.z.object(balanceSummaryShape);
|
|
176
|
+
exports.reputationBalanceSchema = zod_1.z.object({
|
|
177
|
+
...balanceSummaryShape,
|
|
178
|
+
positive: zod_1.z.number(),
|
|
179
|
+
negative: zod_1.z.number(),
|
|
180
|
+
breakdown: exports.reputationBalanceBreakdownSchema,
|
|
181
|
+
influence: exports.reputationInfluenceSchema,
|
|
182
|
+
reliability: exports.reputationReliabilitySchema,
|
|
183
|
+
recalculatedAt: zod_1.z.string(),
|
|
184
|
+
updatedAt: zod_1.z.string(),
|
|
185
|
+
personhood: moderationReputation_1.reputationPersonhoodSchema.optional(),
|
|
186
|
+
contribution: moderationReputation_1.reputationContributionSchema.optional(),
|
|
187
|
+
conduct: moderationReputation_1.reputationConductSchema.optional(),
|
|
188
|
+
reporting: moderationReputation_1.reputationReportingSchema.optional(),
|
|
189
|
+
reviewing: moderationReputation_1.reputationReviewingSchema.optional(),
|
|
190
|
+
contextualInfluence: moderationReputation_1.reputationContextualInfluenceSchema.optional(),
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* The fields the full {@link ReputationBalance} carries beyond the public
|
|
194
|
+
* {@link ReputationBalanceSummary} that the API sends ALL-OR-NOTHING. The
|
|
195
|
+
* runtime discriminant between the two views.
|
|
196
|
+
*
|
|
197
|
+
* The V2 blocks (`conduct`, `contribution`, …) are deliberately NOT listed:
|
|
198
|
+
* they are optional on the wire, so requiring them here would make a balance
|
|
199
|
+
* from a server that predates them fail to narrow, hiding the whole private
|
|
200
|
+
* view. Read a V2 block by checking that block.
|
|
201
|
+
*/
|
|
202
|
+
const FULL_BALANCE_FIELDS = [
|
|
203
|
+
'positive',
|
|
204
|
+
'negative',
|
|
205
|
+
'breakdown',
|
|
206
|
+
'influence',
|
|
207
|
+
'reliability',
|
|
208
|
+
'recalculatedAt',
|
|
209
|
+
'updatedAt',
|
|
210
|
+
];
|
|
211
|
+
/**
|
|
212
|
+
* Whether a balance came back as the SUBJECT view, and so carries the
|
|
213
|
+
* breakdown / influence / reliability blocks.
|
|
214
|
+
*
|
|
215
|
+
* Checks every extra field rather than one representative: the point of the
|
|
216
|
+
* guard is that the caller then dereferences those blocks, so a partial payload
|
|
217
|
+
* must not narrow.
|
|
218
|
+
*
|
|
219
|
+
* @param balance - A balance from `getReputationBalance`.
|
|
220
|
+
*/
|
|
221
|
+
function isFullReputationBalance(balance) {
|
|
222
|
+
return FULL_BALANCE_FIELDS.every((field) => field in balance);
|
|
223
|
+
}
|
|
224
|
+
exports.reputationDisputeSchema = zod_1.z.object({
|
|
225
|
+
id: zod_1.z.string(),
|
|
226
|
+
transactionId: zod_1.z.string(),
|
|
227
|
+
userId: zod_1.z.string(),
|
|
228
|
+
reason: zod_1.z.string(),
|
|
229
|
+
status: exports.reputationDisputeStatusSchema,
|
|
230
|
+
evidence: zod_1.z.array(zod_1.z.string()).optional(),
|
|
231
|
+
resolvedAt: zod_1.z.string().optional(),
|
|
232
|
+
resolvedByUserId: zod_1.z.string().optional(),
|
|
233
|
+
createdAt: zod_1.z.string(),
|
|
234
|
+
updatedAt: zod_1.z.string(),
|
|
235
|
+
});
|
|
236
|
+
exports.reputationRuleSchema = zod_1.z.object({
|
|
237
|
+
id: zod_1.z.string(),
|
|
238
|
+
actionType: zod_1.z.string(),
|
|
239
|
+
points: zod_1.z.number(),
|
|
240
|
+
category: exports.reputationCategorySchema,
|
|
241
|
+
description: zod_1.z.string(),
|
|
242
|
+
cooldownInMinutes: zod_1.z.number(),
|
|
243
|
+
isEnabled: zod_1.z.boolean(),
|
|
244
|
+
});
|
|
245
|
+
exports.reputationLeaderboardUserSchema = zod_1.z.object({
|
|
246
|
+
id: zod_1.z.string(),
|
|
247
|
+
username: zod_1.z.string(),
|
|
248
|
+
name: userResponse_1.userNameSchema,
|
|
249
|
+
avatar: zod_1.z.string().optional(),
|
|
250
|
+
publicKey: zod_1.z.string().optional(),
|
|
251
|
+
});
|
|
252
|
+
exports.reputationLeaderboardEntrySchema = zod_1.z.object({
|
|
253
|
+
user: exports.reputationLeaderboardUserSchema,
|
|
254
|
+
total: zod_1.z.number(),
|
|
255
|
+
trustTier: exports.trustTierSchema,
|
|
256
|
+
rank: zod_1.z.number(),
|
|
257
|
+
});
|
|
258
|
+
exports.reputationInfluenceResultSchema = zod_1.z.object({
|
|
259
|
+
context: exports.reputationInfluenceContextSchema,
|
|
260
|
+
weight: zod_1.z.number(),
|
|
261
|
+
influence: exports.reputationInfluenceSchema,
|
|
262
|
+
});
|
|
263
|
+
exports.reverseReputationTransactionResultSchema = zod_1.z.object({
|
|
264
|
+
original: exports.reputationTransactionSchema,
|
|
265
|
+
reversal: exports.reputationTransactionSchema,
|
|
266
|
+
});
|
|
267
|
+
exports.awardReputationSchema = zod_1.z.object({
|
|
268
|
+
userId: zod_1.z.string().trim().min(1),
|
|
269
|
+
actionType: zod_1.z.string().trim().min(1),
|
|
270
|
+
applicationId: zod_1.z.string().trim().min(1).optional(),
|
|
271
|
+
credentialId: zod_1.z.string().trim().min(1).optional(),
|
|
272
|
+
sourceActionId: zod_1.z.string().trim().min(1).optional(),
|
|
273
|
+
sourceActionType: zod_1.z.string().trim().min(1).optional(),
|
|
274
|
+
targetEntityId: zod_1.z.string().trim().min(1).optional(),
|
|
275
|
+
targetEntityType: exports.reputationTargetEntityTypeSchema.optional(),
|
|
276
|
+
reason: zod_1.z.string().trim().max(500).optional(),
|
|
277
|
+
metadata: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
278
|
+
});
|
|
279
|
+
exports.createReputationDisputeSchema = zod_1.z.object({
|
|
280
|
+
transactionId: zod_1.z.string().trim().min(1),
|
|
281
|
+
reason: zod_1.z.string().trim().min(1).max(1000),
|
|
282
|
+
evidence: zod_1.z.array(zod_1.z.string().trim().min(1)).max(20).optional(),
|
|
283
|
+
});
|
|
284
|
+
exports.resolveReputationDisputeSchema = zod_1.z.object({
|
|
285
|
+
status: zod_1.z.enum(['accepted', 'rejected']),
|
|
286
|
+
});
|
|
287
|
+
exports.upsertReputationRuleSchema = zod_1.z.object({
|
|
288
|
+
actionType: zod_1.z.string().trim().min(1),
|
|
289
|
+
points: zod_1.z.number(),
|
|
290
|
+
category: exports.reputationCategorySchema,
|
|
291
|
+
description: zod_1.z.string().trim().min(1).max(500),
|
|
292
|
+
cooldownInMinutes: zod_1.z.number().int().min(0).default(0),
|
|
293
|
+
isEnabled: zod_1.z.boolean().default(true),
|
|
294
|
+
});
|
|
295
|
+
exports.reverseReputationTransactionSchema = zod_1.z.object({
|
|
296
|
+
reason: zod_1.z.string().trim().max(500).optional(),
|
|
297
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical contract for `GET /auth/session/status/:sessionToken`.
|
|
4
|
+
*
|
|
5
|
+
* SINGLE SOURCE OF TRUTH for the wire shape of the cross-app device-flow
|
|
6
|
+
* session-status payload and the sanitized public application identity it
|
|
7
|
+
* embeds. The API validates its OUTPUT against these schemas; the auth app
|
|
8
|
+
* (consent UI) validates its INPUT against the same schemas. Because there is
|
|
9
|
+
* exactly one definition, the producer and the consumer cannot drift.
|
|
10
|
+
*
|
|
11
|
+
* The class of bug that motivated moving this into `@oxy.so/contracts`: the auth
|
|
12
|
+
* app's LOCAL `sessionStatusSchema` typed `sessionId` as a non-nullable
|
|
13
|
+
* `z.string().optional()`. The producer emits `sessionId: authorizedSessionId ||
|
|
14
|
+
* null`, so a PENDING session (not yet authorized) carries `sessionId: null` —
|
|
15
|
+
* `.optional()` permits `undefined`/missing but REJECTS `null`, so `safeParse`
|
|
16
|
+
* failed, the whole response collapsed to `null`, and the consent screen showed
|
|
17
|
+
* "Unable to identify the requesting application". Pinning the nullability in one
|
|
18
|
+
* shared place makes that drift impossible.
|
|
19
|
+
*
|
|
20
|
+
* Faithful to the producers:
|
|
21
|
+
* - `packages/api/src/utils/serializeApplication.ts` `serializePublicApplication`
|
|
22
|
+
* — the ONLY shape returned to an unauthenticated consent UI. Optional fields
|
|
23
|
+
* (`description`, `icon`, `websiteUrl`, `privacyPolicyUrl`, `termsUrl`,
|
|
24
|
+
* `developerName`) are OMITTED when absent (never serialized as `null`), so
|
|
25
|
+
* they are `.optional()` — NOT `.nullable()`. `type` is the `Application.type`
|
|
26
|
+
* enum.
|
|
27
|
+
* - `packages/api/src/routes/auth.ts` `GET /session/status/:sessionToken` — the
|
|
28
|
+
* inner object of the API's `{ data: ... }` success envelope. The handler
|
|
29
|
+
* ALWAYS emits `status`, `authorized` (`status === 'authorized'`),
|
|
30
|
+
* `sessionToken`, `expiresAt` (ISO string), and `application` (resolved object
|
|
31
|
+
* OR `null`). It ALWAYS emits `sessionId` / `publicKey` / `userId`, each as a
|
|
32
|
+
* string value OR `null` (`authorizedSessionId || null`, `authorizedBy ||
|
|
33
|
+
* null`, `authorizedUserId?.toString() || null`).
|
|
34
|
+
*
|
|
35
|
+
* Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
|
|
36
|
+
* `require()`).
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.sessionStatusSchema = exports.publicApplicationSchema = exports.applicationTypeSchema = void 0;
|
|
40
|
+
const zod_1 = require("zod");
|
|
41
|
+
/**
|
|
42
|
+
* Application `type` enum. Mirrors `APPLICATION_TYPES` in
|
|
43
|
+
* `packages/api/src/models/Application.ts` (`first_party` | `third_party` |
|
|
44
|
+
* `internal` | `system`).
|
|
45
|
+
*/
|
|
46
|
+
exports.applicationTypeSchema = zod_1.z.enum([
|
|
47
|
+
'first_party',
|
|
48
|
+
'third_party',
|
|
49
|
+
'internal',
|
|
50
|
+
'system',
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* The display-safe public identity of a requesting application, exactly as
|
|
54
|
+
* `serializePublicApplication` emits it. Returned by the API inside
|
|
55
|
+
* `GET /auth/session/status/:sessionToken` (device flow) and
|
|
56
|
+
* `GET /auth/oauth/client/:clientId` (OAuth code flow).
|
|
57
|
+
*
|
|
58
|
+
* Optional fields are `.optional()` (NOT `.nullable()`): the serializer OMITS
|
|
59
|
+
* `description` / `icon` / `websiteUrl` / `privacyPolicyUrl` / `termsUrl` /
|
|
60
|
+
* `developerName` when the underlying value is absent — it never writes `null`
|
|
61
|
+
* for them. `developerName` is only attached for non-official apps when a name
|
|
62
|
+
* could be resolved.
|
|
63
|
+
*/
|
|
64
|
+
exports.publicApplicationSchema = zod_1.z.object({
|
|
65
|
+
id: zod_1.z.string(),
|
|
66
|
+
name: zod_1.z.string(),
|
|
67
|
+
description: zod_1.z.string().optional(),
|
|
68
|
+
icon: zod_1.z.string().optional(),
|
|
69
|
+
websiteUrl: zod_1.z.string().optional(),
|
|
70
|
+
privacyPolicyUrl: zod_1.z.string().optional(),
|
|
71
|
+
termsUrl: zod_1.z.string().optional(),
|
|
72
|
+
type: exports.applicationTypeSchema,
|
|
73
|
+
isOfficial: zod_1.z.boolean(),
|
|
74
|
+
isInternal: zod_1.z.boolean(),
|
|
75
|
+
scopes: zod_1.z.array(zod_1.z.string()),
|
|
76
|
+
developerName: zod_1.z.string().optional(),
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* The inner object of `GET /auth/session/status/:sessionToken` (inside the API's
|
|
80
|
+
* `{ data: ... }` envelope).
|
|
81
|
+
*
|
|
82
|
+
* `application` is the resolved {@link publicApplicationSchema} identity of the
|
|
83
|
+
* requesting application, or `null` when the bound app was hard-deleted / is no
|
|
84
|
+
* longer `active` (defensive — normally always present).
|
|
85
|
+
*
|
|
86
|
+
* `sessionId` / `publicKey` / `userId` are `.nullable().optional()`: the producer
|
|
87
|
+
* ALWAYS emits the key, with a string for an AUTHORIZED session or `null` for a
|
|
88
|
+
* PENDING one. `.nullable()` accepts the PENDING `null`; `.optional()` is belt-
|
|
89
|
+
* and-braces so a consumer is never broken by a future projection that drops the
|
|
90
|
+
* key. (`.optional()` alone would REJECT the PENDING `null` — that was the bug.)
|
|
91
|
+
*
|
|
92
|
+
* `authorized` / `sessionToken` / `expiresAt` are emitted unconditionally by the
|
|
93
|
+
* current producer and are never `null`, but stay `.optional()` so the contract
|
|
94
|
+
* tolerates leaner shapes from other producers of this same payload without a
|
|
95
|
+
* coordinated bump.
|
|
96
|
+
*
|
|
97
|
+
* `pushSentAt` / `openedAt` are DELIVERY PROGRESS, not authorization state: they
|
|
98
|
+
* let a waiting surface render "Check Commons on your phone" → "Opened in
|
|
99
|
+
* Commons" without inventing competing statuses. `status` remains the only
|
|
100
|
+
* authority on whether the request is pending, authorized, cancelled or expired.
|
|
101
|
+
* Both are `.nullable().optional()` for the same reason as `sessionId` — the
|
|
102
|
+
* producer always emits the key with `null` until that step happens, and an
|
|
103
|
+
* older API that omits them entirely must degrade, not fail the parse.
|
|
104
|
+
*/
|
|
105
|
+
exports.sessionStatusSchema = zod_1.z.object({
|
|
106
|
+
status: zod_1.z.string(),
|
|
107
|
+
authorized: zod_1.z.boolean().optional(),
|
|
108
|
+
sessionToken: zod_1.z.string().optional(),
|
|
109
|
+
application: exports.publicApplicationSchema.nullable().optional(),
|
|
110
|
+
expiresAt: zod_1.z.string().optional(),
|
|
111
|
+
sessionId: zod_1.z.string().nullable().optional(),
|
|
112
|
+
publicKey: zod_1.z.string().nullable().optional(),
|
|
113
|
+
userId: zod_1.z.string().nullable().optional(),
|
|
114
|
+
/**
|
|
115
|
+
* What approving this request does. Legacy rows read as `device_sign_in`.
|
|
116
|
+
* OAuth-bound sessions finalize into an authorization code (no `sessionId`).
|
|
117
|
+
*/
|
|
118
|
+
purpose: zod_1.z.enum(['device_sign_in', 'oauth_authorization']).optional(),
|
|
119
|
+
pushSentAt: zod_1.z.string().nullable().optional(),
|
|
120
|
+
openedAt: zod_1.z.string().nullable().optional(),
|
|
121
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transparencyCheckpointListSchema = exports.transparencyInclusionProofSchema = exports.transparencyCheckpointSchema = exports.transparencyAnchorSchema = exports.transparencyCheckpointSignatureSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Transparency log — the public wire contract of the checkpoint surface.
|
|
7
|
+
*
|
|
8
|
+
* A checkpoint is the operator's signed commitment to EVERY subject's chain head
|
|
9
|
+
* at a point in time: "at `periodEnd` I committed to `root` over `treeSize`
|
|
10
|
+
* subjects, and the previous checkpoint hashed to `prevCheckpointHash`". Anyone
|
|
11
|
+
* can then ask for an inclusion proof of their own head and verify it against
|
|
12
|
+
* that root without trusting the server — which is what closes the one gap a
|
|
13
|
+
* per-subject hash chain cannot close on its own (the server serving two
|
|
14
|
+
* different histories, or quietly dropping a record).
|
|
15
|
+
*
|
|
16
|
+
* The Merkle math, the leaf/checkpoint signing bytes, and the proof verifier all
|
|
17
|
+
* live in `@oxy.so/protocol` (`src/transparency/`); this module only fixes the
|
|
18
|
+
* SHAPES that cross the wire, so a client and the API cannot drift on them.
|
|
19
|
+
*
|
|
20
|
+
* Digest fields are pinned to 64-char LOWERCASE hex on purpose: the digests are
|
|
21
|
+
* compared as strings against locally recomputed hashes, so accepting an
|
|
22
|
+
* upper-case or truncated variant would turn a real mismatch into a confusing
|
|
23
|
+
* verification failure at a distance.
|
|
24
|
+
*/
|
|
25
|
+
/** A SHA-256 digest in the exact form the protocol emits: 64 lowercase hex chars. */
|
|
26
|
+
const hexDigestSchema = zod_1.z.string().regex(/^[0-9a-f]{64}$/, 'Expected a 64-char lowercase hex digest');
|
|
27
|
+
/**
|
|
28
|
+
* One signer's endorsement of a checkpoint's signed fields.
|
|
29
|
+
*
|
|
30
|
+
* The operator and every independent witness produce this same shape over the
|
|
31
|
+
* SAME bytes, so the array on a checkpoint can grow without coordination.
|
|
32
|
+
*/
|
|
33
|
+
exports.transparencyCheckpointSignatureSchema = zod_1.z.object({
|
|
34
|
+
/** Uncompressed hex public key of the signer. */
|
|
35
|
+
publicKey: zod_1.z.string().min(1),
|
|
36
|
+
alg: zod_1.z.literal('ES256K-DER-SHA256'),
|
|
37
|
+
/** DER-encoded hex secp256k1 signature over the checkpoint signing input. */
|
|
38
|
+
signature: zod_1.z.string().min(1),
|
|
39
|
+
});
|
|
40
|
+
/** Where a checkpoint root was published on a public chain. */
|
|
41
|
+
exports.transparencyAnchorSchema = zod_1.z.object({
|
|
42
|
+
/** Chain/network identifier, e.g. `faircoin-main`. */
|
|
43
|
+
network: zod_1.z.string().min(1),
|
|
44
|
+
txid: zod_1.z.string().min(1),
|
|
45
|
+
confirmations: zod_1.z.number().int().nonnegative(),
|
|
46
|
+
/** When the anchoring transaction was broadcast (ms epoch). */
|
|
47
|
+
anchoredAt: zod_1.z.number().int().positive(),
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* A published checkpoint.
|
|
51
|
+
*
|
|
52
|
+
* `signatures` is non-empty by contract: an unsigned root commits nobody and
|
|
53
|
+
* must never be served as a checkpoint. `anchors` may be empty — a checkpoint is
|
|
54
|
+
* published immediately and anchored asynchronously, so "not yet anchored" is a
|
|
55
|
+
* normal, temporary state rather than an error.
|
|
56
|
+
*/
|
|
57
|
+
exports.transparencyCheckpointSchema = zod_1.z.object({
|
|
58
|
+
index: zod_1.z.number().int().nonnegative(),
|
|
59
|
+
/** End of the committed period (ms epoch). */
|
|
60
|
+
periodEnd: zod_1.z.number().int().positive(),
|
|
61
|
+
/** Number of subjects (leaves) committed. */
|
|
62
|
+
treeSize: zod_1.z.number().int().nonnegative(),
|
|
63
|
+
root: hexDigestSchema,
|
|
64
|
+
/** Hash of the previous checkpoint; `null` only at genesis. */
|
|
65
|
+
prevCheckpointHash: hexDigestSchema.nullable(),
|
|
66
|
+
signatures: zod_1.z.array(exports.transparencyCheckpointSignatureSchema).min(1),
|
|
67
|
+
anchors: zod_1.z.array(exports.transparencyAnchorSchema),
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* An inclusion proof for one subject against one checkpoint.
|
|
71
|
+
*
|
|
72
|
+
* Carries the leaf PREIMAGE (`subjectDid`, `seq`, `headRecordId`) as well as the
|
|
73
|
+
* `leaf` digest so the verifier re-derives the leaf itself rather than trusting
|
|
74
|
+
* the server's hash, then walks `proof` up to the checkpoint's `root`.
|
|
75
|
+
*/
|
|
76
|
+
exports.transparencyInclusionProofSchema = zod_1.z.object({
|
|
77
|
+
checkpoint: exports.transparencyCheckpointSchema,
|
|
78
|
+
subjectDid: zod_1.z.string().min(1),
|
|
79
|
+
seq: zod_1.z.number().int().nonnegative(),
|
|
80
|
+
headRecordId: hexDigestSchema,
|
|
81
|
+
leaf: hexDigestSchema,
|
|
82
|
+
leafIndex: zod_1.z.number().int().nonnegative(),
|
|
83
|
+
/** Audit path, leaf-adjacent sibling first; empty for a single-leaf tree. */
|
|
84
|
+
proof: zod_1.z.array(hexDigestSchema),
|
|
85
|
+
});
|
|
86
|
+
/** A page of the checkpoint chain, oldest first, for walking `prevCheckpointHash`. */
|
|
87
|
+
exports.transparencyCheckpointListSchema = zod_1.z.object({
|
|
88
|
+
checkpoints: zod_1.z.array(exports.transparencyCheckpointSchema),
|
|
89
|
+
});
|