@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,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The public surface an application implements.
|
|
4
|
+
*
|
|
5
|
+
* Everything in this file is a seam. Nothing in it knows what a post, a listing,
|
|
6
|
+
* a message or a property is — that knowledge belongs to the application and
|
|
7
|
+
* reaches this package through exactly four ports:
|
|
8
|
+
*
|
|
9
|
+
* 1. {@link ModerationSubjectProvider} — "given one of MY nouns and its id,
|
|
10
|
+
* describe the material".
|
|
11
|
+
* 2. {@link ModerationTaxonomy} — "what my reporters can pick, as universal
|
|
12
|
+
* allegation codes".
|
|
13
|
+
* 3. {@link ModerationEnforcementConfig} — "what I can do about a decision, and
|
|
14
|
+
* how to do and undo it".
|
|
15
|
+
* 4. {@link ModerationStore} — where its reports and this package's own rows are
|
|
16
|
+
* kept, built by the backend factory it chose and passed in whole.
|
|
17
|
+
*
|
|
18
|
+
* Everything else — the outbox, the transaction coupling, delivery, the webhook
|
|
19
|
+
* receiver, deduplication, decision application, enforcement idempotency and
|
|
20
|
+
* reversibility, reconciliation — is identical in every application and is
|
|
21
|
+
* imported, not written.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/outbox/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { type ProcessedEventStore } from '../express/index.js';
|
|
3
|
+
import type { InboundService } from './inbound.js';
|
|
4
|
+
import type { ModerationLogger, ModerationMetrics } from './types.js';
|
|
5
|
+
export declare function createWebhookRouter(input: {
|
|
6
|
+
inbound: InboundService;
|
|
7
|
+
store: ProcessedEventStore;
|
|
8
|
+
secret?: string;
|
|
9
|
+
previousSecret?: string;
|
|
10
|
+
logger: ModerationLogger;
|
|
11
|
+
metrics?: ModerationMetrics;
|
|
12
|
+
/** Defaults to `/crowdsource`. */
|
|
13
|
+
path?: string;
|
|
14
|
+
}): Router;
|
|
15
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/outbox/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAuB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiDtE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAuGT"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWebhookRouter = createWebhookRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const index_js_1 = require("../express/index.js");
|
|
6
|
+
/**
|
|
7
|
+
* `POST /crowdsource` — where decisions come back.
|
|
8
|
+
*
|
|
9
|
+
* ## The mount is part of the correctness
|
|
10
|
+
*
|
|
11
|
+
* This router MUST be mounted BEFORE `express.json()`. The signature covers
|
|
12
|
+
* `timestamp + "." + rawBody` — the bytes that arrived — and once a JSON parser
|
|
13
|
+
* has run, those bytes are gone. `@crowdsource.you/core/express` looks for a raw
|
|
14
|
+
* Buffer; handed a parsed `req.body` it refuses and raises a configuration error
|
|
15
|
+
* rather than verifying a signature over a re-serialisation. That refusal is the
|
|
16
|
+
* correct behaviour and it is also why the mount order cannot be got wrong
|
|
17
|
+
* silently.
|
|
18
|
+
*
|
|
19
|
+
* The property to TEST is not the mount order — that only proves the order. It
|
|
20
|
+
* is that no parser ran: assert `typeof req.body === 'undefined'` from inside
|
|
21
|
+
* the route.
|
|
22
|
+
*
|
|
23
|
+
* ## What this handler does and does not do
|
|
24
|
+
*
|
|
25
|
+
* It records and returns. A receiver should answer 2xx quickly and queue the
|
|
26
|
+
* processing, and the reason is not latency: applying a decision means reading
|
|
27
|
+
* objects, planning enforcement and writing several collections, and a receiver
|
|
28
|
+
* that did all that inline would time out under a burst and be retried while the
|
|
29
|
+
* first attempt was still running. So the event and a durable `decision.apply`
|
|
30
|
+
* outbox row commit in ONE transaction, and the dispatcher does the work.
|
|
31
|
+
*
|
|
32
|
+
* Nothing here is authenticated by Oxy. The HMAC IS the authentication, and an
|
|
33
|
+
* Oxy session must never satisfy this route — it is not a user endpoint.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* One string field out of an event payload this version does not know.
|
|
37
|
+
*
|
|
38
|
+
* A webhook envelope's `data` is deliberately OPAQUE in the contract: an
|
|
39
|
+
* unrecognised event's payload is whatever a newer CrowdSource decided to send,
|
|
40
|
+
* and the exported type says so — property access on it does not compile. That
|
|
41
|
+
* is the contract being honest rather than an obstacle, so this reads the key
|
|
42
|
+
* defensively instead of asserting a shape nobody has verified. Anything that is
|
|
43
|
+
* not a string is treated as absent, which is the only safe reading of a field
|
|
44
|
+
* this deployment has never seen.
|
|
45
|
+
*/
|
|
46
|
+
function stringField(source, key) {
|
|
47
|
+
if (typeof source !== 'object' || source === null)
|
|
48
|
+
return undefined;
|
|
49
|
+
const value = Reflect.get(source, key);
|
|
50
|
+
return typeof value === 'string' ? value : undefined;
|
|
51
|
+
}
|
|
52
|
+
function createWebhookRouter(input) {
|
|
53
|
+
const router = (0, express_1.Router)();
|
|
54
|
+
if (!input.secret) {
|
|
55
|
+
/**
|
|
56
|
+
* Not mounted, rather than mounted and permissive.
|
|
57
|
+
*
|
|
58
|
+
* A route that answers anything at all without a secret is a route that will
|
|
59
|
+
* one day be reasoned about as if it verified something. An unconfigured
|
|
60
|
+
* deployment 404s here, which is indistinguishable from not having the
|
|
61
|
+
* feature — which is exactly what it is.
|
|
62
|
+
*/
|
|
63
|
+
input.logger.info('[CrowdSource] webhook route not mounted: no webhook secret is configured');
|
|
64
|
+
return router;
|
|
65
|
+
}
|
|
66
|
+
router.post(input.path ?? '/crowdsource', (0, index_js_1.crowdsourceWebhooks)({
|
|
67
|
+
secret: input.secret,
|
|
68
|
+
...(input.previousSecret === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { previousSecret: input.previousSecret }),
|
|
71
|
+
// Shared across tasks: the in-process default would dedupe only the
|
|
72
|
+
// instance that happened to receive both copies of a redelivery.
|
|
73
|
+
store: input.store,
|
|
74
|
+
on: {
|
|
75
|
+
/**
|
|
76
|
+
* A decision, provisional or final. Both are queued: a provisional
|
|
77
|
+
* decision is real and is recorded; what may be ACTED on is decided by
|
|
78
|
+
* the enforcement mode, not by discarding the event here.
|
|
79
|
+
*/
|
|
80
|
+
'case.decided': async (event) => {
|
|
81
|
+
await input.inbound.recordDecisionEvent({
|
|
82
|
+
eventId: event.id,
|
|
83
|
+
type: event.type,
|
|
84
|
+
caseId: event.data.caseId,
|
|
85
|
+
decision: event.data.decision,
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* A later revision replacing an earlier one. The SAME path: the decision
|
|
90
|
+
* worker compares revisions and the enforcement executor reverses what
|
|
91
|
+
* the superseded revision did. A correction is not a special case with
|
|
92
|
+
* its own code — it is an ordinary decision that supersedes another, and
|
|
93
|
+
* giving it a separate path is how a restore ends up not being
|
|
94
|
+
* idempotent.
|
|
95
|
+
*/
|
|
96
|
+
'decision.corrected': async (event) => {
|
|
97
|
+
await input.inbound.recordDecisionEvent({
|
|
98
|
+
eventId: event.id,
|
|
99
|
+
type: event.type,
|
|
100
|
+
caseId: event.data.caseId,
|
|
101
|
+
decision: event.data.decision,
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
/**
|
|
105
|
+
* An appeal's outcome carries a decision too, and it is the current
|
|
106
|
+
* answer for the case, so it takes the same path.
|
|
107
|
+
*/
|
|
108
|
+
'appeal.decided': async (event) => {
|
|
109
|
+
await input.inbound.recordDecisionEvent({
|
|
110
|
+
eventId: event.id,
|
|
111
|
+
type: event.type,
|
|
112
|
+
caseId: event.data.caseId,
|
|
113
|
+
decision: event.data.decision,
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* Every other event type — including one this version of the contracts
|
|
119
|
+
* package has never heard of.
|
|
120
|
+
*
|
|
121
|
+
* Recorded rather than dropped. `case.created`, `case.escalated` and
|
|
122
|
+
* `case.closed` carry no decision and nothing to enforce, but "did
|
|
123
|
+
* CrowdSource tell us about this case, and when" is the first question
|
|
124
|
+
* asked when a report appears stuck, and the answer has to exist
|
|
125
|
+
* somewhere.
|
|
126
|
+
*/
|
|
127
|
+
onUnhandled: async (event) => {
|
|
128
|
+
const caseId = stringField(event.data, 'caseId');
|
|
129
|
+
await input.inbound.recordIgnoredEvent({
|
|
130
|
+
eventId: event.id,
|
|
131
|
+
type: event.type,
|
|
132
|
+
...(caseId === undefined ? {} : { caseId }),
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* A refusal reason and nothing else — never a body, a header or a
|
|
137
|
+
* signature. It is a bounded label, so it can be a metric.
|
|
138
|
+
*/
|
|
139
|
+
onRejected: (rejection) => {
|
|
140
|
+
input.metrics?.incrementCounter('crowdsource_webhook_rejected_total', 1, {
|
|
141
|
+
rejection,
|
|
142
|
+
});
|
|
143
|
+
input.logger.warn('[CrowdSource] webhook delivery refused', { rejection });
|
|
144
|
+
},
|
|
145
|
+
}));
|
|
146
|
+
return router;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/outbox/webhook.ts"],"names":[],"mappings":";;AAoDA,kDAgHC;AApKD,qCAAiC;AACjC,kDAAoF;AAIpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;;;;;;GAUG;AACH,SAAS,WAAW,CAAC,MAAe,EAAE,GAAW;IAC/C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAgB,mBAAmB,CAAC,KASnC;IACC,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB;;;;;;;WAOG;QACH,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,0EAA0E,CAC3E,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,IAAI,IAAI,cAAc,EAC5B,IAAA,8BAAmB,EAAC;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS;YACpC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC;QAC7C,oEAAoE;QACpE,iEAAiE;QACjE,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,EAAE,EAAE;YACF;;;;eAIG;YACH,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBAC9B,MAAM,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC;oBACtC,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM;oBACzB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ;iBAC9B,CAAC,CAAC;YACL,CAAC;YACD;;;;;;;eAOG;YACH,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACpC,MAAM,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC;oBACtC,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM;oBACzB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ;iBAC9B,CAAC,CAAC;YACL,CAAC;YACD;;;eAGG;YACH,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBAChC,MAAM,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC;oBACtC,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM;oBACzB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ;iBAC9B,CAAC,CAAC;YACL,CAAC;SACF;QACD;;;;;;;;;WASG;QACH,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YAC3B,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACjD,MAAM,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;gBACrC,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC;QACD;;;WAGG;QACH,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE;YACxB,KAAK,CAAC,OAAO,EAAE,gBAAgB,CAAC,oCAAoC,EAAE,CAAC,EAAE;gBACvE,SAAS;aACV,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC7E,CAAC;KACF,CAAC,CACH,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /v1/reports` and `GET /v1/reports/{id}` (§10.2, §10.4).
|
|
3
|
+
*
|
|
4
|
+
* The 202 this returns means what §7.1 says it means: the report is stored and
|
|
5
|
+
* durable rows exist for everything that happens next. It does not mean a jury
|
|
6
|
+
* exists, a decision exists, or that any downstream call succeeded — and an
|
|
7
|
+
* application that treats it as "moderation is done" has misread the contract.
|
|
8
|
+
* The decision comes back later, over a webhook.
|
|
9
|
+
*/
|
|
10
|
+
import { type CreateReportResponse, type ReportStatus } from '@crowdsource.you/contracts';
|
|
11
|
+
import { type ReportInput } from './envelope.js';
|
|
12
|
+
import type { Transport } from './transport.js';
|
|
13
|
+
/**
|
|
14
|
+
* `GET /v1/reports/{id}` (§10.2 "receipt, caseId and limited status").
|
|
15
|
+
*
|
|
16
|
+
* Declared here because `@crowdsource.you/contracts` publishes the DOCUMENTS
|
|
17
|
+
* that travel over the API and not every HTTP response shape. The receipt and
|
|
18
|
+
* the case view below both belong in contracts; until they move, these are the
|
|
19
|
+
* types integrators compile against and the loose parse is what keeps a newer
|
|
20
|
+
* server from breaking an older client (§10.11).
|
|
21
|
+
*/
|
|
22
|
+
export interface ReportReceipt {
|
|
23
|
+
readonly reportId: string;
|
|
24
|
+
readonly externalReportId: string;
|
|
25
|
+
readonly caseId: string;
|
|
26
|
+
/**
|
|
27
|
+
* §3.2's report states, kept open. §10.11 requires a newer server not to break
|
|
28
|
+
* an older client, and a state added to §3.2 is exactly that case: the union
|
|
29
|
+
* still autocompletes the four this version knows and still type-checks the
|
|
30
|
+
* fifth when it arrives.
|
|
31
|
+
*/
|
|
32
|
+
readonly status: ReportStatus | (string & {});
|
|
33
|
+
/** ISO-8601 UTC, as the API sends it. */
|
|
34
|
+
readonly receivedAt: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ReportRequestOptions {
|
|
37
|
+
readonly signal?: AbortSignal;
|
|
38
|
+
}
|
|
39
|
+
export declare class Reports {
|
|
40
|
+
private readonly transport;
|
|
41
|
+
/**
|
|
42
|
+
* Awaited rather than read: with an Oxy service token the client has no
|
|
43
|
+
* credential to read an application id off, so the id is resolved from
|
|
44
|
+
* CrowdSource once and this holds that answer (settled or still in flight).
|
|
45
|
+
* A service key resolves to a plain string, and awaiting one costs nothing.
|
|
46
|
+
*/
|
|
47
|
+
private readonly applicationId;
|
|
48
|
+
private readonly environment;
|
|
49
|
+
constructor(input: {
|
|
50
|
+
transport: Transport;
|
|
51
|
+
applicationId: string | Promise<string>;
|
|
52
|
+
environment: 'production' | 'sandbox';
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Delivers a report.
|
|
56
|
+
*
|
|
57
|
+
* Call this from a delivery worker draining the application's own outbox, not
|
|
58
|
+
* from the request handler that answered the user (§7.1). The user is told
|
|
59
|
+
* their report was received the moment it is stored locally; this call is what
|
|
60
|
+
* eventually makes it CrowdSource's problem, and it is allowed to fail and be
|
|
61
|
+
* retried without the user ever knowing.
|
|
62
|
+
*
|
|
63
|
+
* Re-delivering the same report is safe and returns the same `reportId`. What
|
|
64
|
+
* is NOT safe is re-delivering the same `externalReportId` with a CHANGED
|
|
65
|
+
* body: §10.5 answers 409 and this client surfaces it as
|
|
66
|
+
* `CrowdSourceApiError` with `isPayloadConflict` and `retryable: false`,
|
|
67
|
+
* because no number of retries makes two different payloads one report.
|
|
68
|
+
*/
|
|
69
|
+
create(input: ReportInput, options?: ReportRequestOptions): Promise<CreateReportResponse>;
|
|
70
|
+
/** Reads back the receipt for a report this application delivered. */
|
|
71
|
+
get(reportId: string, options?: ReportRequestOptions): Promise<ReportReceipt>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=reports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reports.d.ts","sourceRoot":"","sources":["../src/reports.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC9C,yCAAyC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAUD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;gBAE3C,KAAK,EAAE;QACjB,SAAS,EAAE,SAAS,CAAC;QACrB,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACxC,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC;KACvC;IAMD;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACV,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,oBAAoB,CAAC;IAwBhC,sEAAsE;IAChE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,aAAa,CAAC;CAgBxF"}
|
package/dist/reports.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `POST /v1/reports` and `GET /v1/reports/{id}` (§10.2, §10.4).
|
|
4
|
+
*
|
|
5
|
+
* The 202 this returns means what §7.1 says it means: the report is stored and
|
|
6
|
+
* durable rows exist for everything that happens next. It does not mean a jury
|
|
7
|
+
* exists, a decision exists, or that any downstream call succeeded — and an
|
|
8
|
+
* application that treats it as "moderation is done" has misread the contract.
|
|
9
|
+
* The decision comes back later, over a webhook.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Reports = void 0;
|
|
13
|
+
const contracts_1 = require("@crowdsource.you/contracts");
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const envelope_js_1 = require("./envelope.js");
|
|
16
|
+
const errors_js_1 = require("./errors.js");
|
|
17
|
+
const ReportReceiptSchema = zod_1.z.looseObject({
|
|
18
|
+
reportId: zod_1.z.string(),
|
|
19
|
+
externalReportId: zod_1.z.string(),
|
|
20
|
+
caseId: zod_1.z.string(),
|
|
21
|
+
status: zod_1.z.string(),
|
|
22
|
+
receivedAt: zod_1.z.string(),
|
|
23
|
+
});
|
|
24
|
+
class Reports {
|
|
25
|
+
constructor(input) {
|
|
26
|
+
this.transport = input.transport;
|
|
27
|
+
this.applicationId = input.applicationId;
|
|
28
|
+
this.environment = input.environment;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Delivers a report.
|
|
32
|
+
*
|
|
33
|
+
* Call this from a delivery worker draining the application's own outbox, not
|
|
34
|
+
* from the request handler that answered the user (§7.1). The user is told
|
|
35
|
+
* their report was received the moment it is stored locally; this call is what
|
|
36
|
+
* eventually makes it CrowdSource's problem, and it is allowed to fail and be
|
|
37
|
+
* retried without the user ever knowing.
|
|
38
|
+
*
|
|
39
|
+
* Re-delivering the same report is safe and returns the same `reportId`. What
|
|
40
|
+
* is NOT safe is re-delivering the same `externalReportId` with a CHANGED
|
|
41
|
+
* body: §10.5 answers 409 and this client surfaces it as
|
|
42
|
+
* `CrowdSourceApiError` with `isPayloadConflict` and `retryable: false`,
|
|
43
|
+
* because no number of retries makes two different payloads one report.
|
|
44
|
+
*/
|
|
45
|
+
async create(input, options = {}) {
|
|
46
|
+
const envelope = (0, envelope_js_1.composeCaseEnvelope)(input, {
|
|
47
|
+
applicationId: await this.applicationId,
|
|
48
|
+
environment: this.environment,
|
|
49
|
+
});
|
|
50
|
+
const response = await this.transport.request({
|
|
51
|
+
method: 'POST',
|
|
52
|
+
path: '/v1/reports',
|
|
53
|
+
body: { externalReportId: input.externalReportId, envelope },
|
|
54
|
+
idempotencyKey: input.idempotencyKey ?? (0, envelope_js_1.defaultIdempotencyKey)(input.externalReportId),
|
|
55
|
+
signal: options.signal,
|
|
56
|
+
});
|
|
57
|
+
const parsed = contracts_1.CreateReportResponseSchema.safeParse(response);
|
|
58
|
+
if (!parsed.success) {
|
|
59
|
+
throw new errors_js_1.CrowdSourceTransportError('CrowdSource accepted the report but answered with a body this client does not recognise.', { retryable: false, cause: parsed.error });
|
|
60
|
+
}
|
|
61
|
+
return parsed.data;
|
|
62
|
+
}
|
|
63
|
+
/** Reads back the receipt for a report this application delivered. */
|
|
64
|
+
async get(reportId, options = {}) {
|
|
65
|
+
const response = await this.transport.request({
|
|
66
|
+
method: 'GET',
|
|
67
|
+
path: `/v1/reports/${encodeURIComponent(reportId)}`,
|
|
68
|
+
signal: options.signal,
|
|
69
|
+
});
|
|
70
|
+
const parsed = ReportReceiptSchema.safeParse(response);
|
|
71
|
+
if (!parsed.success) {
|
|
72
|
+
throw new errors_js_1.CrowdSourceTransportError('CrowdSource answered with a report receipt this client does not recognise.', { retryable: false, cause: parsed.error });
|
|
73
|
+
}
|
|
74
|
+
return parsed.data;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.Reports = Reports;
|
|
78
|
+
//# sourceMappingURL=reports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reports.js","sourceRoot":"","sources":["../src/reports.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,0DAIoC;AACpC,6BAAwB;AAExB,+CAA6F;AAC7F,2CAAwD;AA2BxD,MAAM,mBAAmB,GAAG,OAAC,CAAC,WAAW,CAAC;IACxC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;IACpB,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE;IAC5B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAMH,MAAa,OAAO;IAWlB,YAAY,KAIX;QACC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IACvC,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,MAAM,CACV,KAAkB,EAClB,UAAgC,EAAE;QAElC,MAAM,QAAQ,GAAG,IAAA,iCAAmB,EAAC,KAAK,EAAE;YAC1C,aAAa,EAAE,MAAM,IAAI,CAAC,aAAa;YACvC,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAU;YACrD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,QAAQ,EAAE;YAC5D,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAA,mCAAqB,EAAC,KAAK,CAAC,gBAAgB,CAAC;YACrF,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,sCAA0B,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,qCAAyB,CACjC,0FAA0F,EAC1F,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAC1C,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,GAAG,CAAC,QAAgB,EAAE,UAAgC,EAAE;QAC5D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAU;YACrD,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,eAAe,kBAAkB,CAAC,QAAQ,CAAC,EAAE;YACnD,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,qCAAyB,CACjC,4EAA4E,EAC5E,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAC1C,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF;AAhFD,0BAgFC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixtures.
|
|
3
|
+
*
|
|
4
|
+
* Every builder below runs its result through the published contract before
|
|
5
|
+
* returning it. A fixture that does not validate is a failing test, not
|
|
6
|
+
* something to loosen — it is how an integrator finds out a contract moved, and
|
|
7
|
+
* it is the only mechanism that stops this package from teaching a shape
|
|
8
|
+
* CrowdSource will refuse.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is synthetic. No real reported material, no real evidence, no
|
|
11
|
+
* real reviewer or reporter identity ever ships in a test package: §13.5
|
|
12
|
+
* minimises what exists at all, and a fixture file is forever.
|
|
13
|
+
*/
|
|
14
|
+
import { type CaseEnvelope, type Decision, type KnownWebhookEvent, type TaxonomyCode } from '@crowdsource.you/contracts';
|
|
15
|
+
export interface CaseEnvelopeFixtureOptions {
|
|
16
|
+
readonly applicationId?: string;
|
|
17
|
+
readonly externalReportId?: string;
|
|
18
|
+
readonly externalSubjectId?: string;
|
|
19
|
+
readonly text?: string;
|
|
20
|
+
readonly allegations?: readonly TaxonomyCode[];
|
|
21
|
+
/** An `oxy_user` binding carries a proof; §11.14 admits no effect without one. */
|
|
22
|
+
readonly authorOxyUserId?: string;
|
|
23
|
+
readonly allowCommunityReview?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A minimal valid Case Envelope: one text resource, one allegation, one author.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately the SMALLEST thing the contract accepts rather than a showcase of
|
|
29
|
+
* every field. A fixture that exercises everything hides which field a test
|
|
30
|
+
* actually depends on, and every optional field it carries is one more thing a
|
|
31
|
+
* contract change can break for a reason unrelated to the test.
|
|
32
|
+
*/
|
|
33
|
+
export declare function caseEnvelopeFixture(options?: CaseEnvelopeFixtureOptions): CaseEnvelope;
|
|
34
|
+
export interface DecisionFixtureOptions {
|
|
35
|
+
readonly id?: string;
|
|
36
|
+
readonly caseId?: string;
|
|
37
|
+
readonly revision?: number;
|
|
38
|
+
readonly outcome?: Decision['outcome'];
|
|
39
|
+
readonly status?: Decision['status'];
|
|
40
|
+
readonly findingCode?: TaxonomyCode;
|
|
41
|
+
readonly supersedesDecisionId?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A published decision (Appendix B).
|
|
45
|
+
*
|
|
46
|
+
* The jury arithmetic is real, not decorative: `agreement` must equal
|
|
47
|
+
* `winningVotes / decisiveVotes` or the contract refuses the document. That
|
|
48
|
+
* check is the auditable trace of "one qualified person, one vote", so a fixture
|
|
49
|
+
* that faked it would teach a shape the service will never emit.
|
|
50
|
+
*/
|
|
51
|
+
export declare function decisionFixture(options?: DecisionFixtureOptions): Decision;
|
|
52
|
+
export interface WebhookEventFixtureOptions {
|
|
53
|
+
readonly id?: string;
|
|
54
|
+
readonly organizationId?: string;
|
|
55
|
+
readonly applicationId?: string;
|
|
56
|
+
readonly createdAt?: string;
|
|
57
|
+
readonly caseId?: string;
|
|
58
|
+
readonly decision?: Decision;
|
|
59
|
+
}
|
|
60
|
+
/** A `case.decided` delivery (§10.7) — the one event §10.7 specifies in full. */
|
|
61
|
+
export declare function caseDecidedEventFixture(options?: WebhookEventFixtureOptions): KnownWebhookEvent;
|
|
62
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/testing/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAML,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAQpC,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/C,kFAAkF;IAClF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CACzC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,YAAY,CAyC1F;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,QAAQ,CA6C9E;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,0BAA+B,GACvC,iBAAiB,CAWnB"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fixtures.
|
|
4
|
+
*
|
|
5
|
+
* Every builder below runs its result through the published contract before
|
|
6
|
+
* returning it. A fixture that does not validate is a failing test, not
|
|
7
|
+
* something to loosen — it is how an integrator finds out a contract moved, and
|
|
8
|
+
* it is the only mechanism that stops this package from teaching a shape
|
|
9
|
+
* CrowdSource will refuse.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is synthetic. No real reported material, no real evidence, no
|
|
12
|
+
* real reviewer or reporter identity ever ships in a test package: §13.5
|
|
13
|
+
* minimises what exists at all, and a fixture file is forever.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.caseEnvelopeFixture = caseEnvelopeFixture;
|
|
17
|
+
exports.decisionFixture = decisionFixture;
|
|
18
|
+
exports.caseDecidedEventFixture = caseDecidedEventFixture;
|
|
19
|
+
const contracts_1 = require("@crowdsource.you/contracts");
|
|
20
|
+
const node_crypto_1 = require("node:crypto");
|
|
21
|
+
/** Deterministic, so a fixture used twice produces the same bytes. */
|
|
22
|
+
function digestOf(value) {
|
|
23
|
+
return `sha256:${(0, node_crypto_1.createHash)('sha256').update(JSON.stringify(value)).digest('hex')}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A minimal valid Case Envelope: one text resource, one allegation, one author.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately the SMALLEST thing the contract accepts rather than a showcase of
|
|
29
|
+
* every field. A fixture that exercises everything hides which field a test
|
|
30
|
+
* actually depends on, and every optional field it carries is one more thing a
|
|
31
|
+
* contract change can break for a reason unrelated to the test.
|
|
32
|
+
*/
|
|
33
|
+
function caseEnvelopeFixture(options = {}) {
|
|
34
|
+
const text = options.text ?? 'Synthetic reported text for an integration test.';
|
|
35
|
+
const authorOxyUserId = options.authorOxyUserId ?? 'oxy_test_author';
|
|
36
|
+
return contracts_1.CaseEnvelopeSchema.parse({
|
|
37
|
+
schemaVersion: contracts_1.CASE_ENVELOPE_SCHEMA_VERSION,
|
|
38
|
+
applicationId: options.applicationId ?? 'app_test',
|
|
39
|
+
externalReportId: options.externalReportId ?? 'report_test_1',
|
|
40
|
+
source: { environment: 'sandbox', submittedAt: '2026-07-29T00:00:00.000Z' },
|
|
41
|
+
subject: {
|
|
42
|
+
externalId: options.externalSubjectId ?? 'post_test_1',
|
|
43
|
+
type: 'social.post',
|
|
44
|
+
primaryResourceId: 'res_subject',
|
|
45
|
+
},
|
|
46
|
+
principalBindings: [
|
|
47
|
+
{
|
|
48
|
+
principalRef: 'p_author',
|
|
49
|
+
type: 'oxy_user',
|
|
50
|
+
externalPrincipalId: authorOxyUserId,
|
|
51
|
+
bindingProofId: authorOxyUserId,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
resources: [
|
|
55
|
+
{
|
|
56
|
+
id: 'res_subject',
|
|
57
|
+
type: 'text',
|
|
58
|
+
role: 'subject',
|
|
59
|
+
language: 'en',
|
|
60
|
+
data: { text },
|
|
61
|
+
sha256: digestOf({ type: 'text', data: { text } }),
|
|
62
|
+
authorPrincipalRef: 'p_author',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
relations: [],
|
|
66
|
+
allegations: (options.allegations ?? ['harassment.targeted_abuse']).map((code) => ({ code })),
|
|
67
|
+
policy: { policySetId: 'crowdsource.baseline', version: '2026.07' },
|
|
68
|
+
privacy: {
|
|
69
|
+
retentionDays: 30,
|
|
70
|
+
allowCommunityReview: options.allowCommunityReview ?? true,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A published decision (Appendix B).
|
|
76
|
+
*
|
|
77
|
+
* The jury arithmetic is real, not decorative: `agreement` must equal
|
|
78
|
+
* `winningVotes / decisiveVotes` or the contract refuses the document. That
|
|
79
|
+
* check is the auditable trace of "one qualified person, one vote", so a fixture
|
|
80
|
+
* that faked it would teach a shape the service will never emit.
|
|
81
|
+
*/
|
|
82
|
+
function decisionFixture(options = {}) {
|
|
83
|
+
const outcome = options.outcome ?? 'violation';
|
|
84
|
+
const revision = options.revision ?? 1;
|
|
85
|
+
return contracts_1.DecisionSchema.parse({
|
|
86
|
+
id: options.id ?? 'dec_test_1',
|
|
87
|
+
caseId: options.caseId ?? 'case_test_1',
|
|
88
|
+
revision,
|
|
89
|
+
status: options.status ?? 'final',
|
|
90
|
+
outcome,
|
|
91
|
+
contextSufficiency: 'sufficient',
|
|
92
|
+
confidence: 1,
|
|
93
|
+
findings: outcome === 'violation'
|
|
94
|
+
? [
|
|
95
|
+
{
|
|
96
|
+
code: options.findingCode ?? 'harassment.targeted_abuse',
|
|
97
|
+
resourceIds: ['res_subject'],
|
|
98
|
+
severity: 'medium',
|
|
99
|
+
scope: 'application_local',
|
|
100
|
+
attribution: 'author',
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
: [],
|
|
104
|
+
recommendedActions: outcome === 'violation'
|
|
105
|
+
? [{ action: 'remove_or_restrict', targetResourceIds: ['res_subject'] }]
|
|
106
|
+
: [{ action: 'no_action' }],
|
|
107
|
+
jury: {
|
|
108
|
+
size: 3,
|
|
109
|
+
decisiveVotes: 3,
|
|
110
|
+
winningVotes: 3,
|
|
111
|
+
agreement: 1,
|
|
112
|
+
specialistPresent: false,
|
|
113
|
+
},
|
|
114
|
+
policyVersions: {
|
|
115
|
+
taxonomy: contracts_1.UNIVERSAL_TAXONOMY_VERSION,
|
|
116
|
+
application: '2026.07',
|
|
117
|
+
oxyConduct: '2026.07',
|
|
118
|
+
},
|
|
119
|
+
...(options.supersedesDecisionId === undefined
|
|
120
|
+
? {}
|
|
121
|
+
: { supersedesDecisionId: options.supersedesDecisionId }),
|
|
122
|
+
publishedAt: '2026-07-29T12:00:00.000Z',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** A `case.decided` delivery (§10.7) — the one event §10.7 specifies in full. */
|
|
126
|
+
function caseDecidedEventFixture(options = {}) {
|
|
127
|
+
const decision = options.decision ?? decisionFixture({ caseId: options.caseId });
|
|
128
|
+
return contracts_1.KnownWebhookEventSchema.parse({
|
|
129
|
+
id: options.id ?? 'evt_test_1',
|
|
130
|
+
type: 'case.decided',
|
|
131
|
+
createdAt: options.createdAt ?? '2026-07-29T12:00:00.000Z',
|
|
132
|
+
organizationId: options.organizationId ?? 'org_test',
|
|
133
|
+
applicationId: options.applicationId ?? 'app_test',
|
|
134
|
+
data: { caseId: options.caseId ?? decision.caseId, decision },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/testing/fixtures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@crowdsource.you/core/testing` — integrate against CrowdSource before a jury
|
|
3
|
+
* exists.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { createCrowdSourceSandbox } from '@crowdsource.you/core/testing';
|
|
7
|
+
* import { CrowdSource } from '@crowdsource.you/core';
|
|
8
|
+
*
|
|
9
|
+
* const sandbox = createCrowdSourceSandbox();
|
|
10
|
+
* const crowdsource = new CrowdSource({
|
|
11
|
+
* serviceKey: sandbox.serviceKey,
|
|
12
|
+
* baseUrl: sandbox.baseUrl,
|
|
13
|
+
* fetch: sandbox.fetch,
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const { caseId } = await crowdsource.reports.create({ ... });
|
|
17
|
+
* const decision = sandbox.decide(caseId, { outcome: 'violation' });
|
|
18
|
+
* await sandbox.deliver('http://localhost:3000/webhooks/crowdsource', sandbox.eventFor(decision));
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The report goes through the real client, the sandbox applies the real rules
|
|
22
|
+
* (tenant from the credential, idempotency, 409 on a changed body, one case per
|
|
23
|
+
* reported version), and the webhook that comes back is genuinely signed — so
|
|
24
|
+
* the receiver being tested is the receiver that will run in production.
|
|
25
|
+
*
|
|
26
|
+
* The simulator can also deliver a stale, forged or tampered event on purpose.
|
|
27
|
+
* Asserting that a receiver REFUSES those is the half of a webhook test that
|
|
28
|
+
* actually proves something.
|
|
29
|
+
*/
|
|
30
|
+
export { caseDecidedEventFixture, caseEnvelopeFixture, decisionFixture, } from './fixtures.js';
|
|
31
|
+
export type { CaseEnvelopeFixtureOptions, DecisionFixtureOptions, WebhookEventFixtureOptions, } from './fixtures.js';
|
|
32
|
+
export { CrowdSourceSandbox, createCrowdSourceSandbox } from './sandbox.js';
|
|
33
|
+
export type { CrowdSourceSandboxOptions, SandboxCase, SandboxDecisionInput, SandboxReport, } from './sandbox.js';
|
|
34
|
+
export { WebhookSimulator, signWebhookDelivery } from './webhook-simulator.js';
|
|
35
|
+
export type { SignWebhookInput, SignedWebhookDelivery, WebhookDeliveryOverrides, WebhookDeliveryResult, WebhookSimulatorOptions, } from './webhook-simulator.js';
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EACV,yBAAyB,EACzB,WAAW,EACX,oBAAoB,EACpB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC"}
|