@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.
Files changed (339) hide show
  1. package/README.md +571 -0
  2. package/dist/cases.d.ts +66 -0
  3. package/dist/cases.d.ts.map +1 -0
  4. package/dist/cases.js +79 -0
  5. package/dist/cases.js.map +1 -0
  6. package/dist/client.d.ts +90 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +125 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/communityNotes.d.ts +49 -0
  11. package/dist/communityNotes.d.ts.map +1 -0
  12. package/dist/communityNotes.js +104 -0
  13. package/dist/communityNotes.js.map +1 -0
  14. package/dist/credential.d.ts +58 -0
  15. package/dist/credential.d.ts.map +1 -0
  16. package/dist/credential.js +71 -0
  17. package/dist/credential.js.map +1 -0
  18. package/dist/defaults.d.ts +61 -0
  19. package/dist/defaults.d.ts.map +1 -0
  20. package/dist/defaults.js +82 -0
  21. package/dist/defaults.js.map +1 -0
  22. package/dist/digest.d.ts +44 -0
  23. package/dist/digest.d.ts.map +1 -0
  24. package/dist/digest.js +79 -0
  25. package/dist/digest.js.map +1 -0
  26. package/dist/envelope.d.ts +143 -0
  27. package/dist/envelope.d.ts.map +1 -0
  28. package/dist/envelope.js +258 -0
  29. package/dist/envelope.js.map +1 -0
  30. package/dist/errors.d.ts +90 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +151 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/esm/cases.js +74 -0
  35. package/dist/esm/cases.js.map +1 -0
  36. package/dist/esm/client.js +121 -0
  37. package/dist/esm/client.js.map +1 -0
  38. package/dist/esm/communityNotes.js +100 -0
  39. package/dist/esm/communityNotes.js.map +1 -0
  40. package/dist/esm/credential.js +67 -0
  41. package/dist/esm/credential.js.map +1 -0
  42. package/dist/esm/defaults.js +77 -0
  43. package/dist/esm/defaults.js.map +1 -0
  44. package/dist/esm/digest.js +74 -0
  45. package/dist/esm/digest.js.map +1 -0
  46. package/dist/esm/envelope.js +252 -0
  47. package/dist/esm/envelope.js.map +1 -0
  48. package/dist/esm/errors.js +140 -0
  49. package/dist/esm/errors.js.map +1 -0
  50. package/dist/esm/express/index.js +29 -0
  51. package/dist/esm/express/index.js.map +1 -0
  52. package/dist/esm/express/middleware.js +293 -0
  53. package/dist/esm/express/middleware.js.map +1 -0
  54. package/dist/esm/express/store.js +69 -0
  55. package/dist/esm/express/store.js.map +1 -0
  56. package/dist/esm/express/verify.js +133 -0
  57. package/dist/esm/express/verify.js.map +1 -0
  58. package/dist/esm/index.js +57 -0
  59. package/dist/esm/index.js.map +1 -0
  60. package/dist/esm/outbox/client.js +43 -0
  61. package/dist/esm/outbox/client.js.map +1 -0
  62. package/dist/esm/outbox/decision.js +153 -0
  63. package/dist/esm/outbox/decision.js.map +1 -0
  64. package/dist/esm/outbox/delivery.js +129 -0
  65. package/dist/esm/outbox/delivery.js.map +1 -0
  66. package/dist/esm/outbox/enforcement/executor.js +219 -0
  67. package/dist/esm/outbox/enforcement/executor.js.map +1 -0
  68. package/dist/esm/outbox/enforcement/planner.js +326 -0
  69. package/dist/esm/outbox/enforcement/planner.js.map +1 -0
  70. package/dist/esm/outbox/evidence.js +173 -0
  71. package/dist/esm/outbox/evidence.js.map +1 -0
  72. package/dist/esm/outbox/inbound.js +87 -0
  73. package/dist/esm/outbox/inbound.js.map +1 -0
  74. package/dist/esm/outbox/index.js +71 -0
  75. package/dist/esm/outbox/index.js.map +1 -0
  76. package/dist/esm/outbox/intake.js +140 -0
  77. package/dist/esm/outbox/intake.js.map +1 -0
  78. package/dist/esm/outbox/integration.js +116 -0
  79. package/dist/esm/outbox/integration.js.map +1 -0
  80. package/dist/esm/outbox/outbox/dispatcher.js +114 -0
  81. package/dist/esm/outbox/outbox/dispatcher.js.map +1 -0
  82. package/dist/esm/outbox/outbox/service.js +271 -0
  83. package/dist/esm/outbox/outbox/service.js.map +1 -0
  84. package/dist/esm/outbox/postgres/index.js +41 -0
  85. package/dist/esm/outbox/postgres/index.js.map +1 -0
  86. package/dist/esm/outbox/postgres/registries.js +155 -0
  87. package/dist/esm/outbox/postgres/registries.js.map +1 -0
  88. package/dist/esm/outbox/postgres/reportColumns.js +191 -0
  89. package/dist/esm/outbox/postgres/reportColumns.js.map +1 -0
  90. package/dist/esm/outbox/postgres/store/enforcement.js +158 -0
  91. package/dist/esm/outbox/postgres/store/enforcement.js.map +1 -0
  92. package/dist/esm/outbox/postgres/store/events.js +101 -0
  93. package/dist/esm/outbox/postgres/store/events.js.map +1 -0
  94. package/dist/esm/outbox/postgres/store/index.js +59 -0
  95. package/dist/esm/outbox/postgres/store/index.js.map +1 -0
  96. package/dist/esm/outbox/postgres/store/outbox.js +207 -0
  97. package/dist/esm/outbox/postgres/store/outbox.js.map +1 -0
  98. package/dist/esm/outbox/postgres/store/reports.js +231 -0
  99. package/dist/esm/outbox/postgres/store/reports.js.map +1 -0
  100. package/dist/esm/outbox/postgres/store/transaction.js +29 -0
  101. package/dist/esm/outbox/postgres/store/transaction.js.map +1 -0
  102. package/dist/esm/outbox/postgres/tables.js +270 -0
  103. package/dist/esm/outbox/postgres/tables.js.map +1 -0
  104. package/dist/esm/outbox/reconciliation.js +157 -0
  105. package/dist/esm/outbox/reconciliation.js.map +1 -0
  106. package/dist/esm/outbox/reportStatus.js +22 -0
  107. package/dist/esm/outbox/reportStatus.js.map +1 -0
  108. package/dist/esm/outbox/retention.js +33 -0
  109. package/dist/esm/outbox/retention.js.map +1 -0
  110. package/dist/esm/outbox/store/types.js +30 -0
  111. package/dist/esm/outbox/store/types.js.map +1 -0
  112. package/dist/esm/outbox/types.js +23 -0
  113. package/dist/esm/outbox/types.js.map +1 -0
  114. package/dist/esm/outbox/webhook.js +145 -0
  115. package/dist/esm/outbox/webhook.js.map +1 -0
  116. package/dist/esm/package.json +1 -0
  117. package/dist/esm/reports.js +74 -0
  118. package/dist/esm/reports.js.map +1 -0
  119. package/dist/esm/testing/fixtures.js +132 -0
  120. package/dist/esm/testing/fixtures.js.map +1 -0
  121. package/dist/esm/testing/index.js +33 -0
  122. package/dist/esm/testing/index.js.map +1 -0
  123. package/dist/esm/testing/sandbox.js +337 -0
  124. package/dist/esm/testing/sandbox.js.map +1 -0
  125. package/dist/esm/testing/webhook-simulator.js +93 -0
  126. package/dist/esm/testing/webhook-simulator.js.map +1 -0
  127. package/dist/esm/transport.js +177 -0
  128. package/dist/esm/transport.js.map +1 -0
  129. package/dist/esm/webhookEndpoints.js +139 -0
  130. package/dist/esm/webhookEndpoints.js.map +1 -0
  131. package/dist/express/index.d.ts +32 -0
  132. package/dist/express/index.d.ts.map +1 -0
  133. package/dist/express/index.js +40 -0
  134. package/dist/express/index.js.map +1 -0
  135. package/dist/express/middleware.d.ts +97 -0
  136. package/dist/express/middleware.d.ts.map +1 -0
  137. package/dist/express/middleware.js +298 -0
  138. package/dist/express/middleware.js.map +1 -0
  139. package/dist/express/store.d.ts +45 -0
  140. package/dist/express/store.d.ts.map +1 -0
  141. package/dist/express/store.js +72 -0
  142. package/dist/express/store.js.map +1 -0
  143. package/dist/express/verify.d.ts +86 -0
  144. package/dist/express/verify.d.ts.map +1 -0
  145. package/dist/express/verify.js +138 -0
  146. package/dist/express/verify.js.map +1 -0
  147. package/dist/index.d.ts +67 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +89 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/outbox/client.d.ts +35 -0
  152. package/dist/outbox/client.d.ts.map +1 -0
  153. package/dist/outbox/client.js +46 -0
  154. package/dist/outbox/client.js.map +1 -0
  155. package/dist/outbox/decision.d.ts +36 -0
  156. package/dist/outbox/decision.d.ts.map +1 -0
  157. package/dist/outbox/decision.js +159 -0
  158. package/dist/outbox/decision.js.map +1 -0
  159. package/dist/outbox/delivery.d.ts +50 -0
  160. package/dist/outbox/delivery.d.ts.map +1 -0
  161. package/dist/outbox/delivery.js +135 -0
  162. package/dist/outbox/delivery.js.map +1 -0
  163. package/dist/outbox/enforcement/executor.d.ts +26 -0
  164. package/dist/outbox/enforcement/executor.d.ts.map +1 -0
  165. package/dist/outbox/enforcement/executor.js +222 -0
  166. package/dist/outbox/enforcement/executor.js.map +1 -0
  167. package/dist/outbox/enforcement/planner.d.ts +50 -0
  168. package/dist/outbox/enforcement/planner.d.ts.map +1 -0
  169. package/dist/outbox/enforcement/planner.js +333 -0
  170. package/dist/outbox/enforcement/planner.js.map +1 -0
  171. package/dist/outbox/evidence.d.ts +114 -0
  172. package/dist/outbox/evidence.d.ts.map +1 -0
  173. package/dist/outbox/evidence.js +181 -0
  174. package/dist/outbox/evidence.js.map +1 -0
  175. package/dist/outbox/inbound.d.ts +78 -0
  176. package/dist/outbox/inbound.d.ts.map +1 -0
  177. package/dist/outbox/inbound.js +91 -0
  178. package/dist/outbox/inbound.js.map +1 -0
  179. package/dist/outbox/index.d.ts +79 -0
  180. package/dist/outbox/index.d.ts.map +1 -0
  181. package/dist/outbox/index.js +98 -0
  182. package/dist/outbox/index.js.map +1 -0
  183. package/dist/outbox/intake.d.ts +62 -0
  184. package/dist/outbox/intake.d.ts.map +1 -0
  185. package/dist/outbox/intake.js +145 -0
  186. package/dist/outbox/intake.js.map +1 -0
  187. package/dist/outbox/integration.d.ts +62 -0
  188. package/dist/outbox/integration.d.ts.map +1 -0
  189. package/dist/outbox/integration.js +119 -0
  190. package/dist/outbox/integration.js.map +1 -0
  191. package/dist/outbox/outbox/dispatcher.d.ts +26 -0
  192. package/dist/outbox/outbox/dispatcher.d.ts.map +1 -0
  193. package/dist/outbox/outbox/dispatcher.js +119 -0
  194. package/dist/outbox/outbox/dispatcher.js.map +1 -0
  195. package/dist/outbox/outbox/service.d.ts +124 -0
  196. package/dist/outbox/outbox/service.d.ts.map +1 -0
  197. package/dist/outbox/outbox/service.js +279 -0
  198. package/dist/outbox/outbox/service.js.map +1 -0
  199. package/dist/outbox/postgres/index.d.ts +44 -0
  200. package/dist/outbox/postgres/index.d.ts.map +1 -0
  201. package/dist/outbox/postgres/index.js +51 -0
  202. package/dist/outbox/postgres/index.js.map +1 -0
  203. package/dist/outbox/postgres/registries.d.ts +69 -0
  204. package/dist/outbox/postgres/registries.d.ts.map +1 -0
  205. package/dist/outbox/postgres/registries.js +159 -0
  206. package/dist/outbox/postgres/registries.js.map +1 -0
  207. package/dist/outbox/postgres/reportColumns.d.ts +154 -0
  208. package/dist/outbox/postgres/reportColumns.d.ts.map +1 -0
  209. package/dist/outbox/postgres/reportColumns.js +195 -0
  210. package/dist/outbox/postgres/reportColumns.js.map +1 -0
  211. package/dist/outbox/postgres/store/enforcement.d.ts +33 -0
  212. package/dist/outbox/postgres/store/enforcement.d.ts.map +1 -0
  213. package/dist/outbox/postgres/store/enforcement.js +161 -0
  214. package/dist/outbox/postgres/store/enforcement.js.map +1 -0
  215. package/dist/outbox/postgres/store/events.d.ts +46 -0
  216. package/dist/outbox/postgres/store/events.d.ts.map +1 -0
  217. package/dist/outbox/postgres/store/events.js +104 -0
  218. package/dist/outbox/postgres/store/events.js.map +1 -0
  219. package/dist/outbox/postgres/store/index.d.ts +27 -0
  220. package/dist/outbox/postgres/store/index.d.ts.map +1 -0
  221. package/dist/outbox/postgres/store/index.js +62 -0
  222. package/dist/outbox/postgres/store/index.js.map +1 -0
  223. package/dist/outbox/postgres/store/outbox.d.ts +8 -0
  224. package/dist/outbox/postgres/store/outbox.d.ts.map +1 -0
  225. package/dist/outbox/postgres/store/outbox.js +210 -0
  226. package/dist/outbox/postgres/store/outbox.js.map +1 -0
  227. package/dist/outbox/postgres/store/reports.d.ts +69 -0
  228. package/dist/outbox/postgres/store/reports.d.ts.map +1 -0
  229. package/dist/outbox/postgres/store/reports.js +234 -0
  230. package/dist/outbox/postgres/store/reports.js.map +1 -0
  231. package/dist/outbox/postgres/store/transaction.d.ts +49 -0
  232. package/dist/outbox/postgres/store/transaction.d.ts.map +1 -0
  233. package/dist/outbox/postgres/store/transaction.js +32 -0
  234. package/dist/outbox/postgres/store/transaction.js.map +1 -0
  235. package/dist/outbox/postgres/tables.d.ts +737 -0
  236. package/dist/outbox/postgres/tables.d.ts.map +1 -0
  237. package/dist/outbox/postgres/tables.js +273 -0
  238. package/dist/outbox/postgres/tables.js.map +1 -0
  239. package/dist/outbox/reconciliation.d.ts +49 -0
  240. package/dist/outbox/reconciliation.d.ts.map +1 -0
  241. package/dist/outbox/reconciliation.js +162 -0
  242. package/dist/outbox/reconciliation.js.map +1 -0
  243. package/dist/outbox/reportStatus.d.ts +12 -0
  244. package/dist/outbox/reportStatus.d.ts.map +1 -0
  245. package/dist/outbox/reportStatus.js +25 -0
  246. package/dist/outbox/reportStatus.js.map +1 -0
  247. package/dist/outbox/retention.d.ts +33 -0
  248. package/dist/outbox/retention.d.ts.map +1 -0
  249. package/dist/outbox/retention.js +36 -0
  250. package/dist/outbox/retention.js.map +1 -0
  251. package/dist/outbox/store/types.d.ts +466 -0
  252. package/dist/outbox/store/types.d.ts.map +1 -0
  253. package/dist/outbox/store/types.js +31 -0
  254. package/dist/outbox/store/types.js.map +1 -0
  255. package/dist/outbox/types.d.ts +660 -0
  256. package/dist/outbox/types.d.ts.map +1 -0
  257. package/dist/outbox/types.js +24 -0
  258. package/dist/outbox/types.js.map +1 -0
  259. package/dist/outbox/webhook.d.ts +15 -0
  260. package/dist/outbox/webhook.d.ts.map +1 -0
  261. package/dist/outbox/webhook.js +148 -0
  262. package/dist/outbox/webhook.js.map +1 -0
  263. package/dist/reports.d.ts +73 -0
  264. package/dist/reports.d.ts.map +1 -0
  265. package/dist/reports.js +78 -0
  266. package/dist/reports.js.map +1 -0
  267. package/dist/testing/fixtures.d.ts +62 -0
  268. package/dist/testing/fixtures.d.ts.map +1 -0
  269. package/dist/testing/fixtures.js +137 -0
  270. package/dist/testing/fixtures.js.map +1 -0
  271. package/dist/testing/index.d.ts +36 -0
  272. package/dist/testing/index.d.ts.map +1 -0
  273. package/dist/testing/index.js +43 -0
  274. package/dist/testing/index.js.map +1 -0
  275. package/dist/testing/sandbox.d.ts +109 -0
  276. package/dist/testing/sandbox.d.ts.map +1 -0
  277. package/dist/testing/sandbox.js +342 -0
  278. package/dist/testing/sandbox.js.map +1 -0
  279. package/dist/testing/webhook-simulator.d.ts +78 -0
  280. package/dist/testing/webhook-simulator.d.ts.map +1 -0
  281. package/dist/testing/webhook-simulator.js +98 -0
  282. package/dist/testing/webhook-simulator.js.map +1 -0
  283. package/dist/transport.d.ts +59 -0
  284. package/dist/transport.d.ts.map +1 -0
  285. package/dist/transport.js +181 -0
  286. package/dist/transport.js.map +1 -0
  287. package/dist/webhookEndpoints.d.ts +147 -0
  288. package/dist/webhookEndpoints.d.ts.map +1 -0
  289. package/dist/webhookEndpoints.js +143 -0
  290. package/dist/webhookEndpoints.js.map +1 -0
  291. package/package.json +127 -0
  292. package/src/cases.ts +119 -0
  293. package/src/client.ts +202 -0
  294. package/src/communityNotes.ts +194 -0
  295. package/src/credential.ts +100 -0
  296. package/src/defaults.ts +87 -0
  297. package/src/digest.ts +91 -0
  298. package/src/envelope.ts +432 -0
  299. package/src/errors.ts +165 -0
  300. package/src/express/index.ts +43 -0
  301. package/src/express/middleware.ts +387 -0
  302. package/src/express/store.ts +85 -0
  303. package/src/express/verify.ts +166 -0
  304. package/src/index.ts +107 -0
  305. package/src/outbox/client.ts +75 -0
  306. package/src/outbox/decision.ts +209 -0
  307. package/src/outbox/delivery.ts +163 -0
  308. package/src/outbox/enforcement/executor.ts +288 -0
  309. package/src/outbox/enforcement/planner.ts +377 -0
  310. package/src/outbox/evidence.ts +231 -0
  311. package/src/outbox/inbound.ts +140 -0
  312. package/src/outbox/index.ts +160 -0
  313. package/src/outbox/intake.ts +175 -0
  314. package/src/outbox/integration.ts +205 -0
  315. package/src/outbox/outbox/dispatcher.ts +131 -0
  316. package/src/outbox/outbox/service.ts +444 -0
  317. package/src/outbox/postgres/index.ts +59 -0
  318. package/src/outbox/postgres/registries.ts +177 -0
  319. package/src/outbox/postgres/reportColumns.ts +281 -0
  320. package/src/outbox/postgres/store/enforcement.ts +186 -0
  321. package/src/outbox/postgres/store/events.ts +112 -0
  322. package/src/outbox/postgres/store/index.ts +72 -0
  323. package/src/outbox/postgres/store/outbox.ts +273 -0
  324. package/src/outbox/postgres/store/reports.ts +349 -0
  325. package/src/outbox/postgres/store/transaction.ts +64 -0
  326. package/src/outbox/postgres/tables.ts +339 -0
  327. package/src/outbox/reconciliation.ts +199 -0
  328. package/src/outbox/reportStatus.ts +24 -0
  329. package/src/outbox/retention.ts +34 -0
  330. package/src/outbox/store/types.ts +528 -0
  331. package/src/outbox/types.ts +753 -0
  332. package/src/outbox/webhook.ts +165 -0
  333. package/src/reports.ts +138 -0
  334. package/src/testing/fixtures.ts +183 -0
  335. package/src/testing/index.ts +57 -0
  336. package/src/testing/sandbox.ts +469 -0
  337. package/src/testing/webhook-simulator.ts +166 -0
  338. package/src/transport.ts +250 -0
  339. 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"}