@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,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage port: everything this package writes down, as a contract.
|
|
3
|
+
*
|
|
4
|
+
* INTERNAL, and deliberately so. An adopting application never implements any
|
|
5
|
+
* of this — it imports the PostgreSQL subpath and the implementation ships with
|
|
6
|
+
* the package. The port keeps storage mechanics out of policy code; it is not a
|
|
7
|
+
* seam an application is invited to fill:
|
|
8
|
+
* a port with this many members handed to seven applications would be seven
|
|
9
|
+
* chances to get the revision guard, the lease ownership check or the
|
|
10
|
+
* insert-if-absent wrong, each in a way nothing fails on.
|
|
11
|
+
*
|
|
12
|
+
* Which is why every member below carries the correctness property it has to
|
|
13
|
+
* preserve. The comments are not documentation of an obvious signature; they
|
|
14
|
+
* are the reason the implementation can be audited. A method whose
|
|
15
|
+
* comment says `true` means THIS call took the claim cannot be implemented as
|
|
16
|
+
* "row exists" without the difference being visible.
|
|
17
|
+
*
|
|
18
|
+
* Two rules shape the whole shape:
|
|
19
|
+
*
|
|
20
|
+
* 1. **The store applies transitions; it never decides them.** `fail` takes the
|
|
21
|
+
* status and the next attempt time already computed, `claim` takes an
|
|
22
|
+
* already-computed `leaseUntil`. Backoff, the retry ceiling, the lease-length
|
|
23
|
+
* floor and the retryable/permanent classification live in the shared half,
|
|
24
|
+
* outside the PostgreSQL query implementation.
|
|
25
|
+
* 2. **No opaque record id crosses the port.** An enforcement row is addressed
|
|
26
|
+
* by the natural triple it is keyed on, so the same three values reach the
|
|
27
|
+
* same row through its PostgreSQL composite primary key.
|
|
28
|
+
*/
|
|
29
|
+
import type { EnforcementPreviousState, ModerationEnforcementMode, ModerationLocalStatus, ModerationOutboxEvent, ModerationOutboxKind, ModerationOutboxPayload, ModerationOutboxStatus, ModerationReportFields, ReportDecisionExtraFields } from '../types.js';
|
|
30
|
+
/**
|
|
31
|
+
* The transaction a domain write and its outbox row commit inside.
|
|
32
|
+
*
|
|
33
|
+
* The core cannot know what a transaction IS — a Mongo `ClientSession` and a
|
|
34
|
+
* drizzle transaction handle have nothing in common but the fact that carrying
|
|
35
|
+
* one is what makes two writes atomic — so `TTx` is opaque here and is only
|
|
36
|
+
* ever passed back to the store that produced it.
|
|
37
|
+
*
|
|
38
|
+
* What the runner owns is the guarantee, and it is the same on both backends:
|
|
39
|
+
* two writes outside one transaction give two silent failure modes. A report
|
|
40
|
+
* with no delivery event (nothing will ever send it, and nobody finds out until
|
|
41
|
+
* somebody asks why a case never opened) or a delivery event with no report (a
|
|
42
|
+
* worker looking up a row that was rolled back). Neither surfaces as an error at
|
|
43
|
+
* the moment it happens, which is why this is atomic rather than carefully
|
|
44
|
+
* ordered.
|
|
45
|
+
*/
|
|
46
|
+
export interface ModerationTransactionRunner<TTx> {
|
|
47
|
+
/**
|
|
48
|
+
* Run `operation` in one transaction and return its result.
|
|
49
|
+
*
|
|
50
|
+
* A backend may retry the operation on a transient conflict, so it must be
|
|
51
|
+
* safe to run more than once — everything this package enqueues is keyed on a
|
|
52
|
+
* deterministic id for exactly that reason.
|
|
53
|
+
*/
|
|
54
|
+
run<T>(operation: (tx: TTx) => Promise<T>): Promise<T>;
|
|
55
|
+
}
|
|
56
|
+
export interface ModerationOutboxStore<TTx> {
|
|
57
|
+
/**
|
|
58
|
+
* Insert-if-absent, in the CALLER's transaction. A true no-op for a row that
|
|
59
|
+
* exists.
|
|
60
|
+
*
|
|
61
|
+
* Both halves of that sentence are load-bearing. The transaction is what makes
|
|
62
|
+
* "the report was answered 201 and then vanished" impossible, and an
|
|
63
|
+
* implementation that could write outside one is the single line that
|
|
64
|
+
* reintroduces it — so an implementation MUST refuse a `tx` that is not
|
|
65
|
+
* actually in a transaction, by throwing `ModerationOutboxTransactionError`.
|
|
66
|
+
* A required parameter alone does not give this: a bare Mongo session nobody
|
|
67
|
+
* opened a transaction on type-checks perfectly and commits the row on its
|
|
68
|
+
* own.
|
|
69
|
+
*
|
|
70
|
+
* "A true no-op" is the other half. A repeated enqueue is ORDINARY — a
|
|
71
|
+
* transaction retry, two concurrent duplicate submissions, a reconciliation
|
|
72
|
+
* sweep re-deriving an event — and the dispatcher is concurrently taking,
|
|
73
|
+
* renewing and completing leases on these same rows. An implementation that
|
|
74
|
+
* merely avoids a duplicate ROW, but still writes (a touched `updatedAt`, an
|
|
75
|
+
* `ON CONFLICT DO UPDATE`), conflicts with a live lease and aborts the
|
|
76
|
+
* enclosing transaction. Nothing may be written for an id that already exists.
|
|
77
|
+
*
|
|
78
|
+
* `now`, `availableAt` and `expiresAt` are supplied rather than computed here
|
|
79
|
+
* so that a row's timestamps come from one clock and one retention policy.
|
|
80
|
+
*
|
|
81
|
+
* **Writing a second implementation of this method:** the transaction guard is
|
|
82
|
+
* proven by `scripts/test-invariants.mjs`, which DELETES it and requires the
|
|
83
|
+
* mutated tree to still type-check — a mutation that does not compile is not
|
|
84
|
+
* evidence about a guard. Since `ModerationOutboxTransactionError` is declared
|
|
85
|
+
* in the shared half and IMPORTED by each store, deleting the throw on its own
|
|
86
|
+
* leaves an unused import and fails `noUnusedLocals`. A mutation aimed at a new
|
|
87
|
+
* store has to delete the import with it, which is also what removing the guard
|
|
88
|
+
* actually looks like; the Mongoose one is the worked example.
|
|
89
|
+
*/
|
|
90
|
+
enqueue(input: {
|
|
91
|
+
eventId: string;
|
|
92
|
+
kind: ModerationOutboxKind;
|
|
93
|
+
payload: ModerationOutboxPayload;
|
|
94
|
+
availableAt: Date;
|
|
95
|
+
expiresAt: Date;
|
|
96
|
+
now: Date;
|
|
97
|
+
}, tx: TTx): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Atomically claim one due event, oldest first. `eventId` narrows to one row.
|
|
100
|
+
*
|
|
101
|
+
* Due means either `pending` and past its `availableAt`, or `processing` with
|
|
102
|
+
* an EXPIRED lease — reclaiming the second is what stops a dead worker from
|
|
103
|
+
* stranding moderation work forever. The read and the write must be one
|
|
104
|
+
* operation: two workers evaluating "what is due" and then writing would both
|
|
105
|
+
* take the same row.
|
|
106
|
+
*
|
|
107
|
+
* Claiming increments `attempts`, which is what the retry ceiling counts, and
|
|
108
|
+
* clears the previous `lastError` so a stale message cannot be read as this
|
|
109
|
+
* attempt's.
|
|
110
|
+
*/
|
|
111
|
+
claim(input: {
|
|
112
|
+
leaseOwner: string;
|
|
113
|
+
leaseUntil: Date;
|
|
114
|
+
now: Date;
|
|
115
|
+
eventId?: string;
|
|
116
|
+
}): Promise<ModerationOutboxEvent | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Finish the event, and only under the lease THIS dispatcher owns.
|
|
119
|
+
*
|
|
120
|
+
* `false` means the lease was lost — expired, or taken by another task — and
|
|
121
|
+
* the caller must not treat the delivery as recorded. The owner and expiry
|
|
122
|
+
* checks belong in the write itself; a read-then-write leaves the window a
|
|
123
|
+
* reclaim arrives in.
|
|
124
|
+
*/
|
|
125
|
+
complete(input: {
|
|
126
|
+
eventId: string;
|
|
127
|
+
leaseOwner: string;
|
|
128
|
+
now: Date;
|
|
129
|
+
}): Promise<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Extend a lease that is still live and still owned. `false` means it is not.
|
|
132
|
+
*
|
|
133
|
+
* A renewal that could revive an EXPIRED lease would let two workers believe
|
|
134
|
+
* they own one event, so `leaseUntil` must still be in the future for this to
|
|
135
|
+
* match.
|
|
136
|
+
*/
|
|
137
|
+
renew(input: {
|
|
138
|
+
eventId: string;
|
|
139
|
+
leaseOwner: string;
|
|
140
|
+
leaseUntil: Date;
|
|
141
|
+
now: Date;
|
|
142
|
+
}): Promise<boolean>;
|
|
143
|
+
/**
|
|
144
|
+
* Release a failed claim with the transition the caller already decided.
|
|
145
|
+
*
|
|
146
|
+
* `status` is `pending` for a retry and `dead_letter` for a failure no retry
|
|
147
|
+
* can fix; `availableAt` is the computed backoff. The store writes them and
|
|
148
|
+
* checks the lease, exactly as {@link complete} does — `false` means the lease
|
|
149
|
+
* was lost and the failure was not recorded by this worker.
|
|
150
|
+
*/
|
|
151
|
+
fail(input: {
|
|
152
|
+
eventId: string;
|
|
153
|
+
leaseOwner: string;
|
|
154
|
+
status: 'pending' | 'dead_letter';
|
|
155
|
+
availableAt: Date;
|
|
156
|
+
lastError: string;
|
|
157
|
+
now: Date;
|
|
158
|
+
}): Promise<boolean>;
|
|
159
|
+
/**
|
|
160
|
+
* The status of one event, or `null` when there is no such row.
|
|
161
|
+
*
|
|
162
|
+
* Read by reconciliation, where the two answers mean different things: `null`
|
|
163
|
+
* is a report whose delivery event is genuinely missing and must be
|
|
164
|
+
* re-derived, and `dead_letter` is one that must NOT be — it needs a human,
|
|
165
|
+
* and re-queueing it would spin.
|
|
166
|
+
*/
|
|
167
|
+
statusOf(eventId: string): Promise<ModerationOutboxStatus | null>;
|
|
168
|
+
}
|
|
169
|
+
export interface ModerationEventStore<TTx> {
|
|
170
|
+
/**
|
|
171
|
+
* `true` when THIS call took the claim. Never throws for a duplicate.
|
|
172
|
+
*
|
|
173
|
+
* The insert IS the claim, so a duplicate id is not an error condition to work
|
|
174
|
+
* around — it is the answer "somebody else has this event". Anything that is
|
|
175
|
+
* not a duplicate — a lost connection, a failover — MUST be rethrown: it is
|
|
176
|
+
* not "already processed", and answering `false` would make the receiver
|
|
177
|
+
* answer 2xx and retire a decision nobody ever handled.
|
|
178
|
+
*/
|
|
179
|
+
claim(input: {
|
|
180
|
+
eventId: string;
|
|
181
|
+
receivedAt: Date;
|
|
182
|
+
expiresAt: Date;
|
|
183
|
+
}): Promise<boolean>;
|
|
184
|
+
/**
|
|
185
|
+
* Give the claim back so a redelivery can be processed.
|
|
186
|
+
*
|
|
187
|
+
* Called when the handler threw. Keeping the claim would make a transient
|
|
188
|
+
* failure permanent and lose a decision silently.
|
|
189
|
+
*/
|
|
190
|
+
release(eventId: string): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Record a decision-bearing event as queued, in the caller's transaction.
|
|
193
|
+
*
|
|
194
|
+
* The transaction is what makes the dedupe safe: completing this row and
|
|
195
|
+
* queueing the work separately would let a crash between them leave an event
|
|
196
|
+
* that is permanently deduplicated with no work queued.
|
|
197
|
+
*/
|
|
198
|
+
markQueued(input: {
|
|
199
|
+
eventId: string;
|
|
200
|
+
type: string;
|
|
201
|
+
caseId: string;
|
|
202
|
+
payload: unknown;
|
|
203
|
+
now: Date;
|
|
204
|
+
}, tx: TTx): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Record an event there is nothing to do about.
|
|
207
|
+
*
|
|
208
|
+
* No outbox row, because no work — but the row is kept, because "did
|
|
209
|
+
* CrowdSource tell us about this case, and when" is the first question asked
|
|
210
|
+
* when a report looks stuck.
|
|
211
|
+
*/
|
|
212
|
+
markIgnored(input: {
|
|
213
|
+
eventId: string;
|
|
214
|
+
type: string;
|
|
215
|
+
caseId?: string;
|
|
216
|
+
now: Date;
|
|
217
|
+
}): Promise<void>;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The idempotency key of one enforcement row, and the only way to address one.
|
|
221
|
+
*
|
|
222
|
+
* `decisionId + revision + action`, which both backends can enforce as a
|
|
223
|
+
* uniqueness constraint. `revision` is in the key deliberately: a correction is
|
|
224
|
+
* a NEW revision, so the restore it asks for is a different action from the
|
|
225
|
+
* removal that came before and must be allowed to happen — while still being
|
|
226
|
+
* impossible to apply twice itself.
|
|
227
|
+
*/
|
|
228
|
+
export interface ModerationEnforcementKey {
|
|
229
|
+
readonly decisionId: string;
|
|
230
|
+
readonly decisionRevision: number;
|
|
231
|
+
readonly action: string;
|
|
232
|
+
}
|
|
233
|
+
/** The whole row, as it is first written. `action` is the PLANNED action. */
|
|
234
|
+
export interface ModerationEnforcementInsert extends ModerationEnforcementKey {
|
|
235
|
+
readonly caseId: string;
|
|
236
|
+
/** The application's own noun. Never a CrowdSource resource id. */
|
|
237
|
+
readonly subjectType: string;
|
|
238
|
+
readonly subjectId: string;
|
|
239
|
+
readonly outcome: string;
|
|
240
|
+
readonly recommendedAction?: string;
|
|
241
|
+
/** Why this action, in words an operator can read. Never reported material. */
|
|
242
|
+
readonly reason: string;
|
|
243
|
+
readonly mode: ModerationEnforcementMode;
|
|
244
|
+
readonly now: Date;
|
|
245
|
+
}
|
|
246
|
+
export interface ModerationEnforcementStore {
|
|
247
|
+
/**
|
|
248
|
+
* Take the claim for this key. `false` means another delivery already holds
|
|
249
|
+
* it. Not an error.
|
|
250
|
+
*
|
|
251
|
+
* The insert IS the check. Reading "have I done this?" before writing leaves
|
|
252
|
+
* the window between them, which is precisely the window a redelivery arrives
|
|
253
|
+
* in — and the consequence of losing that race is an object removed twice, or
|
|
254
|
+
* an appeal restored twice.
|
|
255
|
+
*/
|
|
256
|
+
claim(row: ModerationEnforcementInsert): Promise<boolean>;
|
|
257
|
+
/**
|
|
258
|
+
* Record that the claimed action was deliberately NOT carried out, and why.
|
|
259
|
+
*
|
|
260
|
+
* `recordedAs` corrects the label when the effect says the planned action did
|
|
261
|
+
* not amount to what was planned; `action` itself never changes, because it is
|
|
262
|
+
* half the idempotency key and it is what was decided.
|
|
263
|
+
*/
|
|
264
|
+
markSkipped(key: ModerationEnforcementKey, input: {
|
|
265
|
+
skippedReason: string;
|
|
266
|
+
recordedAs?: string;
|
|
267
|
+
now: Date;
|
|
268
|
+
}): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* Record that the effect actually landed.
|
|
271
|
+
*
|
|
272
|
+
* `previousState` is the application's own opaque record of what the effect
|
|
273
|
+
* replaced, and it is what makes reversibility real rather than aspirational —
|
|
274
|
+
* a later restore returns the object to what it WAS rather than to a guess.
|
|
275
|
+
*/
|
|
276
|
+
markApplied(key: ModerationEnforcementKey, input: {
|
|
277
|
+
appliedAt: Date;
|
|
278
|
+
previousState?: EnforcementPreviousState;
|
|
279
|
+
now: Date;
|
|
280
|
+
}): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* Delete a claim whose effect threw, so a retry can take it again.
|
|
283
|
+
*
|
|
284
|
+
* Without this a transient failure in `apply` would permanently consume the
|
|
285
|
+
* one chance this decision revision had to act.
|
|
286
|
+
*/
|
|
287
|
+
releaseClaim(key: ModerationEnforcementKey): Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* The most recent APPLIED row across a declared action set, or `null`.
|
|
290
|
+
*
|
|
291
|
+
* Two properties, both proven by mutation rather than by a test that could
|
|
292
|
+
* pass either way. **APPLIED**, because a row that was recorded and never
|
|
293
|
+
* carried out describes a state change that never happened, and reversing to
|
|
294
|
+
* it puts back something nobody removed. **The whole set**, because an
|
|
295
|
+
* application whose `restore` reverses any of three levers needs whichever one
|
|
296
|
+
* actually applied last — querying only the first declared action is silent
|
|
297
|
+
* for every application that exercises its levers one at a time, and wrong
|
|
298
|
+
* exactly when two of them applied.
|
|
299
|
+
*
|
|
300
|
+
* `action` comes back as a bare `string`: the store has no knowledge of the
|
|
301
|
+
* application's action union, and the executor narrows it through the declared
|
|
302
|
+
* set exactly as it does for a planned action.
|
|
303
|
+
*/
|
|
304
|
+
latestApplied(input: {
|
|
305
|
+
subjectType: string;
|
|
306
|
+
subjectId: string;
|
|
307
|
+
actions: readonly string[];
|
|
308
|
+
}): Promise<{
|
|
309
|
+
action: string;
|
|
310
|
+
previousState?: EnforcementPreviousState;
|
|
311
|
+
} | null>;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Identity plus the two fields the decision worker needs, without loading a
|
|
315
|
+
* whole report.
|
|
316
|
+
*
|
|
317
|
+
* One subject per case — the dedup key includes the subject's external id — so
|
|
318
|
+
* the first of these names the object every report in the case is about.
|
|
319
|
+
*/
|
|
320
|
+
export interface ModerationReportRef {
|
|
321
|
+
readonly id: string;
|
|
322
|
+
readonly reportedType: string;
|
|
323
|
+
readonly reportedId: string;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* What intake writes.
|
|
327
|
+
*
|
|
328
|
+
* `localStatus` and `localStatusReason` arrive already decided, from the one
|
|
329
|
+
* fact that decides them both: whether the reported type has a subject provider.
|
|
330
|
+
* A report can never be stored as `queued` with nothing to deliver it, nor as
|
|
331
|
+
* `received` with a delivery event that will try anyway.
|
|
332
|
+
*
|
|
333
|
+
* `extra` is the adopter's own columns, stored as given and NEVER used in a
|
|
334
|
+
* filter here.
|
|
335
|
+
*/
|
|
336
|
+
export interface ModerationReportInsert {
|
|
337
|
+
readonly reportedType: string;
|
|
338
|
+
readonly reportedId: string;
|
|
339
|
+
readonly reporter: string;
|
|
340
|
+
readonly categories: readonly string[];
|
|
341
|
+
readonly details?: string;
|
|
342
|
+
readonly localStatus: ModerationLocalStatus;
|
|
343
|
+
readonly localStatusReason?: string;
|
|
344
|
+
readonly extra?: Readonly<Record<string, unknown>>;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* What a decision writes onto a report.
|
|
348
|
+
*
|
|
349
|
+
* `enforcedAction` is what the application DECIDED to do and `enforcedAt` is
|
|
350
|
+
* when an effect actually landed; they are separate because they are different
|
|
351
|
+
* claims. An action that was claimed and recorded — observe mode, or an
|
|
352
|
+
* application with no sanction primitive — leaves `enforcedAt` absent while
|
|
353
|
+
* `enforcedAction` still says what was decided.
|
|
354
|
+
*/
|
|
355
|
+
export interface ModerationReportDecisionUpdate {
|
|
356
|
+
readonly localStatus: ModerationLocalStatus;
|
|
357
|
+
readonly decisionId: string;
|
|
358
|
+
readonly decisionRevision: number;
|
|
359
|
+
readonly decisionOutcome: string;
|
|
360
|
+
readonly decisionStatus: string;
|
|
361
|
+
readonly decidedAt: Date;
|
|
362
|
+
readonly enforcedAction?: string;
|
|
363
|
+
readonly enforcedAt?: Date;
|
|
364
|
+
/** The adopter's legacy verdict field, from `reportDecisionExtraFields`. */
|
|
365
|
+
readonly extra?: ReportDecisionExtraFields;
|
|
366
|
+
}
|
|
367
|
+
export interface ModerationReportStore<TReport extends ModerationReportFields, TTx> {
|
|
368
|
+
/**
|
|
369
|
+
* The one report this reporter already filed about this object, if any.
|
|
370
|
+
*
|
|
371
|
+
* Read inside the intake transaction. Note what it does NOT promise: neither
|
|
372
|
+
* backend serializes this check against a concurrent insert, so the
|
|
373
|
+
* application's own "one report per reporter per object" unique index is what
|
|
374
|
+
* finally decides a tie. This answers the ordinary case with a readable error
|
|
375
|
+
* rather than a constraint violation.
|
|
376
|
+
*/
|
|
377
|
+
findDuplicate(input: {
|
|
378
|
+
reporter: string;
|
|
379
|
+
reportedId: string;
|
|
380
|
+
reportedType: string;
|
|
381
|
+
}, tx: TTx): Promise<TReport | null>;
|
|
382
|
+
/** Store the report, in the same transaction as its delivery event. */
|
|
383
|
+
insert(input: ModerationReportInsert, tx: TTx): Promise<TReport>;
|
|
384
|
+
/**
|
|
385
|
+
* `null` — never a throw — for an id that does not exist OR is malformed.
|
|
386
|
+
*
|
|
387
|
+
* A delivery event can outlive its report, and an id that no backend can parse
|
|
388
|
+
* is the same answer as an id nothing matches: there is nothing to deliver, so
|
|
389
|
+
* the event completes rather than retrying for days against a row that will
|
|
390
|
+
* never appear.
|
|
391
|
+
*/
|
|
392
|
+
findById(reportId: string): Promise<TReport | null>;
|
|
393
|
+
/** Every report that opened or joined this case. Ids and the subject only. */
|
|
394
|
+
findByCaseId(caseId: string): Promise<ModerationReportRef[]>;
|
|
395
|
+
/**
|
|
396
|
+
* Apply a decision, refusing a revision older than the one already stored.
|
|
397
|
+
*
|
|
398
|
+
* The guard belongs in the WHERE clause, so it is the DATABASE that refuses a
|
|
399
|
+
* stale write rather than a read-then-write in this process: deliveries
|
|
400
|
+
* overlap — CrowdSource retries for 24 hours, and a correction can arrive
|
|
401
|
+
* while the decision it supersedes is still being applied — and an older
|
|
402
|
+
* revision landing last would otherwise overwrite the current answer.
|
|
403
|
+
*
|
|
404
|
+
* A report with NO stored revision must match. `<=` rather than `<` is
|
|
405
|
+
* deliberate: a redelivery of the same revision rewrites, which is harmless
|
|
406
|
+
* and keeps a partially-applied decision converging.
|
|
407
|
+
*
|
|
408
|
+
* `true` when one row matched.
|
|
409
|
+
*/
|
|
410
|
+
applyDecision(reportId: string, update: ModerationReportDecisionUpdate, maxRevision: number): Promise<boolean>;
|
|
411
|
+
/** CrowdSource accepted the report: a case exists. Clears any prior error. */
|
|
412
|
+
markSubmitted(reportId: string, input: {
|
|
413
|
+
crowdSourceReportId: string;
|
|
414
|
+
crowdSourceCaseId: string;
|
|
415
|
+
crowdSourceMerged: boolean;
|
|
416
|
+
contentSnapshotHash: string;
|
|
417
|
+
submittedAt: Date;
|
|
418
|
+
}): Promise<void>;
|
|
419
|
+
/**
|
|
420
|
+
* The last delivery attempt failed.
|
|
421
|
+
*
|
|
422
|
+
* Written on the REPORT, not only on the outbox row: `delivery_failed` is what
|
|
423
|
+
* a reporter's receipt and the reconciliation sweep both read, and leaving the
|
|
424
|
+
* report at `queued` while the outbox quietly backed off hides the problem in
|
|
425
|
+
* a collection nobody looks at.
|
|
426
|
+
*/
|
|
427
|
+
markDeliveryFailed(reportId: string, lastDeliveryError: string): Promise<void>;
|
|
428
|
+
/** Close a report there is genuinely nothing left to do about, with a reason. */
|
|
429
|
+
close(reportId: string, localStatusReason: string): Promise<void>;
|
|
430
|
+
/**
|
|
431
|
+
* `queued` + `delivery_failed`, oldest first, bounded. Ids only.
|
|
432
|
+
*
|
|
433
|
+
* `received` is excluded and the omission is the safety property, not an
|
|
434
|
+
* oversight: those reports have no subject provider, so an event re-derived
|
|
435
|
+
* for one would fail on its first attempt and dead-letter.
|
|
436
|
+
*/
|
|
437
|
+
findPendingOldestFirst(limit: number): Promise<string[]>;
|
|
438
|
+
/** Reports submitted before this instant with no decision yet. */
|
|
439
|
+
countAwaitingDecision(submittedBefore: Date): Promise<number>;
|
|
440
|
+
/** Reports stored with no route to review at all. Counted, never re-queued. */
|
|
441
|
+
countLocalOnly(): Promise<number>;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* One object holding every write this package makes.
|
|
445
|
+
*
|
|
446
|
+
* Built by the PostgreSQL factory and passed in whole, so an application cannot
|
|
447
|
+
* assemble pieces backed by different connections, which would put the report
|
|
448
|
+
* and its outbox row in different transactions and silently undo the intake
|
|
449
|
+
* guarantee.
|
|
450
|
+
*/
|
|
451
|
+
export interface ModerationStore<TReport extends ModerationReportFields, TTx> {
|
|
452
|
+
readonly transaction: ModerationTransactionRunner<TTx>;
|
|
453
|
+
readonly outbox: ModerationOutboxStore<TTx>;
|
|
454
|
+
readonly events: ModerationEventStore<TTx>;
|
|
455
|
+
readonly enforcement: ModerationEnforcementStore;
|
|
456
|
+
readonly reports: ModerationReportStore<TReport, TTx>;
|
|
457
|
+
/**
|
|
458
|
+
* Asserts that the migrated PostgreSQL schema is present.
|
|
459
|
+
*
|
|
460
|
+
* Called once at wiring time. The unique indexes are the mechanism behind
|
|
461
|
+
* every "exactly once" claim in this package, so they must exist before the
|
|
462
|
+
* first write rather than whenever the driver gets round to it.
|
|
463
|
+
*/
|
|
464
|
+
ensureSchema(): Promise<void>;
|
|
465
|
+
}
|
|
466
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/outbox/store/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The storage port: everything this package writes down, as a contract.
|
|
4
|
+
*
|
|
5
|
+
* INTERNAL, and deliberately so. An adopting application never implements any
|
|
6
|
+
* of this — it imports the PostgreSQL subpath and the implementation ships with
|
|
7
|
+
* the package. The port keeps storage mechanics out of policy code; it is not a
|
|
8
|
+
* seam an application is invited to fill:
|
|
9
|
+
* a port with this many members handed to seven applications would be seven
|
|
10
|
+
* chances to get the revision guard, the lease ownership check or the
|
|
11
|
+
* insert-if-absent wrong, each in a way nothing fails on.
|
|
12
|
+
*
|
|
13
|
+
* Which is why every member below carries the correctness property it has to
|
|
14
|
+
* preserve. The comments are not documentation of an obvious signature; they
|
|
15
|
+
* are the reason the implementation can be audited. A method whose
|
|
16
|
+
* comment says `true` means THIS call took the claim cannot be implemented as
|
|
17
|
+
* "row exists" without the difference being visible.
|
|
18
|
+
*
|
|
19
|
+
* Two rules shape the whole shape:
|
|
20
|
+
*
|
|
21
|
+
* 1. **The store applies transitions; it never decides them.** `fail` takes the
|
|
22
|
+
* status and the next attempt time already computed, `claim` takes an
|
|
23
|
+
* already-computed `leaseUntil`. Backoff, the retry ceiling, the lease-length
|
|
24
|
+
* floor and the retryable/permanent classification live in the shared half,
|
|
25
|
+
* outside the PostgreSQL query implementation.
|
|
26
|
+
* 2. **No opaque record id crosses the port.** An enforcement row is addressed
|
|
27
|
+
* by the natural triple it is keyed on, so the same three values reach the
|
|
28
|
+
* same row through its PostgreSQL composite primary key.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/outbox/store/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG"}
|