@crowdsource.you/core 1.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.
- package/README.md +571 -0
- package/dist/cases.d.ts +66 -0
- package/dist/cases.d.ts.map +1 -0
- package/dist/cases.js +79 -0
- package/dist/cases.js.map +1 -0
- package/dist/client.d.ts +90 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +125 -0
- package/dist/client.js.map +1 -0
- package/dist/communityNotes.d.ts +49 -0
- package/dist/communityNotes.d.ts.map +1 -0
- package/dist/communityNotes.js +104 -0
- package/dist/communityNotes.js.map +1 -0
- package/dist/credential.d.ts +58 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +71 -0
- package/dist/credential.js.map +1 -0
- package/dist/defaults.d.ts +61 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +82 -0
- package/dist/defaults.js.map +1 -0
- package/dist/digest.d.ts +44 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +79 -0
- package/dist/digest.js.map +1 -0
- package/dist/envelope.d.ts +143 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +258 -0
- package/dist/envelope.js.map +1 -0
- package/dist/errors.d.ts +90 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +151 -0
- package/dist/errors.js.map +1 -0
- package/dist/esm/cases.js +74 -0
- package/dist/esm/cases.js.map +1 -0
- package/dist/esm/client.js +121 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/communityNotes.js +100 -0
- package/dist/esm/communityNotes.js.map +1 -0
- package/dist/esm/credential.js +67 -0
- package/dist/esm/credential.js.map +1 -0
- package/dist/esm/defaults.js +77 -0
- package/dist/esm/defaults.js.map +1 -0
- package/dist/esm/digest.js +74 -0
- package/dist/esm/digest.js.map +1 -0
- package/dist/esm/envelope.js +252 -0
- package/dist/esm/envelope.js.map +1 -0
- package/dist/esm/errors.js +140 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/express/index.js +29 -0
- package/dist/esm/express/index.js.map +1 -0
- package/dist/esm/express/middleware.js +293 -0
- package/dist/esm/express/middleware.js.map +1 -0
- package/dist/esm/express/store.js +69 -0
- package/dist/esm/express/store.js.map +1 -0
- package/dist/esm/express/verify.js +133 -0
- package/dist/esm/express/verify.js.map +1 -0
- package/dist/esm/index.js +57 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/outbox/client.js +43 -0
- package/dist/esm/outbox/client.js.map +1 -0
- package/dist/esm/outbox/decision.js +153 -0
- package/dist/esm/outbox/decision.js.map +1 -0
- package/dist/esm/outbox/delivery.js +129 -0
- package/dist/esm/outbox/delivery.js.map +1 -0
- package/dist/esm/outbox/enforcement/executor.js +219 -0
- package/dist/esm/outbox/enforcement/executor.js.map +1 -0
- package/dist/esm/outbox/enforcement/planner.js +326 -0
- package/dist/esm/outbox/enforcement/planner.js.map +1 -0
- package/dist/esm/outbox/evidence.js +173 -0
- package/dist/esm/outbox/evidence.js.map +1 -0
- package/dist/esm/outbox/inbound.js +87 -0
- package/dist/esm/outbox/inbound.js.map +1 -0
- package/dist/esm/outbox/index.js +71 -0
- package/dist/esm/outbox/index.js.map +1 -0
- package/dist/esm/outbox/intake.js +140 -0
- package/dist/esm/outbox/intake.js.map +1 -0
- package/dist/esm/outbox/integration.js +116 -0
- package/dist/esm/outbox/integration.js.map +1 -0
- package/dist/esm/outbox/outbox/dispatcher.js +114 -0
- package/dist/esm/outbox/outbox/dispatcher.js.map +1 -0
- package/dist/esm/outbox/outbox/service.js +271 -0
- package/dist/esm/outbox/outbox/service.js.map +1 -0
- package/dist/esm/outbox/postgres/index.js +41 -0
- package/dist/esm/outbox/postgres/index.js.map +1 -0
- package/dist/esm/outbox/postgres/registries.js +155 -0
- package/dist/esm/outbox/postgres/registries.js.map +1 -0
- package/dist/esm/outbox/postgres/reportColumns.js +191 -0
- package/dist/esm/outbox/postgres/reportColumns.js.map +1 -0
- package/dist/esm/outbox/postgres/store/enforcement.js +158 -0
- package/dist/esm/outbox/postgres/store/enforcement.js.map +1 -0
- package/dist/esm/outbox/postgres/store/events.js +101 -0
- package/dist/esm/outbox/postgres/store/events.js.map +1 -0
- package/dist/esm/outbox/postgres/store/index.js +59 -0
- package/dist/esm/outbox/postgres/store/index.js.map +1 -0
- package/dist/esm/outbox/postgres/store/outbox.js +207 -0
- package/dist/esm/outbox/postgres/store/outbox.js.map +1 -0
- package/dist/esm/outbox/postgres/store/reports.js +231 -0
- package/dist/esm/outbox/postgres/store/reports.js.map +1 -0
- package/dist/esm/outbox/postgres/store/transaction.js +29 -0
- package/dist/esm/outbox/postgres/store/transaction.js.map +1 -0
- package/dist/esm/outbox/postgres/tables.js +270 -0
- package/dist/esm/outbox/postgres/tables.js.map +1 -0
- package/dist/esm/outbox/reconciliation.js +157 -0
- package/dist/esm/outbox/reconciliation.js.map +1 -0
- package/dist/esm/outbox/reportStatus.js +22 -0
- package/dist/esm/outbox/reportStatus.js.map +1 -0
- package/dist/esm/outbox/retention.js +33 -0
- package/dist/esm/outbox/retention.js.map +1 -0
- package/dist/esm/outbox/store/types.js +30 -0
- package/dist/esm/outbox/store/types.js.map +1 -0
- package/dist/esm/outbox/types.js +23 -0
- package/dist/esm/outbox/types.js.map +1 -0
- package/dist/esm/outbox/webhook.js +145 -0
- package/dist/esm/outbox/webhook.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/reports.js +74 -0
- package/dist/esm/reports.js.map +1 -0
- package/dist/esm/testing/fixtures.js +132 -0
- package/dist/esm/testing/fixtures.js.map +1 -0
- package/dist/esm/testing/index.js +33 -0
- package/dist/esm/testing/index.js.map +1 -0
- package/dist/esm/testing/sandbox.js +337 -0
- package/dist/esm/testing/sandbox.js.map +1 -0
- package/dist/esm/testing/webhook-simulator.js +93 -0
- package/dist/esm/testing/webhook-simulator.js.map +1 -0
- package/dist/esm/transport.js +177 -0
- package/dist/esm/transport.js.map +1 -0
- package/dist/esm/webhookEndpoints.js +139 -0
- package/dist/esm/webhookEndpoints.js.map +1 -0
- package/dist/express/index.d.ts +32 -0
- package/dist/express/index.d.ts.map +1 -0
- package/dist/express/index.js +40 -0
- package/dist/express/index.js.map +1 -0
- package/dist/express/middleware.d.ts +97 -0
- package/dist/express/middleware.d.ts.map +1 -0
- package/dist/express/middleware.js +298 -0
- package/dist/express/middleware.js.map +1 -0
- package/dist/express/store.d.ts +45 -0
- package/dist/express/store.d.ts.map +1 -0
- package/dist/express/store.js +72 -0
- package/dist/express/store.js.map +1 -0
- package/dist/express/verify.d.ts +86 -0
- package/dist/express/verify.d.ts.map +1 -0
- package/dist/express/verify.js +138 -0
- package/dist/express/verify.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/outbox/client.d.ts +35 -0
- package/dist/outbox/client.d.ts.map +1 -0
- package/dist/outbox/client.js +46 -0
- package/dist/outbox/client.js.map +1 -0
- package/dist/outbox/decision.d.ts +36 -0
- package/dist/outbox/decision.d.ts.map +1 -0
- package/dist/outbox/decision.js +159 -0
- package/dist/outbox/decision.js.map +1 -0
- package/dist/outbox/delivery.d.ts +50 -0
- package/dist/outbox/delivery.d.ts.map +1 -0
- package/dist/outbox/delivery.js +135 -0
- package/dist/outbox/delivery.js.map +1 -0
- package/dist/outbox/enforcement/executor.d.ts +26 -0
- package/dist/outbox/enforcement/executor.d.ts.map +1 -0
- package/dist/outbox/enforcement/executor.js +222 -0
- package/dist/outbox/enforcement/executor.js.map +1 -0
- package/dist/outbox/enforcement/planner.d.ts +50 -0
- package/dist/outbox/enforcement/planner.d.ts.map +1 -0
- package/dist/outbox/enforcement/planner.js +333 -0
- package/dist/outbox/enforcement/planner.js.map +1 -0
- package/dist/outbox/evidence.d.ts +114 -0
- package/dist/outbox/evidence.d.ts.map +1 -0
- package/dist/outbox/evidence.js +181 -0
- package/dist/outbox/evidence.js.map +1 -0
- package/dist/outbox/inbound.d.ts +78 -0
- package/dist/outbox/inbound.d.ts.map +1 -0
- package/dist/outbox/inbound.js +91 -0
- package/dist/outbox/inbound.js.map +1 -0
- package/dist/outbox/index.d.ts +79 -0
- package/dist/outbox/index.d.ts.map +1 -0
- package/dist/outbox/index.js +98 -0
- package/dist/outbox/index.js.map +1 -0
- package/dist/outbox/intake.d.ts +62 -0
- package/dist/outbox/intake.d.ts.map +1 -0
- package/dist/outbox/intake.js +145 -0
- package/dist/outbox/intake.js.map +1 -0
- package/dist/outbox/integration.d.ts +62 -0
- package/dist/outbox/integration.d.ts.map +1 -0
- package/dist/outbox/integration.js +119 -0
- package/dist/outbox/integration.js.map +1 -0
- package/dist/outbox/outbox/dispatcher.d.ts +26 -0
- package/dist/outbox/outbox/dispatcher.d.ts.map +1 -0
- package/dist/outbox/outbox/dispatcher.js +119 -0
- package/dist/outbox/outbox/dispatcher.js.map +1 -0
- package/dist/outbox/outbox/service.d.ts +124 -0
- package/dist/outbox/outbox/service.d.ts.map +1 -0
- package/dist/outbox/outbox/service.js +279 -0
- package/dist/outbox/outbox/service.js.map +1 -0
- package/dist/outbox/postgres/index.d.ts +44 -0
- package/dist/outbox/postgres/index.d.ts.map +1 -0
- package/dist/outbox/postgres/index.js +51 -0
- package/dist/outbox/postgres/index.js.map +1 -0
- package/dist/outbox/postgres/registries.d.ts +69 -0
- package/dist/outbox/postgres/registries.d.ts.map +1 -0
- package/dist/outbox/postgres/registries.js +159 -0
- package/dist/outbox/postgres/registries.js.map +1 -0
- package/dist/outbox/postgres/reportColumns.d.ts +154 -0
- package/dist/outbox/postgres/reportColumns.d.ts.map +1 -0
- package/dist/outbox/postgres/reportColumns.js +195 -0
- package/dist/outbox/postgres/reportColumns.js.map +1 -0
- package/dist/outbox/postgres/store/enforcement.d.ts +33 -0
- package/dist/outbox/postgres/store/enforcement.d.ts.map +1 -0
- package/dist/outbox/postgres/store/enforcement.js +161 -0
- package/dist/outbox/postgres/store/enforcement.js.map +1 -0
- package/dist/outbox/postgres/store/events.d.ts +46 -0
- package/dist/outbox/postgres/store/events.d.ts.map +1 -0
- package/dist/outbox/postgres/store/events.js +104 -0
- package/dist/outbox/postgres/store/events.js.map +1 -0
- package/dist/outbox/postgres/store/index.d.ts +27 -0
- package/dist/outbox/postgres/store/index.d.ts.map +1 -0
- package/dist/outbox/postgres/store/index.js +62 -0
- package/dist/outbox/postgres/store/index.js.map +1 -0
- package/dist/outbox/postgres/store/outbox.d.ts +8 -0
- package/dist/outbox/postgres/store/outbox.d.ts.map +1 -0
- package/dist/outbox/postgres/store/outbox.js +210 -0
- package/dist/outbox/postgres/store/outbox.js.map +1 -0
- package/dist/outbox/postgres/store/reports.d.ts +69 -0
- package/dist/outbox/postgres/store/reports.d.ts.map +1 -0
- package/dist/outbox/postgres/store/reports.js +234 -0
- package/dist/outbox/postgres/store/reports.js.map +1 -0
- package/dist/outbox/postgres/store/transaction.d.ts +49 -0
- package/dist/outbox/postgres/store/transaction.d.ts.map +1 -0
- package/dist/outbox/postgres/store/transaction.js +32 -0
- package/dist/outbox/postgres/store/transaction.js.map +1 -0
- package/dist/outbox/postgres/tables.d.ts +737 -0
- package/dist/outbox/postgres/tables.d.ts.map +1 -0
- package/dist/outbox/postgres/tables.js +273 -0
- package/dist/outbox/postgres/tables.js.map +1 -0
- package/dist/outbox/reconciliation.d.ts +49 -0
- package/dist/outbox/reconciliation.d.ts.map +1 -0
- package/dist/outbox/reconciliation.js +162 -0
- package/dist/outbox/reconciliation.js.map +1 -0
- package/dist/outbox/reportStatus.d.ts +12 -0
- package/dist/outbox/reportStatus.d.ts.map +1 -0
- package/dist/outbox/reportStatus.js +25 -0
- package/dist/outbox/reportStatus.js.map +1 -0
- package/dist/outbox/retention.d.ts +33 -0
- package/dist/outbox/retention.d.ts.map +1 -0
- package/dist/outbox/retention.js +36 -0
- package/dist/outbox/retention.js.map +1 -0
- package/dist/outbox/store/types.d.ts +466 -0
- package/dist/outbox/store/types.d.ts.map +1 -0
- package/dist/outbox/store/types.js +31 -0
- package/dist/outbox/store/types.js.map +1 -0
- package/dist/outbox/types.d.ts +660 -0
- package/dist/outbox/types.d.ts.map +1 -0
- package/dist/outbox/types.js +24 -0
- package/dist/outbox/types.js.map +1 -0
- package/dist/outbox/webhook.d.ts +15 -0
- package/dist/outbox/webhook.d.ts.map +1 -0
- package/dist/outbox/webhook.js +148 -0
- package/dist/outbox/webhook.js.map +1 -0
- package/dist/reports.d.ts +73 -0
- package/dist/reports.d.ts.map +1 -0
- package/dist/reports.js +78 -0
- package/dist/reports.js.map +1 -0
- package/dist/testing/fixtures.d.ts +62 -0
- package/dist/testing/fixtures.d.ts.map +1 -0
- package/dist/testing/fixtures.js +137 -0
- package/dist/testing/fixtures.js.map +1 -0
- package/dist/testing/index.d.ts +36 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +43 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/sandbox.d.ts +109 -0
- package/dist/testing/sandbox.d.ts.map +1 -0
- package/dist/testing/sandbox.js +342 -0
- package/dist/testing/sandbox.js.map +1 -0
- package/dist/testing/webhook-simulator.d.ts +78 -0
- package/dist/testing/webhook-simulator.d.ts.map +1 -0
- package/dist/testing/webhook-simulator.js +98 -0
- package/dist/testing/webhook-simulator.js.map +1 -0
- package/dist/transport.d.ts +59 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +181 -0
- package/dist/transport.js.map +1 -0
- package/dist/webhookEndpoints.d.ts +147 -0
- package/dist/webhookEndpoints.d.ts.map +1 -0
- package/dist/webhookEndpoints.js +143 -0
- package/dist/webhookEndpoints.js.map +1 -0
- package/package.json +127 -0
- package/src/cases.ts +119 -0
- package/src/client.ts +202 -0
- package/src/communityNotes.ts +194 -0
- package/src/credential.ts +100 -0
- package/src/defaults.ts +87 -0
- package/src/digest.ts +91 -0
- package/src/envelope.ts +432 -0
- package/src/errors.ts +165 -0
- package/src/express/index.ts +43 -0
- package/src/express/middleware.ts +387 -0
- package/src/express/store.ts +85 -0
- package/src/express/verify.ts +166 -0
- package/src/index.ts +107 -0
- package/src/outbox/client.ts +75 -0
- package/src/outbox/decision.ts +209 -0
- package/src/outbox/delivery.ts +163 -0
- package/src/outbox/enforcement/executor.ts +288 -0
- package/src/outbox/enforcement/planner.ts +377 -0
- package/src/outbox/evidence.ts +231 -0
- package/src/outbox/inbound.ts +140 -0
- package/src/outbox/index.ts +160 -0
- package/src/outbox/intake.ts +175 -0
- package/src/outbox/integration.ts +205 -0
- package/src/outbox/outbox/dispatcher.ts +131 -0
- package/src/outbox/outbox/service.ts +444 -0
- package/src/outbox/postgres/index.ts +59 -0
- package/src/outbox/postgres/registries.ts +177 -0
- package/src/outbox/postgres/reportColumns.ts +281 -0
- package/src/outbox/postgres/store/enforcement.ts +186 -0
- package/src/outbox/postgres/store/events.ts +112 -0
- package/src/outbox/postgres/store/index.ts +72 -0
- package/src/outbox/postgres/store/outbox.ts +273 -0
- package/src/outbox/postgres/store/reports.ts +349 -0
- package/src/outbox/postgres/store/transaction.ts +64 -0
- package/src/outbox/postgres/tables.ts +339 -0
- package/src/outbox/reconciliation.ts +199 -0
- package/src/outbox/reportStatus.ts +24 -0
- package/src/outbox/retention.ts +34 -0
- package/src/outbox/store/types.ts +528 -0
- package/src/outbox/types.ts +753 -0
- package/src/outbox/webhook.ts +165 -0
- package/src/reports.ts +138 -0
- package/src/testing/fixtures.ts +183 -0
- package/src/testing/index.ts +57 -0
- package/src/testing/sandbox.ts +469 -0
- package/src/testing/webhook-simulator.ts +166 -0
- package/src/transport.ts +250 -0
- package/src/webhookEndpoints.ts +256 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface an application implements.
|
|
3
|
+
*
|
|
4
|
+
* Everything in this file is a seam. Nothing in it knows what a post, a listing,
|
|
5
|
+
* a message or a property is — that knowledge belongs to the application and
|
|
6
|
+
* reaches this package through exactly four ports:
|
|
7
|
+
*
|
|
8
|
+
* 1. {@link ModerationSubjectProvider} — "given one of MY nouns and its id,
|
|
9
|
+
* describe the material".
|
|
10
|
+
* 2. {@link ModerationTaxonomy} — "what my reporters can pick, as universal
|
|
11
|
+
* allegation codes".
|
|
12
|
+
* 3. {@link ModerationEnforcementConfig} — "what I can do about a decision, and
|
|
13
|
+
* how to do and undo it".
|
|
14
|
+
* 4. {@link ModerationStore} — where its reports and this package's own rows are
|
|
15
|
+
* kept, built by the backend factory it chose and passed in whole.
|
|
16
|
+
*
|
|
17
|
+
* Everything else — the outbox, the transaction coupling, delivery, the webhook
|
|
18
|
+
* receiver, deduplication, decision application, enforcement idempotency and
|
|
19
|
+
* reversibility, reconciliation — is identical in every application and is
|
|
20
|
+
* imported, not written.
|
|
21
|
+
*/
|
|
22
|
+
import type { ContextInput, ReportSubjectInput, ResourceInput } from '../index.js';
|
|
23
|
+
import type { Decision, RecommendedAction, Severity, TaxonomyCode } from '@crowdsource.you/contracts';
|
|
24
|
+
import type { ModerationStore } from './store/types.js';
|
|
25
|
+
/**
|
|
26
|
+
* The SDK's resource descriptions, unchanged.
|
|
27
|
+
*
|
|
28
|
+
* Aliased so a provider imports its vocabulary from one place, but these ARE the
|
|
29
|
+
* SDK's types rather than a local restatement: a resource type added to the
|
|
30
|
+
* contract becomes available to every provider the moment the dependency is
|
|
31
|
+
* bumped.
|
|
32
|
+
*/
|
|
33
|
+
export type ModerationResource = ResourceInput;
|
|
34
|
+
export type ModerationContextResource = ContextInput;
|
|
35
|
+
/**
|
|
36
|
+
* One reported object, described.
|
|
37
|
+
*
|
|
38
|
+
* `content` is required because a report with no material is a question a jury
|
|
39
|
+
* cannot answer. An application that cannot produce the material for one of its
|
|
40
|
+
* nouns should not register a provider for it — a reported type with no provider
|
|
41
|
+
* is still accepted and stored, it simply never leaves.
|
|
42
|
+
*/
|
|
43
|
+
export interface ModerationSubjectSnapshot {
|
|
44
|
+
/** Identity, type and author of the reported object. */
|
|
45
|
+
readonly subject: ReportSubjectInput;
|
|
46
|
+
/** The reported material itself. A string is shorthand for plain text. */
|
|
47
|
+
readonly content: string | ModerationResource;
|
|
48
|
+
/** Media carried BY the subject. */
|
|
49
|
+
readonly attachments?: readonly ModerationResource[];
|
|
50
|
+
/**
|
|
51
|
+
* Surrounding material a jury needs to judge fairly — the parent of a reply,
|
|
52
|
+
* the listing a review is about. Context, not extra exposure.
|
|
53
|
+
*/
|
|
54
|
+
readonly context?: readonly ModerationContextResource[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Translates one of the application's nouns into universal material.
|
|
58
|
+
*
|
|
59
|
+
* `subjectType` is declared on the provider rather than returned per snapshot
|
|
60
|
+
* because it is a property of the noun: every Mention post is a `social.post`,
|
|
61
|
+
* every Mercaria product a `commerce.listing`. Keeping it here means the registry
|
|
62
|
+
* can answer "what does this application report?" without loading an object.
|
|
63
|
+
*
|
|
64
|
+
* Two rules keep the seam working, and both are load-bearing rather than
|
|
65
|
+
* stylistic:
|
|
66
|
+
*
|
|
67
|
+
* 1. **A provider returns a DESCRIPTION, never an envelope.** The SDK composes
|
|
68
|
+
* the Case Envelope — resource ids, digests, relations, principal refs, the
|
|
69
|
+
* binding proof, the policy version, the idempotency key — and the case dedup
|
|
70
|
+
* key is computed over exactly those. An application that composed its own
|
|
71
|
+
* envelope would be the reason two reporters about one object opened two
|
|
72
|
+
* cases, and "one penalty per incident" would fail in production with nothing
|
|
73
|
+
* failing in a test.
|
|
74
|
+
* 2. **A provider is pure translation with reads.** It fetches its own object and
|
|
75
|
+
* returns. It does not decide whether to deliver, what the allegation is, or
|
|
76
|
+
* what happens to the report.
|
|
77
|
+
*/
|
|
78
|
+
export interface ModerationSubjectProvider {
|
|
79
|
+
/** The application's own name for the noun, as it arrives on a report. */
|
|
80
|
+
readonly reportedType: string;
|
|
81
|
+
/**
|
|
82
|
+
* The namespaced universal subject type, or `custom.<org>.<object_type>`.
|
|
83
|
+
*
|
|
84
|
+
* ## Reporting an ACCOUNT has a tenancy consequence worth knowing first
|
|
85
|
+
*
|
|
86
|
+
* `applicationId` is read off the service credential, so a report this
|
|
87
|
+
* application submits opens a case in ITS tenant. For an object the
|
|
88
|
+
* application owns that is exactly right. For an Oxy IDENTITY it is not: the
|
|
89
|
+
* case names a principal only Oxy can act on, and when a second Oxy
|
|
90
|
+
* application reports the same person under its own credential the dedup key
|
|
91
|
+
* (`applicationId + subject external id + content hash + policy version`)
|
|
92
|
+
* differs by tenant — so one person yields two cases, two juries and two
|
|
93
|
+
* consequences, breaking "one penalty per incident" at a layer nothing inside
|
|
94
|
+
* either application can repair.
|
|
95
|
+
*
|
|
96
|
+
* That is an argument for care, not a prohibition: `identity.profile` is a
|
|
97
|
+
* legitimate subject type, and an application whose own surfaces are what a
|
|
98
|
+
* jury would judge may well register one. What it is not is a way to have
|
|
99
|
+
* somebody's Oxy account sanctioned — an application can never move a
|
|
100
|
+
* reputation figure directly, and cross-application hand-off is a design
|
|
101
|
+
* question the contract does not answer yet. Registering no provider for a
|
|
102
|
+
* reported account is a supported answer: the report is still stored, and
|
|
103
|
+
* still counted, it simply never leaves.
|
|
104
|
+
*
|
|
105
|
+
* Credit: `mercaria` surfaced this while deciding not to give `seller` a
|
|
106
|
+
* provider, and it is app-independent enough to belong here.
|
|
107
|
+
*/
|
|
108
|
+
readonly subjectType: string;
|
|
109
|
+
/**
|
|
110
|
+
* Describes the object, or returns `null` when it no longer exists.
|
|
111
|
+
*
|
|
112
|
+
* `null` is not a failure. Content deleted between the report and its delivery
|
|
113
|
+
* is ordinary, and the caller decides what that means — a provider that threw
|
|
114
|
+
* would make deletion look like an outage and be retried for days.
|
|
115
|
+
*/
|
|
116
|
+
snapshot(reportedId: string): Promise<ModerationSubjectSnapshot | null>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* What a reporter picked, translated into what is being ALLEGED.
|
|
120
|
+
*
|
|
121
|
+
* Versioned because a decision records the policy version it was decided under
|
|
122
|
+
* and this mapping is upstream of that: change what `spam` means and two reports
|
|
123
|
+
* filed a month apart are no longer the same allegation. The version is stamped
|
|
124
|
+
* into the report metadata so a case can be read back against the mapping that
|
|
125
|
+
* produced it. Bump it in the same change that alters a row.
|
|
126
|
+
*/
|
|
127
|
+
export interface ModerationTaxonomy {
|
|
128
|
+
readonly version: string;
|
|
129
|
+
/**
|
|
130
|
+
* Extra metadata to stamp on every report this application sends.
|
|
131
|
+
*
|
|
132
|
+
* **Opt-in, and merged UNDER this package's own keys.** An application that
|
|
133
|
+
* sets nothing emits a byte-identical envelope to one that could not set it,
|
|
134
|
+
* which is the property that makes adding this safe: the SDK derives the case
|
|
135
|
+
* envelope from the `ReportInput`, ingress fingerprints that envelope to
|
|
136
|
+
* detect "same external id, different body", and a metadata change is
|
|
137
|
+
* therefore an envelope change. A default that altered the envelope for every
|
|
138
|
+
* adopter at once would turn each of their in-flight retries into a permanent
|
|
139
|
+
* 409, days later, as reports silently stuck in a queue.
|
|
140
|
+
*
|
|
141
|
+
* `taxonomyVersion` and `categories` are written AFTER this, so an entry using
|
|
142
|
+
* either name is ignored rather than shadowing the two keys a case is read
|
|
143
|
+
* back against.
|
|
144
|
+
*
|
|
145
|
+
* The motivating case, because it is the shape worth recognising rather than
|
|
146
|
+
* the specific field: an application that KNOWS a jury is not being given
|
|
147
|
+
* material it can see exists — Syra's agent avatars are bare strings with no
|
|
148
|
+
* digest recorded anywhere, so they cannot be attached — can say so
|
|
149
|
+
* (`evidenceAttachmentsSupported: false`), and a jury then answers
|
|
150
|
+
* `insufficient_context` for the right reason instead of guessing. Losing that
|
|
151
|
+
* degrades decision quality with nothing failing anywhere.
|
|
152
|
+
*
|
|
153
|
+
* Static, not per-report: it is stamped identically on every report, so it
|
|
154
|
+
* cannot vary between two deliveries of the same one.
|
|
155
|
+
*
|
|
156
|
+
* Credit: `syra`, whose adoption found it missing.
|
|
157
|
+
*/
|
|
158
|
+
readonly metadata?: Readonly<Record<string, string | number | boolean>>;
|
|
159
|
+
/**
|
|
160
|
+
* The allegation codes for a report's categories.
|
|
161
|
+
*
|
|
162
|
+
* MUST be deterministic and stably ordered for a given input set. Ingress
|
|
163
|
+
* fingerprints the whole envelope to detect "same external id, different
|
|
164
|
+
* body", so a list whose order depended on how a client happened to send its
|
|
165
|
+
* categories turns a legitimate outbox retry into a permanent 409 — days
|
|
166
|
+
* later, as a report silently stuck in a queue. Sort the result.
|
|
167
|
+
*
|
|
168
|
+
* MUST NOT return an empty array: a report with no allegation is not a report.
|
|
169
|
+
* Map anything unrecognised to `other.unclassifiable`.
|
|
170
|
+
*/
|
|
171
|
+
allegationsFor(categories: readonly string[]): readonly TaxonomyCode[];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* How much of a plan a deployment is allowed to carry out.
|
|
175
|
+
*
|
|
176
|
+
* `observe` runs everything except the effect — the plan, the idempotency claim
|
|
177
|
+
* and the audit row are identical to production, so what the mode proves is
|
|
178
|
+
* exactly what will happen when it is switched off.
|
|
179
|
+
*/
|
|
180
|
+
export type ModerationEnforcementMode = 'observe' | 'manual' | 'automatic';
|
|
181
|
+
/** One thing the application decided to do, and why. */
|
|
182
|
+
export interface PlannedEnforcementAction<TAction extends string> {
|
|
183
|
+
readonly action: TAction;
|
|
184
|
+
/** Why, in words an operator reads. Never reported material. */
|
|
185
|
+
readonly reason: string;
|
|
186
|
+
/** The recommendation this came from, when it came from one. */
|
|
187
|
+
readonly recommendedAction?: RecommendedAction;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* What an effect changed, so a later revision can put it back.
|
|
191
|
+
*
|
|
192
|
+
* Opaque to this package and owned by the application: it is written on the
|
|
193
|
+
* enforcement row when an action is applied and handed back to
|
|
194
|
+
* {@link ModerationEnforcementConfig.apply} when a superseding revision reverses
|
|
195
|
+
* it. Keep it small, flat and JSON-serialisable — it is stored in Mongo, and it
|
|
196
|
+
* must never contain reported material.
|
|
197
|
+
*/
|
|
198
|
+
export type EnforcementPreviousState = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
199
|
+
/** The object an enforcement action is about, in the application's own terms. */
|
|
200
|
+
export interface EnforcementSubject {
|
|
201
|
+
/** The application's own noun (`post`, `listing`, …). Never a resource id. */
|
|
202
|
+
readonly type: string;
|
|
203
|
+
readonly id: string;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The result of carrying out one action.
|
|
207
|
+
*
|
|
208
|
+
* `changed: false` is not a failure — the object is already gone, or there was
|
|
209
|
+
* no restriction to undo. It is recorded with its reason, which is how "we
|
|
210
|
+
* checked and there was nothing to do" stays distinguishable from "we never
|
|
211
|
+
* looked". Throw only for a real failure; the claim is then released so a retry
|
|
212
|
+
* can try again.
|
|
213
|
+
*/
|
|
214
|
+
export type EnforcementEffect<TAction extends string = string> = {
|
|
215
|
+
readonly changed: true;
|
|
216
|
+
readonly previousState?: EnforcementPreviousState;
|
|
217
|
+
} | {
|
|
218
|
+
readonly changed: false;
|
|
219
|
+
readonly reason: string;
|
|
220
|
+
/**
|
|
221
|
+
* What this action actually amounted to, when that is not the action that
|
|
222
|
+
* was planned.
|
|
223
|
+
*
|
|
224
|
+
* The plan is computed before `apply` runs and is deliberately
|
|
225
|
+
* subject-blind, so it can name an action that cannot apply to THIS
|
|
226
|
+
* object. An application whose restore only exists for some of its subject
|
|
227
|
+
* types is the clearest case: a decision clearing a customer plans
|
|
228
|
+
* `reinstate_courier`, because the tables cannot know the subject — and a
|
|
229
|
+
* report then reads "decided: reinstate_courier" about somebody who was
|
|
230
|
+
* never suspended.
|
|
231
|
+
*
|
|
232
|
+
* Setting `recordedAs` corrects the label at the only point that knows:
|
|
233
|
+
* `apply` is the sole place aware that this object has no such lever. The
|
|
234
|
+
* enforcement row keeps the PLANNED action, because that is the
|
|
235
|
+
* idempotency key and what was actually decided, and additionally records
|
|
236
|
+
* this; the report's `enforcedAction` uses this.
|
|
237
|
+
*
|
|
238
|
+
* Only meaningful when nothing changed — an effect that happened is the
|
|
239
|
+
* action that was planned. Must be one of `actions`.
|
|
240
|
+
*
|
|
241
|
+
* Credit: `noted-moovo`, from Moovo's courier/customer asymmetry, where
|
|
242
|
+
* two of three subject types have no suspendable state.
|
|
243
|
+
*/
|
|
244
|
+
readonly recordedAs?: TAction;
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* The application's half of enforcement: its actions, its mapping, its effects.
|
|
248
|
+
*
|
|
249
|
+
* The idempotency claim, the mode gate, the audit row, the reversal lookup and
|
|
250
|
+
* the release-on-failure are this package's and are not configurable — they are
|
|
251
|
+
* the invariants, not the policy.
|
|
252
|
+
*/
|
|
253
|
+
export interface ModerationEnforcementConfig<TAction extends string> {
|
|
254
|
+
/** Every action this application can plan. Used for the stored enum. */
|
|
255
|
+
readonly actions: readonly TAction[];
|
|
256
|
+
/**
|
|
257
|
+
* The action meaning "deliberately nothing".
|
|
258
|
+
*
|
|
259
|
+
* Never survives beside another action, and is what an otherwise-empty plan
|
|
260
|
+
* becomes: a row saying "we decided to do nothing, and why" is evidence, and
|
|
261
|
+
* an absent row is a question.
|
|
262
|
+
*/
|
|
263
|
+
readonly noneAction: TAction;
|
|
264
|
+
/**
|
|
265
|
+
* The action meaning "a human looks at this".
|
|
266
|
+
*
|
|
267
|
+
* Always survives a collapse, and is where an unmapped recommendation, an
|
|
268
|
+
* unrecognised outcome and an unrecognised severity all go. Dropping it
|
|
269
|
+
* because something else was also done is how a `suspend_user` recommendation
|
|
270
|
+
* gets lost.
|
|
271
|
+
*/
|
|
272
|
+
readonly reviewAction: TAction;
|
|
273
|
+
/**
|
|
274
|
+
* The actions that DO the undoing — `['restore', 'unlabel_sensitive']`, NEVER
|
|
275
|
+
* `['restrict', 'label_sensitive']` — or `null` when there is nothing to undo.
|
|
276
|
+
*
|
|
277
|
+
* **Direction first, because everything below describes the opposite one.**
|
|
278
|
+
* This field holds what the planner EMITS on `no_violation`; `reverses` is the
|
|
279
|
+
* separate map saying what each of those undoes. The rest of this comment
|
|
280
|
+
* necessarily talks about the levers a correction must REVERSE, and a reader
|
|
281
|
+
* arriving from that prose fills in the targets instead of the actors — a
|
|
282
|
+
* mistake two people made within an hour, including the author of the field.
|
|
283
|
+
*
|
|
284
|
+
* It matters more than a naming slip because an inverted value **does not
|
|
285
|
+
* fail**. It type-checks, it plans, and it applies a restriction and a label
|
|
286
|
+
* on an accepted appeal: the correction carrying out the punishment it was
|
|
287
|
+
* correcting, on the one path in the system whose whole purpose is to give
|
|
288
|
+
* something back.
|
|
289
|
+
*
|
|
290
|
+
* **Required, and `null` is a real answer.** Naming an action makes
|
|
291
|
+
* `no_violation` ALWAYS plan it, and that is load-bearing: a correction is a
|
|
292
|
+
* new revision whose outcome is `no_violation` and whose recommendation is
|
|
293
|
+
* frequently `no_action` — which means "take no NEW action", not "leave what
|
|
294
|
+
* you already did in place". Mapping that straight through plans nothing, and
|
|
295
|
+
* the object an earlier revision removed stays removed forever: the appeal
|
|
296
|
+
* succeeded, the case says the content was fine, and nothing ever puts it
|
|
297
|
+
* back. No error, no log line, no failing test.
|
|
298
|
+
*
|
|
299
|
+
* It is required rather than optional because an ABSENT key cannot be told
|
|
300
|
+
* apart from a forgotten one, and forgetting is exactly the silent bug above.
|
|
301
|
+
* `null` says an application considered it and has no restriction to lift —
|
|
302
|
+
* true of an application with no sanction primitive at all, and the compiler
|
|
303
|
+
* makes saying so a deliberate act.
|
|
304
|
+
*
|
|
305
|
+
* **A LIST when more than one action is reversible, and this is the shape most
|
|
306
|
+
* applications actually need.** An application whose levers are "hide it" and
|
|
307
|
+
* "label it" has TWO things a correction must undo, and naming only one leaves
|
|
308
|
+
* the other permanently stuck: the object is un-hidden and stays labelled
|
|
309
|
+
* forever. Every planned restore that finds nothing to undo records
|
|
310
|
+
* `changed: false` with its reason, so listing an action that did not apply
|
|
311
|
+
* costs an audit row rather than a wrong effect — which is the cheap side of
|
|
312
|
+
* the trade.
|
|
313
|
+
*
|
|
314
|
+
* Credit: `mention-finish`, who found `unlabel_sensitive` fully implemented,
|
|
315
|
+
* mode-gated, and reachable from nothing in Mention — the correction fix
|
|
316
|
+
* applied to one of two reversible actions. This package had the same gap.
|
|
317
|
+
*/
|
|
318
|
+
readonly restoreAction: TAction | readonly TAction[] | null;
|
|
319
|
+
/**
|
|
320
|
+
* What each recommendation becomes. Anything unmapped becomes
|
|
321
|
+
* {@link reviewAction}.
|
|
322
|
+
*
|
|
323
|
+
* Optional, because an empty table and an absent one mean the same thing: a
|
|
324
|
+
* recommendation this application has no action for goes to a human, recorded
|
|
325
|
+
* with the recommendation that produced it. An application with no sanction
|
|
326
|
+
* primitive omits it entirely.
|
|
327
|
+
*/
|
|
328
|
+
readonly recommendationToAction?: Partial<Record<RecommendedAction, TAction>>;
|
|
329
|
+
/**
|
|
330
|
+
* What a `violation` with NO recommendation becomes, by highest severity.
|
|
331
|
+
*
|
|
332
|
+
* Be cautious at both ends: a `low`-severity violation nobody recommended
|
|
333
|
+
* anything for is not something to remove an object over, and `critical`
|
|
334
|
+
* material is routed to a specialist team under legal protocol — neither is an
|
|
335
|
+
* automatic effect a mapping table should decide.
|
|
336
|
+
*
|
|
337
|
+
* Optional, and any severity left unmapped falls to {@link reviewAction}. An
|
|
338
|
+
* application with nothing to enforce can omit it entirely; the cautious
|
|
339
|
+
* default is the only honest one when a table says nothing.
|
|
340
|
+
*/
|
|
341
|
+
readonly severityFallback?: Partial<Readonly<Record<Severity, TAction>>>;
|
|
342
|
+
/**
|
|
343
|
+
* Actions that absorb weaker ones when both are planned.
|
|
344
|
+
*
|
|
345
|
+
* `{ restrict: ['label_sensitive', 'none', 'restore'] }` — a removed object
|
|
346
|
+
* does not also need a warning, and recording both would claim two effects
|
|
347
|
+
* where one happened.
|
|
348
|
+
*/
|
|
349
|
+
readonly absorb?: Partial<Record<TAction, readonly TAction[]>>;
|
|
350
|
+
/**
|
|
351
|
+
* Strongest first. Decides the ONE action written onto the report when a plan
|
|
352
|
+
* produced several.
|
|
353
|
+
*
|
|
354
|
+
* Optional; defaults to {@link actions}, so listing `actions` strongest-first
|
|
355
|
+
* is enough for most applications.
|
|
356
|
+
*/
|
|
357
|
+
readonly precedence?: readonly TAction[];
|
|
358
|
+
/**
|
|
359
|
+
* Actions `manual` mode still applies automatically.
|
|
360
|
+
*
|
|
361
|
+
* The reversible, low-consequence half: giving something BACK. Holding those
|
|
362
|
+
* behind a human means a wrongly-removed object stays removed while somebody
|
|
363
|
+
* reads a queue. Taking content down still waits for a person.
|
|
364
|
+
*/
|
|
365
|
+
readonly reversibleActions?: readonly TAction[];
|
|
366
|
+
/**
|
|
367
|
+
* Carry out one action, or say why there was nothing to carry out.
|
|
368
|
+
*
|
|
369
|
+
* Called at most once per `decisionId + revision + action`, and only when the
|
|
370
|
+
* mode allows it. `previousState` is handed back to a later reversal.
|
|
371
|
+
*
|
|
372
|
+
* **Optional.** An application with no sanction primitive at all — nothing to
|
|
373
|
+
* remove, restrict, label or suspend — omits it, and every planned action is
|
|
374
|
+
* recorded as `recorded` with a reason. That is a supported shape rather than
|
|
375
|
+
* an unfinished one: the plan, the idempotency claim and the audit row are
|
|
376
|
+
* still real, so "CrowdSource decided this and we have no way to carry it
|
|
377
|
+
* out" is written down instead of being lost. Omitting `apply` is NOT the same
|
|
378
|
+
* as `observe` mode — the mode is a deployment choice that can be switched
|
|
379
|
+
* off, and this is a property of the application.
|
|
380
|
+
*/
|
|
381
|
+
apply?(input: {
|
|
382
|
+
readonly action: TAction;
|
|
383
|
+
readonly subject: EnforcementSubject;
|
|
384
|
+
/**
|
|
385
|
+
* What the most recent APPLIED row for this subject recorded, when there is
|
|
386
|
+
* one. This is how a restore returns an object to what it WAS rather than to
|
|
387
|
+
* a guess — and how a correction knows not to lift a content warning that
|
|
388
|
+
* moderation never set.
|
|
389
|
+
*/
|
|
390
|
+
readonly previousState?: EnforcementPreviousState;
|
|
391
|
+
/** The action whose `previousState` was found, when one was. */
|
|
392
|
+
readonly previousAction?: TAction;
|
|
393
|
+
readonly decision: Decision;
|
|
394
|
+
}): Promise<EnforcementEffect<TAction>>;
|
|
395
|
+
/**
|
|
396
|
+
* The action — or actions — whose earlier `previousState` `apply` should be
|
|
397
|
+
* given, per action.
|
|
398
|
+
*
|
|
399
|
+
* `{ restore: 'restrict', unlabel_sensitive: 'label_sensitive' }` — a restore
|
|
400
|
+
* needs to know what the restriction replaced. Omit for an action that undoes
|
|
401
|
+
* nothing.
|
|
402
|
+
*
|
|
403
|
+
* A LIST when one action reverses several, which is the ordinary shape once an
|
|
404
|
+
* application has more than one lever:
|
|
405
|
+
* `{ restore: ['restrict', 'request_changes', 'freeze_transaction'] }`. The
|
|
406
|
+
* lookup takes the most recent APPLIED row across the whole set, so `apply` is
|
|
407
|
+
* handed whatever actually happened last rather than whatever single action
|
|
408
|
+
* was declared — and `previousAction` names the row that was found, not the
|
|
409
|
+
* list it came from.
|
|
410
|
+
*
|
|
411
|
+
* A single value is not merely awkward for such an application: it pushes it
|
|
412
|
+
* into re-querying the ledger itself, which is exactly where the
|
|
413
|
+
* `applied: true` filter lives. Re-implementing that per application is
|
|
414
|
+
* per-application chances to omit it, and omitting it means a reversal reads a
|
|
415
|
+
* row whose effect never happened.
|
|
416
|
+
*
|
|
417
|
+
* Credit: `mercaria`, whose `restore` reverses any of three depending on which
|
|
418
|
+
* one the decision actually applied.
|
|
419
|
+
*/
|
|
420
|
+
readonly reverses?: Partial<Record<TAction, TAction | readonly TAction[]>>;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Where a report stands in THIS application, independent of any verdict.
|
|
424
|
+
*
|
|
425
|
+
* Separate from whatever verdict field the application already had, because the
|
|
426
|
+
* two answer different questions: this one is "did it get out of here and come
|
|
427
|
+
* back", and it is the axis every query in this package uses.
|
|
428
|
+
*
|
|
429
|
+
* - `received` — stored, and never going anywhere: the reported type has no
|
|
430
|
+
* subject provider. Not a failure; a deliberate local-only report.
|
|
431
|
+
* - `queued` — stored with a durable delivery event, in one transaction.
|
|
432
|
+
* - `submitted` — CrowdSource has it; a case exists.
|
|
433
|
+
* - `delivery_failed` — the last delivery attempt failed. The outbox is still
|
|
434
|
+
* retrying or has dead-lettered it.
|
|
435
|
+
* - `closed` — a final or corrected decision was applied, or the material is
|
|
436
|
+
* gone and there is nothing to review.
|
|
437
|
+
*/
|
|
438
|
+
export type ModerationLocalStatus = 'received' | 'queued' | 'submitted' | 'delivery_failed' | 'closed';
|
|
439
|
+
/**
|
|
440
|
+
* The fields this package reads and writes on an application's report.
|
|
441
|
+
*
|
|
442
|
+
* The application owns the model, the collection, its own enums and any extra
|
|
443
|
+
* fields; it composes the schema from `moderationReportSchemaFields` and passes
|
|
444
|
+
* the resulting model in. Every query that carries a correctness property — the
|
|
445
|
+
* decision-revision guard above all — lives in this package rather than being
|
|
446
|
+
* re-derived seven times.
|
|
447
|
+
*/
|
|
448
|
+
export interface ModerationReportFields {
|
|
449
|
+
/**
|
|
450
|
+
* The report's own id, as a string.
|
|
451
|
+
*
|
|
452
|
+
* Declared here rather than left to whatever the backend calls its primary
|
|
453
|
+
* key, because the core needs it in exactly one shape and in several places:
|
|
454
|
+
* the delivery event's payload carries it, the deterministic event id is
|
|
455
|
+
* derived from it, and reconciliation re-derives that id from the report. A
|
|
456
|
+
* Re-deriving it from an ORM-specific record at each call site is how an
|
|
457
|
+
* application ends up disagreeing with itself about the report id.
|
|
458
|
+
*/
|
|
459
|
+
id: string;
|
|
460
|
+
reportedType: string;
|
|
461
|
+
reportedId: string;
|
|
462
|
+
/** The reporting Oxy user id. The Oxy subject IS the binding proof. */
|
|
463
|
+
reporter: string;
|
|
464
|
+
categories: string[];
|
|
465
|
+
details?: string;
|
|
466
|
+
localStatus: ModerationLocalStatus;
|
|
467
|
+
/** Why a report is not going anywhere, in words an operator can read. */
|
|
468
|
+
localStatusReason?: string;
|
|
469
|
+
crowdSourceReportId?: string;
|
|
470
|
+
crowdSourceCaseId?: string;
|
|
471
|
+
crowdSourceMerged?: boolean;
|
|
472
|
+
contentSnapshotHash?: string;
|
|
473
|
+
submittedAt?: Date;
|
|
474
|
+
lastDeliveryError?: string;
|
|
475
|
+
decisionId?: string;
|
|
476
|
+
decisionRevision?: number;
|
|
477
|
+
decisionOutcome?: string;
|
|
478
|
+
decisionStatus?: string;
|
|
479
|
+
decidedAt?: Date;
|
|
480
|
+
enforcedAction?: string;
|
|
481
|
+
enforcedAt?: Date;
|
|
482
|
+
createdAt: Date;
|
|
483
|
+
updatedAt: Date;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Extra `$set` fields to write when a decision lands on a report.
|
|
487
|
+
*
|
|
488
|
+
* The escape hatch for an application that already had a verdict field before it
|
|
489
|
+
* adopted CrowdSource and must keep it working. A new application needs none of
|
|
490
|
+
* this: `localStatus` and the `decision*` fields are the whole answer.
|
|
491
|
+
*
|
|
492
|
+
* Two status fields maintained by two call sites is how they drift, so an
|
|
493
|
+
* application that has one derives it HERE, from the decision, and nowhere else.
|
|
494
|
+
*/
|
|
495
|
+
export type ReportDecisionExtraFields = Readonly<Record<string, string | number | boolean | Date>>;
|
|
496
|
+
/**
|
|
497
|
+
* The application's logger.
|
|
498
|
+
*
|
|
499
|
+
* Structurally compatible with pino, winston and Mention's own. Nothing in this
|
|
500
|
+
* package logs reported material — the contexts are ids, counts, states and
|
|
501
|
+
* bounded error messages.
|
|
502
|
+
*/
|
|
503
|
+
export interface ModerationLogger {
|
|
504
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
505
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
506
|
+
error(message: string, context?: Record<string, unknown>): void;
|
|
507
|
+
}
|
|
508
|
+
/** Optional counters. Labels are bounded; no value is ever user-supplied text. */
|
|
509
|
+
export interface ModerationMetrics {
|
|
510
|
+
incrementCounter(name: string, value: number, labels: Record<string, string>): void;
|
|
511
|
+
}
|
|
512
|
+
/** Everything about talking to CrowdSource, and whether to at all. */
|
|
513
|
+
export interface CrowdSourceConnectionConfig {
|
|
514
|
+
/**
|
|
515
|
+
* Gates the DISPATCHER, never the durable record.
|
|
516
|
+
*
|
|
517
|
+
* Reports taken while the integration is off keep their outbox rows and
|
|
518
|
+
* deliver when it is switched on. Running the loop instead would count
|
|
519
|
+
* attempts against a deployment that has nowhere to send anything and
|
|
520
|
+
* dead-letter the backlog it was meant to preserve.
|
|
521
|
+
*/
|
|
522
|
+
readonly enabled: boolean;
|
|
523
|
+
/** `applicationId:credentialId:secret`. The only source of `applicationId`. */
|
|
524
|
+
readonly serviceKey?: string;
|
|
525
|
+
readonly baseUrl?: string;
|
|
526
|
+
readonly webhookSecret?: string;
|
|
527
|
+
/** Accepted during a secret rotation. */
|
|
528
|
+
readonly webhookPreviousSecret?: string;
|
|
529
|
+
readonly enforcementMode: ModerationEnforcementMode;
|
|
530
|
+
readonly outboxPollIntervalMs?: number;
|
|
531
|
+
readonly outboxBatchSize?: number;
|
|
532
|
+
/** How long a `submitted` report may wait before it is worth counting. */
|
|
533
|
+
readonly staleSubmittedHours?: number;
|
|
534
|
+
readonly reconciliationIntervalMs?: number;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Everything the integration needs, in one object.
|
|
538
|
+
*
|
|
539
|
+
* `TReport` is the application's own report document type and must structurally
|
|
540
|
+
* satisfy {@link ModerationReportFields}; `TAction` is the union of its
|
|
541
|
+
* enforcement actions; `TTx` is whatever its backend calls a transaction, and is
|
|
542
|
+
* inferred from `store` rather than written by anyone.
|
|
543
|
+
*/
|
|
544
|
+
export interface ModerationIntegrationConfig<TReport extends ModerationReportFields, TAction extends string, TTx> {
|
|
545
|
+
/**
|
|
546
|
+
* Where everything this package writes goes.
|
|
547
|
+
*
|
|
548
|
+
* Built by `postgresModerationStore` from
|
|
549
|
+
* `@crowdsource.you/core/outbox/postgres` and passed in whole rather than assembled
|
|
550
|
+
* here. A store built over two connections would type-check and quietly put a
|
|
551
|
+
* report and its outbox row in different transactions.
|
|
552
|
+
*/
|
|
553
|
+
readonly store: ModerationStore<TReport, TTx>;
|
|
554
|
+
readonly crowdSource: CrowdSourceConnectionConfig;
|
|
555
|
+
/**
|
|
556
|
+
* Every noun this application can send for review.
|
|
557
|
+
*
|
|
558
|
+
* This list decides DELIVERY and nothing else. A reported type WITHOUT a
|
|
559
|
+
* provider is still accepted and still stored — it simply never leaves. Making
|
|
560
|
+
* it an admission gate breaks an application's existing report surfaces on the
|
|
561
|
+
* day it adopts CrowdSource, and incremental adoption one subject type at a
|
|
562
|
+
* time is the property that makes this package adoptable at all.
|
|
563
|
+
*/
|
|
564
|
+
readonly subjects: readonly ModerationSubjectProvider[];
|
|
565
|
+
readonly taxonomy: ModerationTaxonomy;
|
|
566
|
+
readonly enforcement: ModerationEnforcementConfig<TAction>;
|
|
567
|
+
readonly logger: ModerationLogger;
|
|
568
|
+
readonly metrics?: ModerationMetrics;
|
|
569
|
+
/** See {@link ReportDecisionExtraFields}. Omit unless you have a legacy field. */
|
|
570
|
+
readonly reportDecisionExtraFields?: (decision: Decision) => ReportDecisionExtraFields;
|
|
571
|
+
}
|
|
572
|
+
export type ModerationOutboxKind = 'report.submit' | 'decision.apply';
|
|
573
|
+
export type ModerationOutboxStatus = 'pending' | 'processing' | 'processed' | 'dead_letter';
|
|
574
|
+
export interface ModerationOutboxPayload {
|
|
575
|
+
/** The local report id, for `report.submit`. */
|
|
576
|
+
reportId?: string;
|
|
577
|
+
/** The inbound webhook event id, for `decision.apply`. */
|
|
578
|
+
eventId?: string;
|
|
579
|
+
caseId?: string;
|
|
580
|
+
/**
|
|
581
|
+
* The decision exactly as CrowdSource published it.
|
|
582
|
+
*
|
|
583
|
+
* Stored whole and opaque rather than projected into columns: the decision
|
|
584
|
+
* document is deliberately loose, and a projection would silently drop
|
|
585
|
+
* whatever a newer CrowdSource added. Validated against the published contract
|
|
586
|
+
* when it is READ, so an event is never lost to a schema this deployment has
|
|
587
|
+
* not caught up with.
|
|
588
|
+
*/
|
|
589
|
+
decision?: unknown;
|
|
590
|
+
}
|
|
591
|
+
export interface ModerationOutboxEvent {
|
|
592
|
+
/**
|
|
593
|
+
* The deterministic event id — `moderation:report.submit:<reportId>` or
|
|
594
|
+
* `moderation:decision.apply:<eventId>`.
|
|
595
|
+
*
|
|
596
|
+
* It IS the primary key on both backends, which is what makes a repeated
|
|
597
|
+
* enqueue a no-op rather than a second delivery. Handlers key their downstream
|
|
598
|
+
* effects on it, because the contract is at-least-once.
|
|
599
|
+
*/
|
|
600
|
+
id: string;
|
|
601
|
+
kind: ModerationOutboxKind;
|
|
602
|
+
payload: ModerationOutboxPayload;
|
|
603
|
+
attempts: number;
|
|
604
|
+
availableAt: Date;
|
|
605
|
+
leaseOwner?: string;
|
|
606
|
+
leaseUntil?: Date;
|
|
607
|
+
expiresAt: Date;
|
|
608
|
+
createdAt: Date;
|
|
609
|
+
}
|
|
610
|
+
export interface CreateReportInput {
|
|
611
|
+
reporter: string;
|
|
612
|
+
reportedType: string;
|
|
613
|
+
reportedId: string;
|
|
614
|
+
categories: readonly string[];
|
|
615
|
+
details?: string;
|
|
616
|
+
/**
|
|
617
|
+
* Extra fields to store on the report document, for application columns this
|
|
618
|
+
* package knows nothing about. Never used in a query filter here.
|
|
619
|
+
*/
|
|
620
|
+
extra?: Readonly<Record<string, unknown>>;
|
|
621
|
+
}
|
|
622
|
+
export interface CreateReportResult<TReport> {
|
|
623
|
+
report: TReport;
|
|
624
|
+
/**
|
|
625
|
+
* The durable delivery event.
|
|
626
|
+
*
|
|
627
|
+
* Absent exactly when the reported type has no subject provider — the report
|
|
628
|
+
* was stored and there is nothing to deliver it, by design rather than by
|
|
629
|
+
* failure.
|
|
630
|
+
*/
|
|
631
|
+
outboxEventId?: string;
|
|
632
|
+
}
|
|
633
|
+
export interface ModerationDispatchResult {
|
|
634
|
+
processed: number;
|
|
635
|
+
failed: number;
|
|
636
|
+
deadLettered: number;
|
|
637
|
+
}
|
|
638
|
+
export interface ModerationReconciliationResult {
|
|
639
|
+
/** Reports that had no delivery event and now have one. */
|
|
640
|
+
requeued: number;
|
|
641
|
+
/** Reports whose delivery event is dead-lettered. Needs a human. */
|
|
642
|
+
deadLettered: number;
|
|
643
|
+
/** Reports submitted long ago with no decision yet. */
|
|
644
|
+
awaitingDecision: number;
|
|
645
|
+
/** Reports stored with no route to review at all. Never re-queued. */
|
|
646
|
+
localOnly: number;
|
|
647
|
+
}
|
|
648
|
+
export interface EnforcementOutcome<TAction extends string> {
|
|
649
|
+
/** The action that was PLANNED and claimed. Never rewritten. */
|
|
650
|
+
action: TAction;
|
|
651
|
+
/** What it amounted to, when `apply` said so. See {@link EnforcementEffect}. */
|
|
652
|
+
recordedAs?: TAction;
|
|
653
|
+
/**
|
|
654
|
+
* `applied` — the effect happened. `recorded` — claimed and deliberately not
|
|
655
|
+
* carried out (observe/manual mode, or nothing to do). `duplicate` — another
|
|
656
|
+
* delivery of this same decision revision already handled it.
|
|
657
|
+
*/
|
|
658
|
+
result: 'applied' | 'recorded' | 'duplicate';
|
|
659
|
+
}
|
|
660
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/outbox/types.ts"],"names":[],"mappings":"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"}
|