@oxyhq/crowdsource-contracts 0.2.0 → 0.4.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/dist/appeals.d.ts +323 -0
- package/dist/appeals.d.ts.map +1 -0
- package/dist/appeals.js +203 -0
- package/dist/appeals.js.map +1 -0
- package/dist/case-envelope.d.ts +59 -59
- package/dist/case-envelope.d.ts.map +1 -1
- package/dist/case-envelope.js +32 -32
- package/dist/case-envelope.js.map +1 -1
- package/dist/decisions.d.ts +3 -3
- package/dist/decisions.d.ts.map +1 -1
- package/dist/decisions.js +28 -28
- package/dist/decisions.js.map +1 -1
- package/dist/esm/appeals.js +200 -0
- package/dist/esm/appeals.js.map +1 -0
- package/dist/esm/case-envelope.js +380 -0
- package/dist/esm/case-envelope.js.map +1 -0
- package/dist/esm/closed.js +32 -0
- package/dist/esm/closed.js.map +1 -0
- package/dist/esm/decisions.js +195 -0
- package/dist/esm/decisions.js.map +1 -0
- package/dist/esm/index.js +48 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/json-schema.js +84 -0
- package/dist/esm/json-schema.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/policies.js +175 -0
- package/dist/esm/policies.js.map +1 -0
- package/dist/esm/primitives.js +249 -0
- package/dist/esm/primitives.js.map +1 -0
- package/dist/esm/reputation-events.js +125 -0
- package/dist/esm/reputation-events.js.map +1 -0
- package/dist/esm/resources.js +455 -0
- package/dist/esm/resources.js.map +1 -0
- package/dist/esm/reviewer-surface.js +612 -0
- package/dist/esm/reviewer-surface.js.map +1 -0
- package/dist/esm/reviews.js +141 -0
- package/dist/esm/reviews.js.map +1 -0
- package/dist/esm/taxonomy.js +278 -0
- package/dist/esm/taxonomy.js.map +1 -0
- package/dist/esm/webhooks.js +188 -0
- package/dist/esm/webhooks.js.map +1 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/json-schema.d.ts +2 -2
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +26 -21
- package/dist/json-schema.js.map +1 -1
- package/dist/policies.d.ts +1 -1
- package/dist/policies.d.ts.map +1 -1
- package/dist/policies.js +14 -14
- package/dist/policies.js.map +1 -1
- package/dist/primitives.d.ts +17 -0
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +22 -1
- package/dist/primitives.js.map +1 -1
- package/dist/reputation-events.js +19 -19
- package/dist/reputation-events.js.map +1 -1
- package/dist/resources.d.ts +116 -39
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +143 -121
- package/dist/resources.js.map +1 -1
- package/dist/reviewer-surface.d.ts +1956 -0
- package/dist/reviewer-surface.d.ts.map +1 -0
- package/dist/reviewer-surface.js +615 -0
- package/dist/reviewer-surface.js.map +1 -0
- package/dist/reviews.d.ts +2 -2
- package/dist/reviews.js +15 -15
- package/dist/reviews.js.map +1 -1
- package/dist/webhooks.d.ts +4 -4
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +18 -18
- package/dist/webhooks.js.map +1 -1
- package/package.json +4 -4
- package/src/appeals.ts +229 -0
- package/src/case-envelope.ts +5 -5
- package/src/decisions.ts +6 -6
- package/src/index.ts +13 -11
- package/src/json-schema.ts +13 -8
- package/src/policies.ts +3 -3
- package/src/primitives.ts +25 -0
- package/src/reputation-events.ts +4 -4
- package/src/resources.ts +136 -100
- package/src/reviewer-surface.ts +715 -0
- package/src/reviews.ts +4 -4
- package/src/webhooks.ts +4 -4
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Case Envelope (§5.1) — the universal content contract.
|
|
3
|
+
*
|
|
4
|
+
* §5 opens by naming the mistake this contract exists to avoid: designing
|
|
5
|
+
* moderation around `post`, `comment`, `room` or `product`. Nothing below knows
|
|
6
|
+
* what a post is. An envelope is resources, relations, pseudonymous principals,
|
|
7
|
+
* allegations, a policy reference and privacy terms; what the material happens
|
|
8
|
+
* to be called in the application that sent it lives in `subject.type`, which
|
|
9
|
+
* is a namespaced label, not a branch in this file.
|
|
10
|
+
*
|
|
11
|
+
* Two rules govern the whole module.
|
|
12
|
+
*
|
|
13
|
+
* **Everything inbound is strict.** An unknown key on an envelope is either a
|
|
14
|
+
* typo — in which case the application believes it sent context that the jury
|
|
15
|
+
* will never see — or a field somebody hopes will reach the reviewer's screen.
|
|
16
|
+
* Both are rejected loudly. §10.11's "unknown fields must not break clients"
|
|
17
|
+
* governs what CrowdSource SENDS; see `decisions.ts` and `webhooks.ts` for the
|
|
18
|
+
* other half of that rule.
|
|
19
|
+
*
|
|
20
|
+
* **References resolve inside the envelope.** §5.5 requires the backend to
|
|
21
|
+
* check that every referenced id exists. That check lives here, in the schema,
|
|
22
|
+
* because it is a property of the document and nothing downstream can restate
|
|
23
|
+
* it as reliably. It is extended past `relations` to every in-envelope
|
|
24
|
+
* reference — `subject.primaryResourceId`, `allegation.resourceIds`,
|
|
25
|
+
* `authorPrincipalRef`, conversation members, listing media and seller, profile
|
|
26
|
+
* avatar — since a dangling id is the same defect wherever it appears.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
import { CONTRACT_LIMITS, ExternalIdSchema, HttpUrlSchema, IdentifierSchema, MetadataBagSchema, TimestampSchema, } from './primitives.js';
|
|
30
|
+
import { CasePolicyRefSchema } from './policies.js';
|
|
31
|
+
import { PRINCIPAL_TARGETED_RELATION_TYPES, PrincipalRefSchema, RelationSchema, ResourceIdSchema, ResourceSchema, } from './resources.js';
|
|
32
|
+
import { SensitivityHintSchema, TaxonomyCodeSchema } from './taxonomy.js';
|
|
33
|
+
/**
|
|
34
|
+
* The contract version carried inside the payload.
|
|
35
|
+
*
|
|
36
|
+
* §10.11 keeps this separate from the `/v1` route version on purpose: the HTTP
|
|
37
|
+
* surface and the document shape evolve at different speeds, and an additive
|
|
38
|
+
* change to either bumps neither.
|
|
39
|
+
*/
|
|
40
|
+
export const CASE_ENVELOPE_SCHEMA_VERSION = 'crowdsource.case.v1';
|
|
41
|
+
export const CaseEnvelopeSchemaVersionSchema = z.literal(CASE_ENVELOPE_SCHEMA_VERSION);
|
|
42
|
+
/** §5.4 namespaced subject types. */
|
|
43
|
+
export const STANDARD_SUBJECT_TYPES = [
|
|
44
|
+
'social.post',
|
|
45
|
+
'social.comment',
|
|
46
|
+
'chat.message',
|
|
47
|
+
'chat.conversation',
|
|
48
|
+
'identity.profile',
|
|
49
|
+
'commerce.listing',
|
|
50
|
+
'commerce.review',
|
|
51
|
+
'forum.thread',
|
|
52
|
+
'video.live_segment',
|
|
53
|
+
'document.file',
|
|
54
|
+
'gaming.username',
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* A subject type: one of §5.4's standard types, or an application's own
|
|
58
|
+
* `custom.<organization>.<object_type>`.
|
|
59
|
+
*
|
|
60
|
+
* The custom branch is a pattern rather than an open string so a tenant's
|
|
61
|
+
* private vocabulary is always visibly namespaced and can never collide with a
|
|
62
|
+
* standard type or be mistaken for one.
|
|
63
|
+
*/
|
|
64
|
+
export const SubjectTypeSchema = z.union([
|
|
65
|
+
z.enum(STANDARD_SUBJECT_TYPES),
|
|
66
|
+
z
|
|
67
|
+
.string()
|
|
68
|
+
.max(CONTRACT_LIMITS.IDENTIFIER_MAX_LENGTH)
|
|
69
|
+
.regex(/^custom\.[a-z0-9][a-z0-9_-]*\.[a-z0-9][a-z0-9_-]*$/, 'a custom subject type must be "custom.<organization>.<object_type>"'),
|
|
70
|
+
]);
|
|
71
|
+
/** §5.1 / Appendix A `subject`. */
|
|
72
|
+
export const CaseSubjectSchema = z.strictObject({
|
|
73
|
+
externalId: ExternalIdSchema,
|
|
74
|
+
type: SubjectTypeSchema,
|
|
75
|
+
primaryResourceId: ResourceIdSchema,
|
|
76
|
+
/** Where the application's own users see the object. Never fetched by a jury. */
|
|
77
|
+
permalink: HttpUrlSchema.optional(),
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* §3 principal kinds.
|
|
81
|
+
*
|
|
82
|
+
* The plan defines a principal in prose — "an Oxy user, a local user, an
|
|
83
|
+
* organization, a bot or a federated actor" — and only ever writes `oxy_user`
|
|
84
|
+
* in an example. These are those five in the plan's own order; the tokens are
|
|
85
|
+
* this contract's, the concepts are the plan's.
|
|
86
|
+
*/
|
|
87
|
+
export const PRINCIPAL_TYPES = [
|
|
88
|
+
'oxy_user',
|
|
89
|
+
'local_user',
|
|
90
|
+
'organization',
|
|
91
|
+
'bot',
|
|
92
|
+
'federated_actor',
|
|
93
|
+
];
|
|
94
|
+
export const PrincipalTypeSchema = z.enum(PRINCIPAL_TYPES);
|
|
95
|
+
/**
|
|
96
|
+
* §5.1 `principalBindings` — proof that a pseudonymous ref corresponds to a
|
|
97
|
+
* verifiable identity.
|
|
98
|
+
*
|
|
99
|
+
* `bindingProofId` is REQUIRED for `oxy_user` and optional for every other
|
|
100
|
+
* principal type. That is the "no binding proof, no Oxy Trust effect" invariant
|
|
101
|
+
* made structural at the earliest possible point: an Oxy identity asserted
|
|
102
|
+
* without a proof cannot be expressed. The other four types can never move Oxy
|
|
103
|
+
* reputation anyway — they have no Oxy identity to move — and requiring a proof
|
|
104
|
+
* from them would lock out every tenant whose users are not Oxy users, which is
|
|
105
|
+
* the multi-tenant case this whole product exists for.
|
|
106
|
+
*/
|
|
107
|
+
export const PrincipalBindingSchema = z
|
|
108
|
+
.strictObject({
|
|
109
|
+
principalRef: PrincipalRefSchema,
|
|
110
|
+
type: PrincipalTypeSchema,
|
|
111
|
+
/** The application's own id for the actor. Pseudonymous to the jury. */
|
|
112
|
+
externalPrincipalId: ExternalIdSchema.optional(),
|
|
113
|
+
bindingProofId: IdentifierSchema.optional(),
|
|
114
|
+
boundAt: TimestampSchema.optional(),
|
|
115
|
+
})
|
|
116
|
+
.superRefine((binding, ctx) => {
|
|
117
|
+
if (binding.type === 'oxy_user' && binding.bindingProofId === undefined) {
|
|
118
|
+
ctx.addIssue({
|
|
119
|
+
code: 'custom',
|
|
120
|
+
path: ['bindingProofId'],
|
|
121
|
+
message: 'an oxy_user binding must carry a bindingProofId',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* §5.1 `allegations` — what the reporter claims, not what is true.
|
|
127
|
+
*
|
|
128
|
+
* §9.1 requires the jury to see this as an unverified allegation, and §6.2
|
|
129
|
+
* shows the allegation and the finding diverging as the normal case. Only
|
|
130
|
+
* `code` is required: §5.8's example carries nothing else.
|
|
131
|
+
*/
|
|
132
|
+
export const AllegationSchema = z.strictObject({
|
|
133
|
+
code: TaxonomyCodeSchema,
|
|
134
|
+
resourceIds: z
|
|
135
|
+
.array(ResourceIdSchema)
|
|
136
|
+
.max(CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX)
|
|
137
|
+
.optional(),
|
|
138
|
+
reporterPrincipalRef: PrincipalRefSchema.optional(),
|
|
139
|
+
details: z.string().max(CONTRACT_LIMITS.LONG_TEXT_MAX_LENGTH).optional(),
|
|
140
|
+
});
|
|
141
|
+
/**
|
|
142
|
+
* The environment the report came from, as the APPLICATION sees it.
|
|
143
|
+
*
|
|
144
|
+
* `staging` is deliberately absent. The plan's §12.4 three-environment model is
|
|
145
|
+
* not what this ecosystem runs — CrowdSource has one deployment, and
|
|
146
|
+
* tenant-facing sandboxing is an application-trust state inside it. A report
|
|
147
|
+
* from a tenant's own pre-production is a sandbox report or it is a real one.
|
|
148
|
+
*/
|
|
149
|
+
export const SOURCE_ENVIRONMENTS = ['production', 'sandbox'];
|
|
150
|
+
export const SourceEnvironmentSchema = z.enum(SOURCE_ENVIRONMENTS);
|
|
151
|
+
/** Appendix A `source`. */
|
|
152
|
+
export const CaseSourceSchema = z.strictObject({
|
|
153
|
+
environment: SourceEnvironmentSchema,
|
|
154
|
+
submittedAt: TimestampSchema,
|
|
155
|
+
});
|
|
156
|
+
/** §5.1 / Appendix A `privacy`. */
|
|
157
|
+
export const CasePrivacySchema = z.strictObject({
|
|
158
|
+
/** §13.6 defaults to 30 days after a final decision, configurable by policy. */
|
|
159
|
+
retentionDays: z.number().int().positive().max(CONTRACT_LIMITS.RETENTION_DAYS_MAX),
|
|
160
|
+
/**
|
|
161
|
+
* Whether a community jury may see this at all. `false` routes the case to a
|
|
162
|
+
* specialist pool (§7.5); it is not a preference the triage may override
|
|
163
|
+
* upwards.
|
|
164
|
+
*/
|
|
165
|
+
allowCommunityReview: z.boolean(),
|
|
166
|
+
containsPersonalData: z.boolean().optional(),
|
|
167
|
+
sensitivityHint: SensitivityHintSchema.optional(),
|
|
168
|
+
});
|
|
169
|
+
/**
|
|
170
|
+
* §5.1 / Appendix A `urgency` — an input to triage, never to a verdict.
|
|
171
|
+
*
|
|
172
|
+
* `hint` stays an open lowercase token for the same reason as
|
|
173
|
+
* `sensitivityHint`: the plan names exactly one value (`normal`) and §7.4 makes
|
|
174
|
+
* clear the authoritative priority is computed server-side from several
|
|
175
|
+
* signals, of which this is one. Closing the list is a product decision that
|
|
176
|
+
* has not been made.
|
|
177
|
+
*/
|
|
178
|
+
export const CaseUrgencySchema = z.strictObject({
|
|
179
|
+
hint: z
|
|
180
|
+
.string()
|
|
181
|
+
.min(1)
|
|
182
|
+
.max(40)
|
|
183
|
+
.regex(/^[a-z][a-z0-9_]*$/, 'must be a lowercase token'),
|
|
184
|
+
/** How many people the material reached, as the application counts it. */
|
|
185
|
+
reach: z.number().int().nonnegative().optional(),
|
|
186
|
+
activeDistribution: z.boolean().optional(),
|
|
187
|
+
});
|
|
188
|
+
const caseEnvelopeShape = {
|
|
189
|
+
schemaVersion: CaseEnvelopeSchemaVersionSchema,
|
|
190
|
+
/**
|
|
191
|
+
* An assertion by the caller, never the source of tenancy.
|
|
192
|
+
*
|
|
193
|
+
* `applicationId` comes from the credential (Appendix F). This field exists
|
|
194
|
+
* so a mismatch can be DETECTED — the ingress route compares it to the
|
|
195
|
+
* credential-derived id and rejects a disagreement rather than trusting
|
|
196
|
+
* either side silently. No schema can enforce that; it is stated here so the
|
|
197
|
+
* rule travels with the field it constrains.
|
|
198
|
+
*/
|
|
199
|
+
applicationId: IdentifierSchema,
|
|
200
|
+
externalReportId: ExternalIdSchema,
|
|
201
|
+
source: CaseSourceSchema.optional(),
|
|
202
|
+
subject: CaseSubjectSchema,
|
|
203
|
+
principalBindings: z
|
|
204
|
+
.array(PrincipalBindingSchema)
|
|
205
|
+
.max(CONTRACT_LIMITS.PRINCIPAL_BINDINGS_PER_ENVELOPE_MAX),
|
|
206
|
+
resources: z.array(ResourceSchema).min(1).max(CONTRACT_LIMITS.RESOURCES_PER_ENVELOPE_MAX),
|
|
207
|
+
relations: z.array(RelationSchema).max(CONTRACT_LIMITS.RELATIONS_PER_ENVELOPE_MAX),
|
|
208
|
+
allegations: z.array(AllegationSchema).min(1).max(CONTRACT_LIMITS.ALLEGATIONS_PER_ENVELOPE_MAX),
|
|
209
|
+
policy: CasePolicyRefSchema,
|
|
210
|
+
privacy: CasePrivacySchema,
|
|
211
|
+
urgency: CaseUrgencySchema.optional(),
|
|
212
|
+
metadata: MetadataBagSchema.optional(),
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* The Case Envelope.
|
|
216
|
+
*
|
|
217
|
+
* `principalBindings`, `resources`, `relations` and `allegations` are all
|
|
218
|
+
* required keys, matching §5.1's declared root structure and both worked
|
|
219
|
+
* examples; `resources` and `allegations` additionally require at least one
|
|
220
|
+
* entry, because an envelope with nothing to look at or no claim to evaluate is
|
|
221
|
+
* not a case. `source`, `urgency` and `metadata` are optional — §5.8 omits all
|
|
222
|
+
* three, Appendix A carries all three.
|
|
223
|
+
*/
|
|
224
|
+
export const CaseEnvelopeSchema = z
|
|
225
|
+
.strictObject(caseEnvelopeShape)
|
|
226
|
+
.superRefine((envelope, ctx) => {
|
|
227
|
+
const resourceIds = new Set();
|
|
228
|
+
envelope.resources.forEach((resource, index) => {
|
|
229
|
+
if (resourceIds.has(resource.id)) {
|
|
230
|
+
ctx.addIssue({
|
|
231
|
+
code: 'custom',
|
|
232
|
+
path: ['resources', index, 'id'],
|
|
233
|
+
message: `duplicate resource id "${resource.id}"`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
resourceIds.add(resource.id);
|
|
237
|
+
});
|
|
238
|
+
const principalRefs = new Set();
|
|
239
|
+
envelope.principalBindings.forEach((binding, index) => {
|
|
240
|
+
if (principalRefs.has(binding.principalRef)) {
|
|
241
|
+
ctx.addIssue({
|
|
242
|
+
code: 'custom',
|
|
243
|
+
path: ['principalBindings', index, 'principalRef'],
|
|
244
|
+
message: `duplicate principalRef "${binding.principalRef}"`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
principalRefs.add(binding.principalRef);
|
|
248
|
+
});
|
|
249
|
+
const requireResource = (id, path) => {
|
|
250
|
+
if (!resourceIds.has(id)) {
|
|
251
|
+
ctx.addIssue({
|
|
252
|
+
code: 'custom',
|
|
253
|
+
path,
|
|
254
|
+
message: `no resource in this envelope has id "${id}"`,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const requirePrincipal = (ref, path) => {
|
|
259
|
+
if (!principalRefs.has(ref)) {
|
|
260
|
+
ctx.addIssue({
|
|
261
|
+
code: 'custom',
|
|
262
|
+
path,
|
|
263
|
+
message: `no principal binding in this envelope has principalRef "${ref}"`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
requireResource(envelope.subject.primaryResourceId, ['subject', 'primaryResourceId']);
|
|
268
|
+
const primary = envelope.resources.find((resource) => resource.id === envelope.subject.primaryResourceId);
|
|
269
|
+
if (primary !== undefined && primary.role !== 'subject') {
|
|
270
|
+
ctx.addIssue({
|
|
271
|
+
code: 'custom',
|
|
272
|
+
path: ['subject', 'primaryResourceId'],
|
|
273
|
+
message: `the primary resource must have role "subject", not "${primary.role}"`,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
envelope.resources.forEach((resource, index) => {
|
|
277
|
+
if (resource.authorPrincipalRef !== undefined) {
|
|
278
|
+
requirePrincipal(resource.authorPrincipalRef, ['resources', index, 'authorPrincipalRef']);
|
|
279
|
+
}
|
|
280
|
+
switch (resource.type) {
|
|
281
|
+
case 'profile':
|
|
282
|
+
if (resource.data.avatarRef !== undefined) {
|
|
283
|
+
requireResource(resource.data.avatarRef, ['resources', index, 'data', 'avatarRef']);
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case 'conversation':
|
|
287
|
+
resource.data.messageResourceIds.forEach((messageId, messageIndex) => {
|
|
288
|
+
requireResource(messageId, [
|
|
289
|
+
'resources',
|
|
290
|
+
index,
|
|
291
|
+
'data',
|
|
292
|
+
'messageResourceIds',
|
|
293
|
+
messageIndex,
|
|
294
|
+
]);
|
|
295
|
+
});
|
|
296
|
+
break;
|
|
297
|
+
case 'listing':
|
|
298
|
+
if (resource.data.sellerRef !== undefined) {
|
|
299
|
+
requirePrincipal(resource.data.sellerRef, ['resources', index, 'data', 'sellerRef']);
|
|
300
|
+
}
|
|
301
|
+
resource.data.mediaRefs?.forEach((mediaId, mediaIndex) => {
|
|
302
|
+
requireResource(mediaId, ['resources', index, 'data', 'mediaRefs', mediaIndex]);
|
|
303
|
+
});
|
|
304
|
+
break;
|
|
305
|
+
default:
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
const seenRelations = new Set();
|
|
310
|
+
envelope.relations.forEach((relation, index) => {
|
|
311
|
+
requireResource(relation.from, ['relations', index, 'from']);
|
|
312
|
+
if (PRINCIPAL_TARGETED_RELATION_TYPES.some((type) => type === relation.type)) {
|
|
313
|
+
requirePrincipal(relation.to, ['relations', index, 'to']);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
requireResource(relation.to, ['relations', index, 'to']);
|
|
317
|
+
if (relation.from === relation.to) {
|
|
318
|
+
ctx.addIssue({
|
|
319
|
+
code: 'custom',
|
|
320
|
+
path: ['relations', index, 'to'],
|
|
321
|
+
message: 'a resource cannot relate to itself',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const key = `${relation.from}\u0000${relation.type}\u0000${relation.to}`;
|
|
326
|
+
if (seenRelations.has(key)) {
|
|
327
|
+
ctx.addIssue({
|
|
328
|
+
code: 'custom',
|
|
329
|
+
path: ['relations', index],
|
|
330
|
+
message: 'duplicate relation',
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
seenRelations.add(key);
|
|
334
|
+
});
|
|
335
|
+
envelope.allegations.forEach((allegation, index) => {
|
|
336
|
+
allegation.resourceIds?.forEach((resourceId, resourceIndex) => {
|
|
337
|
+
requireResource(resourceId, ['allegations', index, 'resourceIds', resourceIndex]);
|
|
338
|
+
});
|
|
339
|
+
if (allegation.reporterPrincipalRef !== undefined) {
|
|
340
|
+
requirePrincipal(allegation.reporterPrincipalRef, [
|
|
341
|
+
'allegations',
|
|
342
|
+
index,
|
|
343
|
+
'reporterPrincipalRef',
|
|
344
|
+
]);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
/** §3.2 report states. */
|
|
349
|
+
export const REPORT_STATUSES = ['received', 'merged', 'invalid', 'withdrawn', 'closed'];
|
|
350
|
+
export const ReportStatusSchema = z.enum(REPORT_STATUSES);
|
|
351
|
+
/**
|
|
352
|
+
* `POST /v1/reports` (§10.4).
|
|
353
|
+
*
|
|
354
|
+
* The request repeats `externalReportId` outside the envelope, so the two must
|
|
355
|
+
* agree — a mismatch means the idempotency key and the document disagree about
|
|
356
|
+
* which report this is, and §12.7's `application_id + external_report_id`
|
|
357
|
+
* uniqueness would then be enforced against the wrong value.
|
|
358
|
+
*/
|
|
359
|
+
export const CreateReportRequestSchema = z
|
|
360
|
+
.strictObject({
|
|
361
|
+
externalReportId: ExternalIdSchema,
|
|
362
|
+
envelope: CaseEnvelopeSchema,
|
|
363
|
+
})
|
|
364
|
+
.superRefine((request, ctx) => {
|
|
365
|
+
if (request.externalReportId !== request.envelope.externalReportId) {
|
|
366
|
+
ctx.addIssue({
|
|
367
|
+
code: 'custom',
|
|
368
|
+
path: ['envelope', 'externalReportId'],
|
|
369
|
+
message: 'externalReportId must match the envelope',
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
/** The `202 Accepted` body of §10.4. Loose: it travels outbound (§10.11). */
|
|
374
|
+
export const CreateReportResponseSchema = z.looseObject({
|
|
375
|
+
reportId: IdentifierSchema,
|
|
376
|
+
caseId: IdentifierSchema,
|
|
377
|
+
status: ReportStatusSchema,
|
|
378
|
+
merged: z.boolean(),
|
|
379
|
+
});
|
|
380
|
+
//# sourceMappingURL=case-envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-envelope.js","sourceRoot":"","sources":["../../src/case-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EACL,iCAAiC,EACjC,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAG1E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,qBAAqB,CAAC;AAElE,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;AAEvF,qCAAqC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,aAAa;IACb,gBAAgB;IAChB,cAAc;IACd,mBAAmB;IACnB,kBAAkB;IAClB,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;IACd,oBAAoB;IACpB,eAAe;IACf,iBAAiB;CACT,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC;IACvC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC;IAC9B,CAAC;SACE,MAAM,EAAE;SACR,GAAG,CAAC,eAAe,CAAC,qBAAqB,CAAC;SAC1C,KAAK,CACJ,oDAAoD,EACpD,qEAAqE,CACtE;CACJ,CAAC,CAAC;AAGH,mCAAmC;AACnC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,UAAU,EAAE,gBAAgB;IAC5B,IAAI,EAAE,iBAAiB;IACvB,iBAAiB,EAAE,gBAAgB;IACnC,iFAAiF;IACjF,SAAS,EAAE,aAAa,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAGH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,UAAU;IACV,YAAY;IACZ,cAAc;IACd,KAAK;IACL,iBAAiB;CACT,CAAC;AACX,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAG3D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,YAAY,CAAC;IACZ,YAAY,EAAE,kBAAkB;IAChC,IAAI,EAAE,mBAAmB;IACzB,wEAAwE;IACxE,mBAAmB,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IAChD,cAAc,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IAC3C,OAAO,EAAE,eAAe,CAAC,QAAQ,EAAE;CACpC,CAAC;KACD,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;IAC5B,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACxE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,gBAAgB,CAAC;YACxB,OAAO,EAAE,iDAAiD;SAC3D,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7C,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,CAAC;SACX,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,eAAe,CAAC,6BAA6B,CAAC;SAClD,QAAQ,EAAE;IACb,oBAAoB,EAAE,kBAAkB,CAAC,QAAQ,EAAE;IACnD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,QAAQ,EAAE;CACzE,CAAC,CAAC;AAGH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,YAAY,EAAE,SAAS,CAAU,CAAC;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAEnE,2BAA2B;AAC3B,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7C,WAAW,EAAE,uBAAuB;IACpC,WAAW,EAAE,eAAe;CAC7B,CAAC,CAAC;AAGH,mCAAmC;AACnC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,gFAAgF;IAChF,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,kBAAkB,CAAC;IAClF;;;;OAIG;IACH,oBAAoB,EAAE,CAAC,CAAC,OAAO,EAAE;IACjC,oBAAoB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5C,eAAe,EAAE,qBAAqB,CAAC,QAAQ,EAAE;CAClD,CAAC,CAAC;AAGH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,KAAK,CAAC,mBAAmB,EAAE,2BAA2B,CAAC;IAC1D,0EAA0E;IAC1E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IAChD,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC;AAGH,MAAM,iBAAiB,GAAG;IACxB,aAAa,EAAE,+BAA+B;IAC9C;;;;;;;;OAQG;IACH,aAAa,EAAE,gBAAgB;IAC/B,gBAAgB,EAAE,gBAAgB;IAClC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,iBAAiB;IAC1B,iBAAiB,EAAE,CAAC;SACjB,KAAK,CAAC,sBAAsB,CAAC;SAC7B,GAAG,CAAC,eAAe,CAAC,mCAAmC,CAAC;IAC3D,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,0BAA0B,CAAC;IACzF,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,0BAA0B,CAAC;IAClF,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,4BAA4B,CAAC;IAC/F,MAAM,EAAE,mBAAmB;IAC3B,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACrC,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,EAAE;CACvC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC;KAChC,YAAY,CAAC,iBAAiB,CAAC;KAC/B,WAAW,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;IAC7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC7C,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACjC,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC;gBAChC,OAAO,EAAE,0BAA0B,QAAQ,CAAC,EAAE,GAAG;aAClD,CAAC,CAAC;QACL,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACpD,IAAI,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC5C,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,mBAAmB,EAAE,KAAK,EAAE,cAAc,CAAC;gBAClD,OAAO,EAAE,2BAA2B,OAAO,CAAC,YAAY,GAAG;aAC5D,CAAC,CAAC;QACL,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,CAAC,EAAU,EAAE,IAAyB,EAAQ,EAAE;QACtE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI;gBACJ,OAAO,EAAE,wCAAwC,EAAE,GAAG;aACvD,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;IACF,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAE,IAAyB,EAAQ,EAAE;QACxE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI;gBACJ,OAAO,EAAE,2DAA2D,GAAG,GAAG;aAC3E,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;IAEF,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CACrC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CACjE,CAAC;IACF,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACxD,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,SAAS,EAAE,mBAAmB,CAAC;YACtC,OAAO,EAAE,uDAAuD,OAAO,CAAC,IAAI,GAAG;SAChF,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC7C,IAAI,QAAQ,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;YAC9C,gBAAgB,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtB,KAAK,SAAS;gBACZ,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC1C,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;gBACtF,CAAC;gBACD,MAAM;YACR,KAAK,cAAc;gBACjB,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE;oBACnE,eAAe,CAAC,SAAS,EAAE;wBACzB,WAAW;wBACX,KAAK;wBACL,MAAM;wBACN,oBAAoB;wBACpB,YAAY;qBACb,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC1C,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;gBACvF,CAAC;gBACD,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE;oBACvD,eAAe,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;gBAClF,CAAC,CAAC,CAAC;gBACH,MAAM;YACR;gBACE,MAAM;QACV,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC7C,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7D,IAAI,iCAAiC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7E,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,eAAe,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;YACzD,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClC,GAAG,CAAC,QAAQ,CAAC;oBACX,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC;oBAChC,OAAO,EAAE,oCAAoC;iBAC9C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,CAAC;QACzE,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;gBAC1B,OAAO,EAAE,oBAAoB;aAC9B,CAAC,CAAC;QACL,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;QACjD,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,UAAU,EAAE,aAAa,EAAE,EAAE;YAC5D,eAAe,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;QACH,IAAI,UAAU,CAAC,oBAAoB,KAAK,SAAS,EAAE,CAAC;YAClD,gBAAgB,CAAC,UAAU,CAAC,oBAAoB,EAAE;gBAChD,aAAa;gBACb,KAAK;gBACL,sBAAsB;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAGL,0BAA0B;AAC1B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AACjG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAG1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,YAAY,CAAC;IACZ,gBAAgB,EAAE,gBAAgB;IAClC,QAAQ,EAAE,kBAAkB;CAC7B,CAAC;KACD,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;IAC5B,IAAI,OAAO,CAAC,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QACnE,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,UAAU,EAAE,kBAAkB,CAAC;YACtC,OAAO,EAAE,0CAA0C;SACpD,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL,6EAA6E;AAC7E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,WAAW,CAAC;IACtD,QAAQ,EAAE,gBAAgB;IAC1B,MAAM,EAAE,gBAAgB;IACxB,MAAM,EAAE,kBAAkB;IAC1B,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closing the forward-compatibility catchall, at the type level only.
|
|
3
|
+
*
|
|
4
|
+
* The outbound schemas in this package are deliberately loose — see the note in
|
|
5
|
+
* `index.ts`. A decision, a webhook envelope and an event payload all pass
|
|
6
|
+
* unknown fields through, so a newer CrowdSource never breaks an older client
|
|
7
|
+
* and a receiver that persists `event.data` keeps all of it. That runtime
|
|
8
|
+
* behaviour is a requirement (§10.11) and nothing here changes it.
|
|
9
|
+
*
|
|
10
|
+
* What it costs, unmodified, is every field-name check the compiler could have
|
|
11
|
+
* given an integrator. `z.infer` of a loose object carries an index signature,
|
|
12
|
+
* and an index signature makes ANY property access legal:
|
|
13
|
+
*
|
|
14
|
+
* receipt.externalReportId // compiles clean. `undefined` at runtime.
|
|
15
|
+
* decision.decisionId // compiles clean. The field is `id`.
|
|
16
|
+
* decision.policyVersion // compiles clean. The field is `policyVersions`.
|
|
17
|
+
*
|
|
18
|
+
* That is not a rough edge. It silently produces wrong code in the integrator's
|
|
19
|
+
* repository, with no error anywhere, and it is the same shape of defect as a
|
|
20
|
+
* duplicated dependency: the compiler reports success and the failure surfaces
|
|
21
|
+
* later as a value that is mysteriously absent.
|
|
22
|
+
*
|
|
23
|
+
* So the schema stays loose and the exported TYPE is closed. `Closed<T>` strips
|
|
24
|
+
* index signatures from `T` and from everything nested inside it, leaving the
|
|
25
|
+
* fields the contract actually declares — with their optionality, their
|
|
26
|
+
* `readonly` modifiers, and any discriminated union intact. A parse still
|
|
27
|
+
* accepts and preserves unknown fields; naming one you invented no longer
|
|
28
|
+
* compiles. Reaching a field the contract does not declare, deliberately, is
|
|
29
|
+
* what an index-signature access on the parsed value is for.
|
|
30
|
+
*/
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=closed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"closed.js","sourceRoot":"","sources":["../../src/closed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decisions (§9.6, §9.9, Appendix B).
|
|
3
|
+
*
|
|
4
|
+
* "A published decision is never edited, only superseded." No parse can see a
|
|
5
|
+
* second write, so schema-level immutability would be theatre — and a shallow
|
|
6
|
+
* `Object.freeze` on the parsed result would be worse than theatre, since it
|
|
7
|
+
* would leave `findings` mutable while looking like a guarantee. Immutability
|
|
8
|
+
* is enforced where it is real: the store never updates a published revision.
|
|
9
|
+
*
|
|
10
|
+
* What the schema CAN enforce is the shape of the supersession chain, and it
|
|
11
|
+
* does: revision 1 supersedes nothing, and every later revision names what it
|
|
12
|
+
* replaced. §9.9's worked example is exactly that, and a revision 2 that
|
|
13
|
+
* supersedes nothing would be an edit wearing a new number.
|
|
14
|
+
*
|
|
15
|
+
* Decisions travel outbound, to tenants and to the reviewer app, so they are
|
|
16
|
+
* `.loose()`: §10.11 requires unknown fields not to break clients, and passing
|
|
17
|
+
* them through rather than stripping them means a client that persists a
|
|
18
|
+
* decision keeps whatever a newer CrowdSource added. This is the opposite of
|
|
19
|
+
* the inbound rule in `case-envelope.ts`, for the opposite reason.
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
import { CONTRACT_LIMITS, IdentifierSchema, TimestampSchema, UnitIntervalSchema } from './primitives.js';
|
|
23
|
+
import { DecisionPolicyVersionsSchema, PolicyRuleIdSchema } from './policies.js';
|
|
24
|
+
import { ResourceIdSchema } from './resources.js';
|
|
25
|
+
import { ContextSufficiencySchema } from './reviews.js';
|
|
26
|
+
import { FindingAttributionSchema, FindingContextSchema, FindingScopeSchema, RecommendedActionSchema, SeveritySchema, TaxonomyCodeSchema, } from './taxonomy.js';
|
|
27
|
+
/** §3.2 decision states. */
|
|
28
|
+
export const DECISION_STATUSES = ['provisional', 'final', 'superseded', 'corrected'];
|
|
29
|
+
export const DecisionStatusSchema = z.enum(DECISION_STATUSES);
|
|
30
|
+
/**
|
|
31
|
+
* §9.6 outcomes.
|
|
32
|
+
*
|
|
33
|
+
* `inconclusive` is its own outcome and must never collapse into
|
|
34
|
+
* `no_violation`: a jury that reviewed the case and did not reach the threshold
|
|
35
|
+
* has said something different from a jury that agreed nothing was wrong. Both
|
|
36
|
+
* are here, distinctly, and no code in this package maps one to the other.
|
|
37
|
+
*/
|
|
38
|
+
export const DECISION_OUTCOMES = [
|
|
39
|
+
'violation',
|
|
40
|
+
'no_violation',
|
|
41
|
+
'insufficient_context',
|
|
42
|
+
'inconclusive',
|
|
43
|
+
'content_unavailable',
|
|
44
|
+
'duplicate',
|
|
45
|
+
'escalated',
|
|
46
|
+
];
|
|
47
|
+
export const DecisionOutcomeSchema = z.enum(DECISION_OUTCOMES);
|
|
48
|
+
/**
|
|
49
|
+
* A confirmed finding (Appendix B).
|
|
50
|
+
*
|
|
51
|
+
* Carries two fields a `ReviewFinding` does not. `scope` says how far the
|
|
52
|
+
* finding reaches and is what §11.7.5 gates an Oxy Trust effect on;
|
|
53
|
+
* `attribution` names whose conduct it is. Neither belongs on an individual
|
|
54
|
+
* review: a reviewer classifies material, and it is the consensus process that
|
|
55
|
+
* decides the classification is confirmed and therefore attributable.
|
|
56
|
+
*
|
|
57
|
+
* `attribution` is optional because plenty of confirmed findings attribute
|
|
58
|
+
* nothing to anybody — material can violate a rule without any principal having
|
|
59
|
+
* behaved badly.
|
|
60
|
+
*
|
|
61
|
+
* `context` carries §6.2's exception through unchanged from the reviews that
|
|
62
|
+
* agreed on it. It is one of §9.4's six consensus dimensions, so a published
|
|
63
|
+
* finding that dropped it would be a decision nobody could reproduce from the
|
|
64
|
+
* ballots.
|
|
65
|
+
*/
|
|
66
|
+
export const DecisionFindingSchema = z.looseObject({
|
|
67
|
+
code: TaxonomyCodeSchema,
|
|
68
|
+
resourceIds: z.array(ResourceIdSchema).min(1).max(CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX),
|
|
69
|
+
severity: SeveritySchema,
|
|
70
|
+
context: FindingContextSchema.optional(),
|
|
71
|
+
scope: FindingScopeSchema,
|
|
72
|
+
attribution: FindingAttributionSchema.optional(),
|
|
73
|
+
policyRuleIds: z.array(PolicyRuleIdSchema).max(CONTRACT_LIMITS.POLICY_RULE_IDS_MAX).optional(),
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* A recommended action bound to what it applies to (Appendix B).
|
|
77
|
+
*
|
|
78
|
+
* §10.7's webhook example writes recommended actions as bare strings, the way
|
|
79
|
+
* §9.3 writes them for a single review. Appendix B — the reference Decision —
|
|
80
|
+
* writes them as objects. The object form wins for decisions: a decision that
|
|
81
|
+
* recommends removal without saying what to remove is not actionable, and an
|
|
82
|
+
* application that acts on it is guessing. The string form survives where the
|
|
83
|
+
* plan uses it, on a review.
|
|
84
|
+
*
|
|
85
|
+
* `targetResourceIds` is optional because some actions have no target —
|
|
86
|
+
* `escalate`, `no_global_effect` and `no_action` are about the case, not about
|
|
87
|
+
* a resource.
|
|
88
|
+
*/
|
|
89
|
+
export const DecisionRecommendedActionSchema = z.looseObject({
|
|
90
|
+
action: RecommendedActionSchema,
|
|
91
|
+
targetResourceIds: z
|
|
92
|
+
.array(ResourceIdSchema)
|
|
93
|
+
.max(CONTRACT_LIMITS.RESOURCE_REFS_PER_FINDING_MAX)
|
|
94
|
+
.optional(),
|
|
95
|
+
});
|
|
96
|
+
/** Floating point: `winningVotes / decisiveVotes` will not be exact. */
|
|
97
|
+
const AGREEMENT_TOLERANCE = 1e-6;
|
|
98
|
+
/**
|
|
99
|
+
* The panel that produced the decision (Appendix B).
|
|
100
|
+
*
|
|
101
|
+
* The arithmetic is checked here because it is the auditable trace of "one
|
|
102
|
+
* qualified person, one vote": `agreement` must equal
|
|
103
|
+
* `winningVotes / decisiveVotes` (§9.5), and the counts must nest. If a
|
|
104
|
+
* weighting ever crept into the engine, `agreement` would stop matching the
|
|
105
|
+
* count of people, and this is where that shows up.
|
|
106
|
+
*
|
|
107
|
+
* `size` is not restricted to {3, 5, 7}. Those are §9.4's community panel
|
|
108
|
+
* sizes, but §9.4 also routes critical categories to specialist pools that do
|
|
109
|
+
* not use the standard jury at all, and an appeal panel is only bounded below.
|
|
110
|
+
*/
|
|
111
|
+
export const DecisionJurySchema = z
|
|
112
|
+
.looseObject({
|
|
113
|
+
size: z.number().int().positive().max(CONTRACT_LIMITS.JURY_SIZE_MAX),
|
|
114
|
+
/** Reviews that expressed a decisive opinion — the denominator of §9.5. */
|
|
115
|
+
decisiveVotes: z.number().int().positive().max(CONTRACT_LIMITS.JURY_SIZE_MAX),
|
|
116
|
+
winningVotes: z.number().int().nonnegative().max(CONTRACT_LIMITS.JURY_SIZE_MAX),
|
|
117
|
+
agreement: UnitIntervalSchema,
|
|
118
|
+
specialistPresent: z.boolean(),
|
|
119
|
+
})
|
|
120
|
+
.superRefine((jury, ctx) => {
|
|
121
|
+
if (jury.decisiveVotes > jury.size) {
|
|
122
|
+
ctx.addIssue({
|
|
123
|
+
code: 'custom',
|
|
124
|
+
path: ['decisiveVotes'],
|
|
125
|
+
message: 'decisiveVotes cannot exceed the panel size',
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (jury.winningVotes > jury.decisiveVotes) {
|
|
130
|
+
ctx.addIssue({
|
|
131
|
+
code: 'custom',
|
|
132
|
+
path: ['winningVotes'],
|
|
133
|
+
message: 'winningVotes cannot exceed decisiveVotes',
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const expected = jury.winningVotes / jury.decisiveVotes;
|
|
138
|
+
if (Math.abs(jury.agreement - expected) > AGREEMENT_TOLERANCE) {
|
|
139
|
+
ctx.addIssue({
|
|
140
|
+
code: 'custom',
|
|
141
|
+
path: ['agreement'],
|
|
142
|
+
message: `agreement must equal winningVotes / decisiveVotes (${expected})`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
/**
|
|
147
|
+
* One immutable revision of a case's outcome (Appendix B).
|
|
148
|
+
*
|
|
149
|
+
* Every field except `supersedesDecisionId` is required, following §12.8, which
|
|
150
|
+
* marks `supersedes_decision_id` as the only nullable column on the decisions
|
|
151
|
+
* table. The DTO uses that column's name; §9.9's prose sketch writes the same
|
|
152
|
+
* link as `supersedes`.
|
|
153
|
+
*/
|
|
154
|
+
export const DecisionSchema = z
|
|
155
|
+
.looseObject({
|
|
156
|
+
id: IdentifierSchema,
|
|
157
|
+
caseId: IdentifierSchema,
|
|
158
|
+
revision: z.number().int().positive(),
|
|
159
|
+
status: DecisionStatusSchema,
|
|
160
|
+
outcome: DecisionOutcomeSchema,
|
|
161
|
+
contextSufficiency: ContextSufficiencySchema,
|
|
162
|
+
confidence: UnitIntervalSchema,
|
|
163
|
+
findings: z.array(DecisionFindingSchema).max(CONTRACT_LIMITS.FINDINGS_MAX),
|
|
164
|
+
recommendedActions: z
|
|
165
|
+
.array(DecisionRecommendedActionSchema)
|
|
166
|
+
.max(CONTRACT_LIMITS.RECOMMENDED_ACTIONS_MAX),
|
|
167
|
+
jury: DecisionJurySchema,
|
|
168
|
+
policyVersions: DecisionPolicyVersionsSchema,
|
|
169
|
+
supersedesDecisionId: IdentifierSchema.optional(),
|
|
170
|
+
publishedAt: TimestampSchema,
|
|
171
|
+
})
|
|
172
|
+
.superRefine((decision, ctx) => {
|
|
173
|
+
if (decision.revision === 1 && decision.supersedesDecisionId !== undefined) {
|
|
174
|
+
ctx.addIssue({
|
|
175
|
+
code: 'custom',
|
|
176
|
+
path: ['supersedesDecisionId'],
|
|
177
|
+
message: 'the first revision of a case supersedes nothing',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (decision.revision > 1 && decision.supersedesDecisionId === undefined) {
|
|
181
|
+
ctx.addIssue({
|
|
182
|
+
code: 'custom',
|
|
183
|
+
path: ['supersedesDecisionId'],
|
|
184
|
+
message: 'a revision after the first must name the decision it supersedes',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (decision.outcome === 'violation' && decision.findings.length === 0) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: 'custom',
|
|
190
|
+
path: ['findings'],
|
|
191
|
+
message: 'a violation outcome requires at least one finding',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../src/decisions.ts"],"names":[],"mappings":"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"}
|