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