@blamejs/blamejs-shop 0.4.55 → 0.4.57

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 (399) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/admin.js +385 -2
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +426 -366
  5. package/lib/vendor/blamejs/.github/codeql/codeql-config.yml +25 -0
  6. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
  7. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
  8. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
  9. package/lib/vendor/blamejs/.github/workflows/ci.yml +13 -13
  10. package/lib/vendor/blamejs/.github/workflows/codeql.yml +8 -7
  11. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
  12. package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
  13. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +1 -1
  14. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
  15. package/lib/vendor/blamejs/CHANGELOG.md +2 -0
  16. package/lib/vendor/blamejs/api-snapshot.json +1381 -4
  17. package/lib/vendor/blamejs/eslint.config.mjs +1 -0
  18. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +4 -1
  19. package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +5 -2
  20. package/lib/vendor/blamejs/examples/wiki/routes/pages.js +4 -1
  21. package/lib/vendor/blamejs/fuzz/guard-text.fuzz.js +20 -0
  22. package/lib/vendor/blamejs/index.js +2 -0
  23. package/lib/vendor/blamejs/lib/a2a-tasks.js +7 -23
  24. package/lib/vendor/blamejs/lib/acme.js +6 -5
  25. package/lib/vendor/blamejs/lib/agent-event-bus.js +5 -4
  26. package/lib/vendor/blamejs/lib/agent-idempotency.js +2 -5
  27. package/lib/vendor/blamejs/lib/agent-orchestrator.js +2 -5
  28. package/lib/vendor/blamejs/lib/agent-saga.js +3 -5
  29. package/lib/vendor/blamejs/lib/agent-tenant.js +2 -5
  30. package/lib/vendor/blamejs/lib/ai-adverse-decision.js +2 -15
  31. package/lib/vendor/blamejs/lib/ai-capability.js +1 -6
  32. package/lib/vendor/blamejs/lib/ai-dp.js +1 -5
  33. package/lib/vendor/blamejs/lib/ai-input.js +2 -2
  34. package/lib/vendor/blamejs/lib/ai-pref.js +3 -8
  35. package/lib/vendor/blamejs/lib/ai-quota.js +3 -14
  36. package/lib/vendor/blamejs/lib/api-key.js +37 -28
  37. package/lib/vendor/blamejs/lib/archive-adapters.js +2 -4
  38. package/lib/vendor/blamejs/lib/archive-entry-policy.js +32 -0
  39. package/lib/vendor/blamejs/lib/archive-read.js +5 -17
  40. package/lib/vendor/blamejs/lib/archive-tar-read.js +5 -16
  41. package/lib/vendor/blamejs/lib/archive.js +2 -10
  42. package/lib/vendor/blamejs/lib/arg-parser.js +7 -6
  43. package/lib/vendor/blamejs/lib/asyncapi-traits.js +2 -6
  44. package/lib/vendor/blamejs/lib/atomic-file.js +108 -31
  45. package/lib/vendor/blamejs/lib/audit-chain.js +133 -53
  46. package/lib/vendor/blamejs/lib/audit-daily-review.js +24 -14
  47. package/lib/vendor/blamejs/lib/audit-emit.js +82 -0
  48. package/lib/vendor/blamejs/lib/audit-sign.js +257 -0
  49. package/lib/vendor/blamejs/lib/audit.js +84 -0
  50. package/lib/vendor/blamejs/lib/auth/access-lock.js +5 -27
  51. package/lib/vendor/blamejs/lib/auth/dpop.js +23 -35
  52. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +9 -15
  53. package/lib/vendor/blamejs/lib/auth/jwt-external.js +26 -8
  54. package/lib/vendor/blamejs/lib/auth/jwt.js +13 -15
  55. package/lib/vendor/blamejs/lib/auth/lockout.js +6 -25
  56. package/lib/vendor/blamejs/lib/auth/oauth.js +67 -45
  57. package/lib/vendor/blamejs/lib/auth/oid4vci.js +55 -32
  58. package/lib/vendor/blamejs/lib/auth/oid4vp.js +3 -2
  59. package/lib/vendor/blamejs/lib/auth/openid-federation.js +6 -6
  60. package/lib/vendor/blamejs/lib/auth/passkey.js +3 -3
  61. package/lib/vendor/blamejs/lib/auth/password.js +7 -1
  62. package/lib/vendor/blamejs/lib/auth/saml.js +37 -27
  63. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +2 -14
  64. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +2 -14
  65. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +1 -1
  66. package/lib/vendor/blamejs/lib/auth/status-list.js +7 -7
  67. package/lib/vendor/blamejs/lib/auth/step-up.js +6 -12
  68. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +6 -37
  69. package/lib/vendor/blamejs/lib/backup/bundle.js +11 -18
  70. package/lib/vendor/blamejs/lib/backup/index.js +14 -47
  71. package/lib/vendor/blamejs/lib/bounded-map.js +112 -1
  72. package/lib/vendor/blamejs/lib/breach-deadline.js +1 -11
  73. package/lib/vendor/blamejs/lib/cache.js +7 -18
  74. package/lib/vendor/blamejs/lib/cbor.js +2 -1
  75. package/lib/vendor/blamejs/lib/cdn-cache-control.js +8 -9
  76. package/lib/vendor/blamejs/lib/cert.js +3 -10
  77. package/lib/vendor/blamejs/lib/chain-writer.js +162 -47
  78. package/lib/vendor/blamejs/lib/cli.js +5 -1
  79. package/lib/vendor/blamejs/lib/client-hints.js +7 -9
  80. package/lib/vendor/blamejs/lib/cloud-events.js +40 -31
  81. package/lib/vendor/blamejs/lib/cluster-storage.js +2 -38
  82. package/lib/vendor/blamejs/lib/cms-codec.js +2 -1
  83. package/lib/vendor/blamejs/lib/codepoint-class.js +67 -1
  84. package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +1 -6
  85. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +2 -4
  86. package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -11
  87. package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +21 -28
  88. package/lib/vendor/blamejs/lib/compliance-sanctions.js +2 -14
  89. package/lib/vendor/blamejs/lib/compliance.js +2 -9
  90. package/lib/vendor/blamejs/lib/config-drift.js +2 -12
  91. package/lib/vendor/blamejs/lib/content-digest.js +10 -8
  92. package/lib/vendor/blamejs/lib/cookies.js +5 -11
  93. package/lib/vendor/blamejs/lib/cose.js +19 -11
  94. package/lib/vendor/blamejs/lib/cra-report.js +1 -11
  95. package/lib/vendor/blamejs/lib/crypto-field.js +5 -10
  96. package/lib/vendor/blamejs/lib/crypto.js +120 -3
  97. package/lib/vendor/blamejs/lib/csp.js +235 -3
  98. package/lib/vendor/blamejs/lib/daemon.js +42 -41
  99. package/lib/vendor/blamejs/lib/data-act.js +19 -9
  100. package/lib/vendor/blamejs/lib/db-query.js +6 -40
  101. package/lib/vendor/blamejs/lib/db.js +34 -12
  102. package/lib/vendor/blamejs/lib/dbsc.js +5 -6
  103. package/lib/vendor/blamejs/lib/ddl-change-control.js +18 -14
  104. package/lib/vendor/blamejs/lib/deprecate.js +4 -5
  105. package/lib/vendor/blamejs/lib/did.js +6 -2
  106. package/lib/vendor/blamejs/lib/dsr.js +8 -12
  107. package/lib/vendor/blamejs/lib/external-db-migrate.js +21 -22
  108. package/lib/vendor/blamejs/lib/external-db.js +14 -26
  109. package/lib/vendor/blamejs/lib/fda-21cfr11.js +12 -8
  110. package/lib/vendor/blamejs/lib/fdx.js +22 -18
  111. package/lib/vendor/blamejs/lib/file-upload.js +80 -66
  112. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +5 -8
  113. package/lib/vendor/blamejs/lib/framework-error.js +12 -0
  114. package/lib/vendor/blamejs/lib/framework-schema.js +19 -0
  115. package/lib/vendor/blamejs/lib/fsm.js +7 -12
  116. package/lib/vendor/blamejs/lib/gate-contract.js +869 -38
  117. package/lib/vendor/blamejs/lib/gdpr-ropa.js +4 -13
  118. package/lib/vendor/blamejs/lib/graphql-federation.js +1 -1
  119. package/lib/vendor/blamejs/lib/guard-agent-registry.js +2 -1
  120. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  121. package/lib/vendor/blamejs/lib/guard-archive.js +9 -30
  122. package/lib/vendor/blamejs/lib/guard-auth.js +23 -80
  123. package/lib/vendor/blamejs/lib/guard-cidr.js +20 -96
  124. package/lib/vendor/blamejs/lib/guard-csv.js +135 -196
  125. package/lib/vendor/blamejs/lib/guard-domain.js +23 -106
  126. package/lib/vendor/blamejs/lib/guard-dsn.js +16 -13
  127. package/lib/vendor/blamejs/lib/guard-email.js +46 -53
  128. package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
  129. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +2 -1
  130. package/lib/vendor/blamejs/lib/guard-filename.js +12 -60
  131. package/lib/vendor/blamejs/lib/guard-graphql.js +28 -75
  132. package/lib/vendor/blamejs/lib/guard-html-wcag.js +15 -2
  133. package/lib/vendor/blamejs/lib/guard-html.js +65 -117
  134. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +2 -1
  135. package/lib/vendor/blamejs/lib/guard-image.js +280 -77
  136. package/lib/vendor/blamejs/lib/guard-imap-command.js +8 -9
  137. package/lib/vendor/blamejs/lib/guard-json.js +87 -103
  138. package/lib/vendor/blamejs/lib/guard-jsonpath.js +20 -88
  139. package/lib/vendor/blamejs/lib/guard-jwt.js +32 -114
  140. package/lib/vendor/blamejs/lib/guard-list-id.js +2 -7
  141. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -7
  142. package/lib/vendor/blamejs/lib/guard-mail-compose.js +5 -6
  143. package/lib/vendor/blamejs/lib/guard-mail-move.js +2 -1
  144. package/lib/vendor/blamejs/lib/guard-mail-query.js +5 -3
  145. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +6 -7
  146. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +5 -4
  147. package/lib/vendor/blamejs/lib/guard-markdown.js +76 -110
  148. package/lib/vendor/blamejs/lib/guard-message-id.js +5 -6
  149. package/lib/vendor/blamejs/lib/guard-mime.js +20 -99
  150. package/lib/vendor/blamejs/lib/guard-oauth.js +19 -73
  151. package/lib/vendor/blamejs/lib/guard-pdf.js +65 -72
  152. package/lib/vendor/blamejs/lib/guard-pop3-command.js +12 -13
  153. package/lib/vendor/blamejs/lib/guard-posture-chain.js +2 -1
  154. package/lib/vendor/blamejs/lib/guard-regex.js +24 -99
  155. package/lib/vendor/blamejs/lib/guard-saga-config.js +2 -1
  156. package/lib/vendor/blamejs/lib/guard-shell.js +22 -87
  157. package/lib/vendor/blamejs/lib/guard-smtp-command.js +8 -11
  158. package/lib/vendor/blamejs/lib/guard-sql.js +15 -13
  159. package/lib/vendor/blamejs/lib/guard-stream-args.js +2 -1
  160. package/lib/vendor/blamejs/lib/guard-svg.js +95 -140
  161. package/lib/vendor/blamejs/lib/guard-template.js +23 -80
  162. package/lib/vendor/blamejs/lib/guard-tenant-id.js +2 -1
  163. package/lib/vendor/blamejs/lib/guard-text.js +592 -0
  164. package/lib/vendor/blamejs/lib/guard-time.js +27 -95
  165. package/lib/vendor/blamejs/lib/guard-uuid.js +21 -93
  166. package/lib/vendor/blamejs/lib/guard-xml.js +76 -106
  167. package/lib/vendor/blamejs/lib/guard-yaml.js +24 -60
  168. package/lib/vendor/blamejs/lib/http-client-cache.js +5 -12
  169. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +2 -4
  170. package/lib/vendor/blamejs/lib/http-client.js +8 -21
  171. package/lib/vendor/blamejs/lib/http-message-signature.js +3 -2
  172. package/lib/vendor/blamejs/lib/i18n-messageformat.js +5 -7
  173. package/lib/vendor/blamejs/lib/i18n.js +83 -26
  174. package/lib/vendor/blamejs/lib/inbox.js +8 -8
  175. package/lib/vendor/blamejs/lib/incident-report.js +3 -21
  176. package/lib/vendor/blamejs/lib/ip-utils.js +49 -6
  177. package/lib/vendor/blamejs/lib/jobs.js +3 -2
  178. package/lib/vendor/blamejs/lib/keychain.js +6 -18
  179. package/lib/vendor/blamejs/lib/legal-hold.js +6 -15
  180. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +44 -112
  181. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +17 -14
  182. package/lib/vendor/blamejs/lib/log-stream-otlp.js +16 -80
  183. package/lib/vendor/blamejs/lib/log-stream-webhook.js +20 -92
  184. package/lib/vendor/blamejs/lib/mail-arc-sign.js +8 -3
  185. package/lib/vendor/blamejs/lib/mail-arf.js +3 -2
  186. package/lib/vendor/blamejs/lib/mail-auth.js +40 -66
  187. package/lib/vendor/blamejs/lib/mail-bimi.js +19 -39
  188. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
  189. package/lib/vendor/blamejs/lib/mail-dav.js +8 -35
  190. package/lib/vendor/blamejs/lib/mail-deploy.js +6 -9
  191. package/lib/vendor/blamejs/lib/mail-dkim.js +19 -38
  192. package/lib/vendor/blamejs/lib/mail-greylist.js +15 -26
  193. package/lib/vendor/blamejs/lib/mail-helo.js +2 -3
  194. package/lib/vendor/blamejs/lib/mail-journal.js +2 -1
  195. package/lib/vendor/blamejs/lib/mail-mdn.js +4 -3
  196. package/lib/vendor/blamejs/lib/mail-rbl.js +5 -8
  197. package/lib/vendor/blamejs/lib/mail-scan.js +2 -2
  198. package/lib/vendor/blamejs/lib/mail-send-deliver.js +33 -20
  199. package/lib/vendor/blamejs/lib/mail-server-imap.js +32 -87
  200. package/lib/vendor/blamejs/lib/mail-server-jmap.js +35 -51
  201. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +29 -83
  202. package/lib/vendor/blamejs/lib/mail-server-mx.js +33 -74
  203. package/lib/vendor/blamejs/lib/mail-server-net.js +177 -0
  204. package/lib/vendor/blamejs/lib/mail-server-pop3.js +30 -83
  205. package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +30 -6
  206. package/lib/vendor/blamejs/lib/mail-server-submission.js +34 -73
  207. package/lib/vendor/blamejs/lib/mail-server-tls.js +89 -0
  208. package/lib/vendor/blamejs/lib/mail-spam-score.js +7 -8
  209. package/lib/vendor/blamejs/lib/mail-store.js +3 -2
  210. package/lib/vendor/blamejs/lib/mail.js +6 -11
  211. package/lib/vendor/blamejs/lib/markup-escape.js +31 -0
  212. package/lib/vendor/blamejs/lib/markup-tokenizer.js +54 -0
  213. package/lib/vendor/blamejs/lib/mcp-tool-registry.js +26 -23
  214. package/lib/vendor/blamejs/lib/mcp.js +1 -1
  215. package/lib/vendor/blamejs/lib/mdoc.js +11 -12
  216. package/lib/vendor/blamejs/lib/metrics.js +32 -30
  217. package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -23
  218. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +11 -22
  219. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -7
  220. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
  221. package/lib/vendor/blamejs/lib/middleware/body-parser.js +31 -48
  222. package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +7 -10
  223. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +2 -10
  224. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +13 -2
  225. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -26
  226. package/lib/vendor/blamejs/lib/middleware/deny-response.js +19 -1
  227. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +7 -0
  228. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +4 -20
  229. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +20 -28
  230. package/lib/vendor/blamejs/lib/middleware/scim-server.js +3 -2
  231. package/lib/vendor/blamejs/lib/middleware/security-headers.js +9 -1
  232. package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -6
  233. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +12 -27
  234. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -7
  235. package/lib/vendor/blamejs/lib/migrations.js +4 -13
  236. package/lib/vendor/blamejs/lib/mime-parse.js +34 -17
  237. package/lib/vendor/blamejs/lib/module-loader.js +44 -0
  238. package/lib/vendor/blamejs/lib/money.js +105 -0
  239. package/lib/vendor/blamejs/lib/mtls-ca.js +116 -36
  240. package/lib/vendor/blamejs/lib/network-byte-quota.js +4 -18
  241. package/lib/vendor/blamejs/lib/network-dane.js +8 -6
  242. package/lib/vendor/blamejs/lib/network-dns-resolver.js +97 -6
  243. package/lib/vendor/blamejs/lib/network-dnssec.js +16 -16
  244. package/lib/vendor/blamejs/lib/network-heartbeat.js +3 -2
  245. package/lib/vendor/blamejs/lib/network-smtp-policy.js +29 -41
  246. package/lib/vendor/blamejs/lib/network-tls.js +40 -42
  247. package/lib/vendor/blamejs/lib/nis2-report.js +1 -11
  248. package/lib/vendor/blamejs/lib/nonce-store.js +81 -3
  249. package/lib/vendor/blamejs/lib/numeric-bounds.js +22 -8
  250. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +2 -6
  251. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +5 -45
  252. package/lib/vendor/blamejs/lib/object-store/gcs.js +8 -71
  253. package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -5
  254. package/lib/vendor/blamejs/lib/object-store/http-request.js +128 -0
  255. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +2 -1
  256. package/lib/vendor/blamejs/lib/object-store/sigv4.js +9 -77
  257. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -25
  258. package/lib/vendor/blamejs/lib/observability.js +95 -0
  259. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +7 -3
  260. package/lib/vendor/blamejs/lib/otel-export.js +7 -10
  261. package/lib/vendor/blamejs/lib/outbox.js +43 -37
  262. package/lib/vendor/blamejs/lib/pagination.js +11 -15
  263. package/lib/vendor/blamejs/lib/parsers/safe-env.js +5 -4
  264. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +10 -4
  265. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +11 -9
  266. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +2 -2
  267. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +7 -6
  268. package/lib/vendor/blamejs/lib/pick.js +63 -5
  269. package/lib/vendor/blamejs/lib/pipl-cn.js +69 -59
  270. package/lib/vendor/blamejs/lib/privacy-pass.js +8 -6
  271. package/lib/vendor/blamejs/lib/problem-details.js +2 -5
  272. package/lib/vendor/blamejs/lib/pubsub.js +4 -8
  273. package/lib/vendor/blamejs/lib/redact.js +2 -1
  274. package/lib/vendor/blamejs/lib/render.js +4 -2
  275. package/lib/vendor/blamejs/lib/request-helpers.js +133 -6
  276. package/lib/vendor/blamejs/lib/restore.js +5 -22
  277. package/lib/vendor/blamejs/lib/retention.js +3 -5
  278. package/lib/vendor/blamejs/lib/safe-async.js +457 -0
  279. package/lib/vendor/blamejs/lib/safe-buffer.js +137 -6
  280. package/lib/vendor/blamejs/lib/safe-decompress.js +2 -1
  281. package/lib/vendor/blamejs/lib/safe-dns.js +2 -1
  282. package/lib/vendor/blamejs/lib/safe-ical.js +12 -26
  283. package/lib/vendor/blamejs/lib/safe-json.js +7 -8
  284. package/lib/vendor/blamejs/lib/safe-jsonpath.js +6 -5
  285. package/lib/vendor/blamejs/lib/safe-mime.js +25 -29
  286. package/lib/vendor/blamejs/lib/safe-redirect.js +2 -5
  287. package/lib/vendor/blamejs/lib/safe-schema.js +7 -6
  288. package/lib/vendor/blamejs/lib/safe-sql.js +126 -0
  289. package/lib/vendor/blamejs/lib/safe-vcard.js +12 -26
  290. package/lib/vendor/blamejs/lib/sandbox-worker.js +9 -2
  291. package/lib/vendor/blamejs/lib/sandbox.js +3 -2
  292. package/lib/vendor/blamejs/lib/scheduler.js +5 -12
  293. package/lib/vendor/blamejs/lib/sec-cyber.js +82 -37
  294. package/lib/vendor/blamejs/lib/seeders.js +4 -11
  295. package/lib/vendor/blamejs/lib/self-update.js +92 -69
  296. package/lib/vendor/blamejs/lib/session-device-binding.js +112 -66
  297. package/lib/vendor/blamejs/lib/session.js +194 -6
  298. package/lib/vendor/blamejs/lib/sql.js +225 -78
  299. package/lib/vendor/blamejs/lib/sse.js +6 -10
  300. package/lib/vendor/blamejs/lib/static.js +136 -98
  301. package/lib/vendor/blamejs/lib/storage.js +4 -2
  302. package/lib/vendor/blamejs/lib/structured-fields.js +275 -16
  303. package/lib/vendor/blamejs/lib/tenant-quota.js +2 -20
  304. package/lib/vendor/blamejs/lib/tsa.js +11 -15
  305. package/lib/vendor/blamejs/lib/validate-opts.js +154 -8
  306. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +30 -48
  307. package/lib/vendor/blamejs/lib/vault-aad.js +3 -2
  308. package/lib/vendor/blamejs/lib/vc.js +2 -7
  309. package/lib/vendor/blamejs/lib/vex.js +35 -13
  310. package/lib/vendor/blamejs/lib/web-push-vapid.js +23 -20
  311. package/lib/vendor/blamejs/lib/webhook-dispatcher.js +706 -0
  312. package/lib/vendor/blamejs/lib/webhook.js +43 -18
  313. package/lib/vendor/blamejs/lib/websocket-channels.js +9 -7
  314. package/lib/vendor/blamejs/lib/websocket.js +7 -3
  315. package/lib/vendor/blamejs/lib/worm.js +2 -3
  316. package/lib/vendor/blamejs/lib/ws-client.js +8 -10
  317. package/lib/vendor/blamejs/package.json +1 -1
  318. package/lib/vendor/blamejs/release-notes/v0.15.13.json +81 -0
  319. package/lib/vendor/blamejs/scripts/check-changelog-extract.js +1 -1
  320. package/lib/vendor/blamejs/test/00-primitives.js +136 -7
  321. package/lib/vendor/blamejs/test/10-state.js +9 -3
  322. package/lib/vendor/blamejs/test/30-chain.js +40 -0
  323. package/lib/vendor/blamejs/test/helpers/check.js +18 -0
  324. package/lib/vendor/blamejs/test/helpers/db.js +4 -0
  325. package/lib/vendor/blamejs/test/helpers/index.js +1 -0
  326. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +2 -1
  327. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +2 -1
  328. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +8 -1
  329. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +1 -1
  330. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +2 -1
  331. package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +269 -0
  332. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +149 -0
  333. package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +51 -0
  334. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +88 -0
  335. package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +55 -1
  336. package/lib/vendor/blamejs/test/layer-0-primitives/backup-object-store-adapter.test.js +2 -2
  337. package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +1 -1
  338. package/lib/vendor/blamejs/test/layer-0-primitives/bounded-map.test.js +129 -0
  339. package/lib/vendor/blamejs/test/layer-0-primitives/chain-writer-multichain.test.js +107 -0
  340. package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +12 -0
  341. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +2788 -3214
  342. package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +78 -0
  343. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +63 -0
  344. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +2 -1
  345. package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +50 -0
  346. package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +1 -1
  347. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +2 -1
  348. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-resolve-opts.test.js +76 -0
  349. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +9 -0
  350. package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract-content-gate.test.js +290 -0
  351. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +84 -0
  352. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +78 -0
  353. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +23 -0
  354. package/lib/vendor/blamejs/test/layer-0-primitives/guard-gate-disposition-coverage.test.js +93 -0
  355. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +60 -0
  356. package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +184 -0
  357. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  358. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +54 -0
  359. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +10 -0
  360. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +45 -0
  361. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +101 -0
  362. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +18 -0
  363. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +76 -0
  364. package/lib/vendor/blamejs/test/layer-0-primitives/guard-text.test.js +365 -0
  365. package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +35 -0
  366. package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +36 -0
  367. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +20 -0
  368. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -0
  369. package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +83 -0
  370. package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +10 -0
  371. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +39 -0
  372. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +36 -0
  373. package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +17 -0
  374. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +39 -0
  375. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +17 -0
  376. package/lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js +106 -0
  377. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +17 -0
  378. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-revocation.test.js +98 -0
  379. package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-release.test.js +77 -0
  380. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +48 -0
  381. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-memory-getorinsert.test.js +100 -0
  382. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +88 -0
  383. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +278 -0
  384. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +37 -0
  385. package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +14 -0
  386. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +19 -0
  387. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +1 -1
  388. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +2 -1
  389. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +18 -0
  390. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +1 -1
  391. package/lib/vendor/blamejs/test/layer-0-primitives/session-valid-from.test.js +73 -0
  392. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +100 -0
  393. package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +80 -0
  394. package/lib/vendor/blamejs/test/layer-0-primitives/validate-opts-shape.test.js +235 -0
  395. package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +374 -0
  396. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +75 -8
  397. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +3 -3
  398. package/lib/winback-campaigns.js +202 -42
  399. package/package.json +1 -1
@@ -0,0 +1,706 @@
1
+ "use strict";
2
+ /**
3
+ * Durable signed-webhook delivery store — the middle the framework was
4
+ * missing between b.webhook.signer (one inline POST, lost on exhaustion) and
5
+ * b.outbox (durable at-least-once, but terminates at an in-process publisher
6
+ * and never speaks HTTP). b.webhook.dispatcher composes the pieces already in
7
+ * the framework: it registers endpoints, fans one business event out to every
8
+ * subscribed endpoint as its own durable delivery row, signs each via
9
+ * b.webhook.signer, attempts HTTP delivery, backs off across attempts on the
10
+ * b.outbox retry curve, dead-letters after N attempts, and exposes a
11
+ * list/retry/replay surface an operator console can drive.
12
+ *
13
+ * Storage is the operator's b.externalDb (the same way b.outbox takes it).
14
+ * Per-endpoint secrets are sealed at rest with b.vault.seal — never stored
15
+ * plaintext. Destination URLs are validated through b.safeUrl at registration
16
+ * AND re-validated at every delivery attempt (DNS-rebinding / SSRF defense).
17
+ *
18
+ * Exposed as b.webhook.dispatcher; lives in its own module because a durable
19
+ * delivery store is a distinct domain from the stateless sign/verify surface
20
+ * in lib/webhook.js.
21
+ */
22
+
23
+ var C = require("./constants");
24
+ var sql = require("./sql");
25
+ var safeSql = require("./safe-sql");
26
+ var safeUrl = require("./safe-url");
27
+ var safeJson = require("./safe-json");
28
+ var bCrypto = require("./crypto");
29
+ var httpClient = require("./http-client");
30
+ var frameworkSchema = require("./framework-schema");
31
+ var validateOpts = require("./validate-opts");
32
+ var lazyRequire = require("./lazy-require");
33
+ var { WebhookDispatcherError } = require("./framework-error");
34
+
35
+ // lib/webhook re-exports this module as b.webhook.dispatcher, so requiring it
36
+ // eagerly here is a cycle — and webhook.js reassigns module.exports at the
37
+ // bottom, which would hand us a stale empty object. Defer to call time.
38
+ var webhookSign = lazyRequire(function () { return require("./webhook"); });
39
+ var vault = lazyRequire(function () { return require("./vault"); });
40
+ // ssrfGuard.checkUrl resolves the host and refuses private / loopback /
41
+ // link-local / metadata destinations — safeUrl.parse only refuses by protocol
42
+ // + userinfo, so it alone does NOT stop SSRF to an internal IP. Composed at
43
+ // registration AND every delivery attempt (DNS-rebinding defense).
44
+ var ssrfGuard = lazyRequire(function () { return require("./ssrf-guard"); });
45
+ var audit = lazyRequire(function () { return require("./audit"); });
46
+ var observability = lazyRequire(function () { return require("./observability"); });
47
+
48
+ var _err = WebhookDispatcherError.factory;
49
+
50
+ // ---- defaults ----
51
+ var DEFAULT_MAX_ATTEMPTS = 8;
52
+ var DEFAULT_BACKOFF_INITIAL = C.TIME.seconds(5);
53
+ var DEFAULT_BACKOFF_MAX = C.TIME.minutes(60);
54
+ var DEFAULT_BACKOFF_FACTOR = 2;
55
+ var DEFAULT_CLAIM_RECLAIM_MS = C.TIME.minutes(5);
56
+ var DEFAULT_BATCH_SIZE = 100;
57
+ var SIGNER_ALGO = "hmac-sha3-512"; // framework symmetric webhook MAC
58
+ var DELIVERY_ID_BYTES = C.BYTES.bytes(16);
59
+ var WILDCARD_EVENT = "*";
60
+
61
+ // HTTP success band — a 2xx is delivered; everything else (incl. 3xx/4xx/5xx)
62
+ // is a delivery failure that backs off and eventually dead-letters.
63
+ var HTTP_OK_MIN = 200;
64
+ var HTTP_OK_MAX = 300;
65
+
66
+ function _validateTableName(name, label) {
67
+ validateOpts.requireNonEmptyString(name, label, WebhookDispatcherError, "webhook-dispatcher/bad-opts");
68
+ // safeSql.quoteIdentifier refuses an injection-bearing name at construction.
69
+ safeSql.quoteIdentifier(name);
70
+ return name;
71
+ }
72
+
73
+ function _sqlDialect(externalDb) {
74
+ var d = externalDb && externalDb.dialect;
75
+ if (d === "postgres" || d === "postgresql") return "postgres";
76
+ if (d === "mysql") return "mysql";
77
+ return "sqlite";
78
+ }
79
+
80
+ // Coerce an integer column read back from the backend to a JS number. Drivers
81
+ // differ: node:sqlite returns INTEGER as a number, but a text protocol (and a
82
+ // node-postgres BIGINT/int8) returns it as a STRING — `"1" + 1` would
83
+ // string-concat to "11", corrupting the attempt counter. Normalize on read so
84
+ // the dispatcher's arithmetic is driver-agnostic.
85
+ function _intOf(v) {
86
+ if (typeof v === "number") return v;
87
+ if (v === null || v === undefined || v === "") return 0;
88
+ var n = Number(v);
89
+ return isFinite(n) ? n : 0;
90
+ }
91
+ function _intOrNull(v) {
92
+ if (v === null || v === undefined || v === "") return null;
93
+ var n = Number(v);
94
+ return isFinite(n) ? n : null;
95
+ }
96
+
97
+ function _coercePayloadString(payload) {
98
+ if (typeof payload === "string") return payload;
99
+ if (Buffer.isBuffer(payload)) return payload.toString("utf8");
100
+ // Objects serialize via safeJson (proto-safe, stable) so the signed body is
101
+ // deterministic and the receiver verifies the same bytes we signed.
102
+ return safeJson.stringify(payload);
103
+ }
104
+
105
+ /**
106
+ * @primitive b.webhook.dispatcher
107
+ * @signature b.webhook.dispatcher(opts)
108
+ * @since 0.15.13
109
+ * @status stable
110
+ * @related b.webhook.signer, b.webhook.verify, b.outbox.create, b.nonceStore.create
111
+ *
112
+ * Build a durable signed-webhook delivery store backed by the operator's
113
+ * `b.externalDb`. The returned object exposes:
114
+ *
115
+ * - `declareSchema(xdb?)` — idempotent `CREATE TABLE` for the endpoints +
116
+ * deliveries tables (run once at boot, like `b.outbox.declareSchema`).
117
+ * - `registerEndpoint({ endpointId, url, eventTypes, secret })` — persist a
118
+ * subscriber. The URL is validated through `b.safeUrl` (SSRF destinations
119
+ * refused); the secret is sealed at rest with `b.vault.seal`. `eventTypes`
120
+ * is an array of event names, or `["*"]` to receive every event.
121
+ * - `removeEndpoint(endpointId)` / `listEndpoints()`.
122
+ * - `dispatch(eventType, payload)` — fan the event out to every subscribed
123
+ * endpoint as its own durable delivery row, sign each via
124
+ * `b.webhook.signer`, and attempt delivery once inline. Returns
125
+ * `{ delivered, failed, deliveries: [...] }`.
126
+ * - `processRetries()` — poll/alarm entry point: claim every delivery whose
127
+ * `next_attempt_at` is due, re-attempt, back off on the `b.outbox` curve,
128
+ * and dead-letter after `maxAttempts`. Reaps deliveries stranded
129
+ * in-flight by a crashed worker. Returns `{ attempted, delivered, dead }`.
130
+ * - `deliveries.list({ endpointId?, status?, limit? })` / `deliveries.get(id)`
131
+ * / `deliveries.retry(id)` — operator-console surface.
132
+ * - `dlq.list({ limit? })` / `dlq.replay(id)` — dead-letter inspect + replay.
133
+ *
134
+ * Each delivery carries a stable `X-Webhook-Delivery-Id` (so a re-delivery is
135
+ * deduped by the receiver, not rejected as a replay) plus the signer's fresh
136
+ * per-attempt nonce in the signature (replay defense at the signature layer).
137
+ *
138
+ * @opts
139
+ * externalDb: b.externalDb, // required — storage backend
140
+ * endpointsTable: string, // default frameworkSchema.tableName("webhook_endpoints")
141
+ * deliveriesTable: string, // default frameworkSchema.tableName("webhook_deliveries")
142
+ * maxAttempts: number, // default 8 → then dead-letter
143
+ * retryBackoff: { initialMs, maxMs, factor }, // default 5s / 60min / 2x
144
+ * claimReclaimMs: number, // default 5 min stale-in-flight lease
145
+ * batchSize: number, // default 100 deliveries per processRetries
146
+ * signatureHeader: string, // forwarded to b.webhook.signer
147
+ * allowedProtocols: object, // b.safeUrl protocol set (default ALLOW_HTTP_TLS)
148
+ * allowInternalDestinations: boolean, // default false — refuse SSRF (private/loopback/metadata)
149
+ * httpRequest: function, // (url, body, headers) → { status } — inject for tests
150
+ * now: function, // clock injection → ms epoch
151
+ *
152
+ * @example
153
+ * var wd = b.webhook.dispatcher({ externalDb: b.externalDb });
154
+ * await wd.declareSchema();
155
+ * await wd.registerEndpoint({
156
+ * endpointId: "acct_42",
157
+ * url: "https://partner.example/hooks",
158
+ * eventTypes: ["invoice.paid", "invoice.refunded"],
159
+ * secret: "whsec_partner_secret",
160
+ * });
161
+ * await wd.dispatch("invoice.paid", { id: "inv_1", amount: 4200 });
162
+ * // later, from a cron / alarm:
163
+ * await wd.processRetries();
164
+ */
165
+ function dispatcher(opts) {
166
+ validateOpts.shape(opts, {
167
+ externalDb: { methods: ["query", "transaction"] },
168
+ endpointsTable: "optional-string",
169
+ deliveriesTable: "optional-string",
170
+ maxAttempts: "optional-positive-finite",
171
+ batchSize: "optional-positive-finite",
172
+ claimReclaimMs: "optional-positive-finite",
173
+ retryBackoff: { optional: true, shape: {
174
+ initialMs: "optional-positive-finite",
175
+ maxMs: "optional-positive-finite",
176
+ factor: "optional-positive-finite",
177
+ } },
178
+ signatureHeader: "optional-string",
179
+ allowedProtocols: "optional-plain-object",
180
+ allowInternalDestinations: "optional-boolean",
181
+ httpRequest: "optional-function",
182
+ now: "optional-function",
183
+ }, "webhook.dispatcher", WebhookDispatcherError, "webhook-dispatcher/bad-opts");
184
+ var externalDb = opts.externalDb;
185
+ var endpointsTable = _validateTableName(
186
+ opts.endpointsTable || frameworkSchema.tableName("webhook_endpoints"),
187
+ "dispatcher: endpointsTable");
188
+ var deliveriesTable = _validateTableName(
189
+ opts.deliveriesTable || frameworkSchema.tableName("webhook_deliveries"),
190
+ "dispatcher: deliveriesTable");
191
+
192
+ var maxAttempts = opts.maxAttempts || DEFAULT_MAX_ATTEMPTS;
193
+ var batchSize = opts.batchSize || DEFAULT_BATCH_SIZE;
194
+ var claimReclaimMs = opts.claimReclaimMs || DEFAULT_CLAIM_RECLAIM_MS;
195
+
196
+ var backoff = opts.retryBackoff || {};
197
+ var backoffInitial = backoff.initialMs || DEFAULT_BACKOFF_INITIAL;
198
+ var backoffMax = backoff.maxMs || DEFAULT_BACKOFF_MAX;
199
+ var backoffFactor = backoff.factor || DEFAULT_BACKOFF_FACTOR;
200
+
201
+ var allowedProtocols = opts.allowedProtocols || safeUrl.ALLOW_HTTP_TLS;
202
+ var signatureHeader = opts.signatureHeader || null;
203
+ // Default-secure: refuse delivery to internal IP ranges. An operator whose
204
+ // subscribers genuinely live on a private network opts in explicitly.
205
+ var allowInternal = opts.allowInternalDestinations === true;
206
+ var clock = typeof opts.now === "function" ? opts.now : function () { return Date.now(); };
207
+
208
+ // Refuse an SSRF / malformed destination: protocol + userinfo via safeUrl,
209
+ // IP classification (private / loopback / metadata) via ssrfGuard. Async
210
+ // because the IP check resolves the host. Throws a dispatcher-coded error.
211
+ async function _assertSafeDestination(url, where) {
212
+ safeUrl.parse(url, { allowedProtocols: allowedProtocols, errorClass: WebhookDispatcherError });
213
+ try {
214
+ await ssrfGuard().checkUrl(url, { allowInternal: allowInternal });
215
+ } catch (e) {
216
+ if (e && e.isSsrfError) {
217
+ throw _err("webhook-dispatcher/ssrf-refused", where + ": " + e.message);
218
+ }
219
+ throw e;
220
+ }
221
+ }
222
+
223
+ // Default transport: a real signed POST through the framework http client.
224
+ // Injectable so a test (or a non-HTTP transport) substitutes its own.
225
+ var httpRequest = opts.httpRequest || function (url, body, headers) {
226
+ return httpClient.request({
227
+ method: "POST",
228
+ url: url,
229
+ headers: headers,
230
+ body: body,
231
+ allowedProtocols: allowedProtocols,
232
+ errorClass: WebhookDispatcherError,
233
+ }).then(function (res) {
234
+ return { status: (res && (res.statusCode || res.status)) || 0 };
235
+ });
236
+ };
237
+
238
+ // Per-secret signer cache — one b.webhook.signer bound to each endpoint
239
+ // secret, keyed by the (unsealed) secret so re-deliveries reuse it.
240
+ var _signerCache = Object.create(null);
241
+ function _signerFor(secret) {
242
+ if (_signerCache[secret]) return _signerCache[secret];
243
+ var s = webhookSign().signer({
244
+ algo: SIGNER_ALGO,
245
+ keys: { v1: secret },
246
+ signatureHeader: signatureHeader || undefined,
247
+ });
248
+ _signerCache[secret] = s;
249
+ return s;
250
+ }
251
+
252
+ function _backoffMs(attempts) {
253
+ var ms = backoffInitial * Math.pow(backoffFactor, Math.max(0, attempts - 1));
254
+ if (ms > backoffMax) ms = backoffMax;
255
+ return ms;
256
+ }
257
+
258
+ function _nowDate() { return new Date(clock()); }
259
+
260
+ // ---- schema ----
261
+ async function declareSchema(xdb) {
262
+ var target = xdb || externalDb;
263
+ var dialect = _sqlDialect(target);
264
+ var tsType = dialect === "postgres" ? "TIMESTAMPTZ" : "TIMESTAMP";
265
+ var endpointsDdl = sql.toExternalSql(sql.createTable(endpointsTable, [
266
+ { name: "id", serial: true },
267
+ { name: "endpoint_id", type: "VARCHAR(255)", notNull: true },
268
+ { name: "url", type: "TEXT", notNull: true },
269
+ { name: "event_types", type: "TEXT", notNull: true },
270
+ { name: "secret_sealed", type: "TEXT", notNull: true },
271
+ { name: "disabled", type: "INTEGER", notNull: true, default: 0 },
272
+ { name: "created_at", type: tsType, notNull: true },
273
+ ], { dialect: dialect }), dialect);
274
+ var endpointsIdx = sql.toExternalSql(sql.createIndex(endpointsTable + "_eid_idx",
275
+ endpointsTable, ["endpoint_id"], { dialect: dialect }), dialect);
276
+
277
+ var deliveriesDdl = sql.toExternalSql(sql.createTable(deliveriesTable, [
278
+ { name: "id", serial: true },
279
+ { name: "delivery_id", type: "VARCHAR(64)", notNull: true },
280
+ { name: "endpoint_id", type: "VARCHAR(255)", notNull: true },
281
+ { name: "url", type: "TEXT", notNull: true },
282
+ { name: "event_type", type: "VARCHAR(255)", notNull: true },
283
+ { name: "payload", type: "TEXT", notNull: true },
284
+ { name: "idempotency_id", type: "VARCHAR(64)", notNull: true },
285
+ { name: "status", type: "VARCHAR(16)", notNull: true, default: "pending" },
286
+ { name: "attempts", type: "INTEGER", notNull: true, default: 0 },
287
+ { name: "next_attempt_at", type: tsType, notNull: true },
288
+ { name: "claimed_at", type: tsType },
289
+ { name: "delivered_at", type: tsType },
290
+ { name: "response_status", type: "INTEGER" },
291
+ { name: "last_error", type: "TEXT" },
292
+ { name: "created_at", type: tsType, notNull: true },
293
+ ], { dialect: dialect }), dialect);
294
+ // Index on the due-pending pool the retry poller scans. Postgres/SQLite
295
+ // get a PARTIAL index (status = 'pending'); MySQL has no partial indexes
296
+ // (sql.createIndex refuses `where` for the mysql dialect), so it gets a
297
+ // plain index on next_attempt_at — the processRetries query still filters
298
+ // status = 'pending', so correctness is unchanged, only the index is a
299
+ // touch less selective.
300
+ var deliveriesIdxOpts = { dialect: dialect };
301
+ if (dialect !== "mysql") deliveriesIdxOpts.where = "status = 'pending'";
302
+ var deliveriesIdx = sql.toExternalSql(sql.createIndex(deliveriesTable + "_pending_idx",
303
+ deliveriesTable, ["next_attempt_at"], deliveriesIdxOpts), dialect);
304
+
305
+ await target.query(endpointsDdl.sql, endpointsDdl.params);
306
+ await target.query(endpointsIdx.sql, endpointsIdx.params);
307
+ await target.query(deliveriesDdl.sql, deliveriesDdl.params);
308
+ await target.query(deliveriesIdx.sql, deliveriesIdx.params);
309
+ }
310
+
311
+ // ---- endpoint registration ----
312
+ async function registerEndpoint(ep) {
313
+ validateOpts.shape(ep, {
314
+ endpointId: "required-string",
315
+ url: "required-string",
316
+ secret: "required-string",
317
+ eventTypes: "optional-string-array", // element validation; required-non-empty checked below
318
+ }, "dispatcher.registerEndpoint", WebhookDispatcherError, "webhook-dispatcher/bad-opts");
319
+ if (!Array.isArray(ep.eventTypes) || ep.eventTypes.length === 0) {
320
+ throw _err("webhook-dispatcher/bad-opts",
321
+ "registerEndpoint: eventTypes must be a non-empty array of event names (or [\"*\"])");
322
+ }
323
+ // Refuse an SSRF / malformed destination at registration time — fail fast,
324
+ // before a single delivery row is written.
325
+ await _assertSafeDestination(ep.url, "registerEndpoint");
326
+
327
+ var dialect = _sqlDialect(externalDb);
328
+ var sealedSecret = vault().seal(ep.secret);
329
+ var stmt = sql.insert(endpointsTable, { dialect: dialect })
330
+ .values({
331
+ endpoint_id: ep.endpointId,
332
+ url: ep.url,
333
+ event_types: safeJson.stringify(ep.eventTypes),
334
+ secret_sealed: sealedSecret,
335
+ disabled: 0,
336
+ created_at: _nowDate(),
337
+ })
338
+ .toExternalSql(dialect);
339
+ await externalDb.query(stmt.sql, stmt.params);
340
+ _emitAudit("webhook.dispatcher.endpoint.register", "success",
341
+ { endpointId: ep.endpointId, eventTypes: ep.eventTypes });
342
+ return { endpointId: ep.endpointId };
343
+ }
344
+
345
+ async function removeEndpoint(endpointId) {
346
+ validateOpts.requireNonEmptyString(endpointId, "removeEndpoint: endpointId",
347
+ WebhookDispatcherError, "webhook-dispatcher/bad-opts");
348
+ var dialect = _sqlDialect(externalDb);
349
+ var stmt = sql.delete(endpointsTable, { dialect: dialect })
350
+ .where("endpoint_id", endpointId)
351
+ .toExternalSql(dialect);
352
+ await externalDb.query(stmt.sql, stmt.params);
353
+ return { endpointId: endpointId, removed: true };
354
+ }
355
+
356
+ async function listEndpoints() {
357
+ var dialect = _sqlDialect(externalDb);
358
+ var stmt = sql.select(endpointsTable, { dialect: dialect })
359
+ .columns(["endpoint_id", "url", "event_types", "disabled", "created_at"])
360
+ .toExternalSql(dialect);
361
+ var res = await externalDb.query(stmt.sql, stmt.params);
362
+ return ((res && res.rows) || []).map(function (r) {
363
+ return {
364
+ endpointId: r.endpoint_id,
365
+ url: r.url,
366
+ eventTypes: safeJson.parse(r.event_types),
367
+ disabled: _isTruthy(r.disabled),
368
+ createdAt: r.created_at,
369
+ };
370
+ });
371
+ }
372
+
373
+ // Load every enabled endpoint subscribed to eventType. event_types is a JSON
374
+ // array stored as TEXT (portable across the three dialects); membership is
375
+ // resolved in JS because a portable JSON-contains predicate doesn't exist
376
+ // across sqlite / postgres / mysql. The endpoint count for a single tenant's
377
+ // webhook fan-out is modest, so the full scan is acceptable.
378
+ async function _subscribedEndpoints(eventType) {
379
+ var all = await listEndpoints();
380
+ return all.filter(function (e) {
381
+ if (e.disabled) return false;
382
+ var types = e.eventTypes || [];
383
+ return types.indexOf(eventType) !== -1 || types.indexOf(WILDCARD_EVENT) !== -1;
384
+ });
385
+ }
386
+
387
+ async function _loadEndpointRow(endpointId) {
388
+ var dialect = _sqlDialect(externalDb);
389
+ var stmt = sql.select(endpointsTable, { dialect: dialect })
390
+ .columns(["endpoint_id", "url", "secret_sealed", "disabled"])
391
+ .where("endpoint_id", endpointId)
392
+ .limit(1)
393
+ .toExternalSql(dialect);
394
+ var res = await externalDb.query(stmt.sql, stmt.params);
395
+ return (res && res.rows && res.rows[0]) || null;
396
+ }
397
+
398
+ // ---- dispatch / delivery ----
399
+ async function dispatch(eventType, payload) {
400
+ validateOpts.requireNonEmptyString(eventType, "dispatch: eventType",
401
+ WebhookDispatcherError, "webhook-dispatcher/bad-opts");
402
+ if (payload === undefined || payload === null) {
403
+ throw _err("webhook-dispatcher/bad-opts", "dispatch: payload required");
404
+ }
405
+ var bodyStr = _coercePayloadString(payload);
406
+ var endpoints = await _subscribedEndpoints(eventType);
407
+ var dialect = _sqlDialect(externalDb);
408
+ var results = [];
409
+ for (var i = 0; i < endpoints.length; i += 1) {
410
+ var ep = endpoints[i];
411
+ var deliveryId = bCrypto.generateToken(DELIVERY_ID_BYTES);
412
+ var idempotencyId = bCrypto.generateToken(DELIVERY_ID_BYTES);
413
+ // Insert already CLAIMED for the inline attempt (status 'in-flight' +
414
+ // claimed_at = now). processRetries() only claims status='pending' rows,
415
+ // so a poller running during the slow inline POST cannot grab this row
416
+ // and double-deliver it. The inline _attemptDelivery transitions it to
417
+ // delivered / pending(+backoff) / dead; if this process dies mid-POST,
418
+ // _reapStaleInflight reclaims it after claimReclaimMs.
419
+ var insertStmt = sql.insert(deliveriesTable, { dialect: dialect })
420
+ .values({
421
+ delivery_id: deliveryId,
422
+ endpoint_id: ep.endpointId,
423
+ url: ep.url,
424
+ event_type: eventType,
425
+ payload: bodyStr,
426
+ idempotency_id: idempotencyId,
427
+ status: "in-flight",
428
+ attempts: 0,
429
+ next_attempt_at: _nowDate(),
430
+ claimed_at: _nowDate(),
431
+ created_at: _nowDate(),
432
+ })
433
+ .toExternalSql(dialect);
434
+ await externalDb.query(insertStmt.sql, insertStmt.params);
435
+ // Best-effort first attempt inline; on failure it is scheduled for retry.
436
+ var r = await _attemptDelivery(deliveryId);
437
+ results.push(r);
438
+ }
439
+ return {
440
+ delivered: results.filter(function (r) { return r.ok; }).length,
441
+ failed: results.filter(function (r) { return !r.ok; }).length,
442
+ deliveries: results,
443
+ };
444
+ }
445
+
446
+ async function _loadDelivery(deliveryId) {
447
+ var dialect = _sqlDialect(externalDb);
448
+ var stmt = sql.select(deliveriesTable, { dialect: dialect })
449
+ .columns(["delivery_id", "endpoint_id", "url", "event_type", "payload",
450
+ "idempotency_id", "status", "attempts"])
451
+ .where("delivery_id", deliveryId)
452
+ .limit(1)
453
+ .toExternalSql(dialect);
454
+ var res = await externalDb.query(stmt.sql, stmt.params);
455
+ return (res && res.rows && res.rows[0]) || null;
456
+ }
457
+
458
+ // Attempt one delivery by id: sign with the endpoint's sealed secret, POST,
459
+ // and transition the row on the outcome (delivered / retry / dead).
460
+ async function _attemptDelivery(deliveryId) {
461
+ var row = await _loadDelivery(deliveryId);
462
+ if (!row) return { deliveryId: deliveryId, ok: false, status: 0, error: "delivery row not found" };
463
+ var epRow = await _loadEndpointRow(row.endpoint_id);
464
+ if (!epRow) {
465
+ await _markDead(deliveryId, _intOf(row.attempts) + 1, "endpoint no longer registered");
466
+ return { deliveryId: deliveryId, ok: false, status: 0, dead: true, error: "endpoint missing" };
467
+ }
468
+ var attemptNo = _intOf(row.attempts) + 1;
469
+ // Re-validate the destination at delivery time — a host that resolved
470
+ // public at registration but rebound to an internal IP is refused here.
471
+ // An SSRF refusal / malformed URL is PERMANENT (won't fix on retry) →
472
+ // dead-letter immediately. Permanence is decided HERE by the failure
473
+ // CLASS, not by err.permanent: the transport (httpClient) errors below
474
+ // are thrown with WebhookDispatcherError, which is alwaysPermanent, so
475
+ // reading err.permanent would mis-mark a timeout / network error / 5xx
476
+ // as permanent and dead-letter it on the first attempt.
477
+ try {
478
+ await _assertSafeDestination(row.url, "deliver");
479
+ } catch (err) {
480
+ return await _onFailure(deliveryId, attemptNo,
481
+ (err && err.message) || String(err), true);
482
+ }
483
+ // Sign + POST. Transport errors (network, TLS, timeout, DNS) and any
484
+ // non-2xx HTTP status are TRANSIENT — back off and retry (capped at
485
+ // maxAttempts, after which _onFailure dead-letters).
486
+ try {
487
+ var secret = vault().unseal(epRow.secret_sealed);
488
+ var signer = _signerFor(secret);
489
+ var signed = signer.sign(row.payload);
490
+ var headers = Object.assign({}, signed.headers, {
491
+ "Content-Type": "application/json",
492
+ "X-Webhook-Delivery-Id": row.delivery_id,
493
+ "X-Webhook-Event-Type": row.event_type,
494
+ "X-Webhook-Idempotency-Id": row.idempotency_id,
495
+ });
496
+ var result = await httpRequest(row.url, row.payload, headers);
497
+ var status = (result && (result.status || result.statusCode)) || 0;
498
+ if (status >= HTTP_OK_MIN && status < HTTP_OK_MAX) {
499
+ await _markDelivered(deliveryId, attemptNo, status);
500
+ return { deliveryId: deliveryId, ok: true, status: status };
501
+ }
502
+ return await _onFailure(deliveryId, attemptNo, "delivery returned HTTP " + status, false);
503
+ } catch (err) {
504
+ return await _onFailure(deliveryId, attemptNo,
505
+ (err && err.message) || String(err), false);
506
+ }
507
+ }
508
+
509
+ async function _markDelivered(deliveryId, attemptNo, status) {
510
+ var dialect = _sqlDialect(externalDb);
511
+ var stmt = sql.update(deliveriesTable, { dialect: dialect })
512
+ .set({
513
+ status: "delivered",
514
+ attempts: attemptNo,
515
+ delivered_at: _nowDate(),
516
+ response_status: status,
517
+ claimed_at: null,
518
+ })
519
+ .where("delivery_id", deliveryId)
520
+ .toExternalSql(dialect);
521
+ await externalDb.query(stmt.sql, stmt.params);
522
+ }
523
+
524
+ // A failed attempt: dead-letter once attempts reach maxAttempts OR the error
525
+ // is permanent (won't fix on retry), else reschedule on the backoff curve.
526
+ async function _onFailure(deliveryId, attemptNo, errMsg, permanent) {
527
+ if (permanent || attemptNo >= maxAttempts) {
528
+ await _markDead(deliveryId, attemptNo, errMsg);
529
+ return { deliveryId: deliveryId, ok: false, status: 0, dead: true, error: errMsg };
530
+ }
531
+ var dialect = _sqlDialect(externalDb);
532
+ var nextAt = new Date(clock() + _backoffMs(attemptNo));
533
+ var stmt = sql.update(deliveriesTable, { dialect: dialect })
534
+ .set({
535
+ status: "pending",
536
+ attempts: attemptNo,
537
+ next_attempt_at: nextAt,
538
+ last_error: errMsg,
539
+ claimed_at: null,
540
+ })
541
+ .where("delivery_id", deliveryId)
542
+ .toExternalSql(dialect);
543
+ await externalDb.query(stmt.sql, stmt.params);
544
+ return { deliveryId: deliveryId, ok: false, status: 0, error: errMsg, nextAttemptAt: nextAt };
545
+ }
546
+
547
+ async function _markDead(deliveryId, attemptNo, errMsg) {
548
+ var dialect = _sqlDialect(externalDb);
549
+ var stmt = sql.update(deliveriesTable, { dialect: dialect })
550
+ .set({ status: "dead", attempts: attemptNo, last_error: errMsg, claimed_at: null })
551
+ .where("delivery_id", deliveryId)
552
+ .toExternalSql(dialect);
553
+ await externalDb.query(stmt.sql, stmt.params);
554
+ _emitAudit("webhook.dispatcher.dead-letter", "failure",
555
+ { deliveryId: deliveryId, attempts: attemptNo, error: errMsg });
556
+ }
557
+
558
+ // ---- retry poller ----
559
+ // Reset deliveries stranded 'in-flight' by a crashed worker (claimed but
560
+ // never resolved) back to 'pending' once the lease expires — the
561
+ // at-least-once guarantee b.outbox's reaper proved is the framework's job.
562
+ async function _reapStaleInflight() {
563
+ var dialect = _sqlDialect(externalDb);
564
+ var cutoff = new Date(clock() - claimReclaimMs);
565
+ var stmt = sql.update(deliveriesTable, { dialect: dialect })
566
+ .set({ status: "pending", claimed_at: null })
567
+ .whereRaw("status = 'in-flight'", [], { allowLiterals: true })
568
+ .whereRaw("(claimed_at IS NULL OR claimed_at <= ?)", [cutoff])
569
+ .toExternalSql(dialect);
570
+ await externalDb.query(stmt.sql, stmt.params);
571
+ }
572
+
573
+ // Claim due-pending deliveries by flipping them to 'in-flight' inside a
574
+ // transaction (mark-then-reselect; gated on status='pending' so two pollers
575
+ // can't both claim the same row), then attempt each claimed delivery.
576
+ async function processRetries() {
577
+ await _reapStaleInflight();
578
+ var dialect = _sqlDialect(externalDb);
579
+ var claimed = await externalDb.transaction(async function (xdb) {
580
+ var nowDate = _nowDate();
581
+ var sel = sql.select(deliveriesTable, { dialect: dialect })
582
+ .columns(["delivery_id"])
583
+ .whereRaw("status = 'pending'", [], { allowLiterals: true })
584
+ .whereRaw("next_attempt_at <= ?", [nowDate])
585
+ .orderBy("next_attempt_at")
586
+ .limit(batchSize)
587
+ .toExternalSql(dialect);
588
+ var rows = await xdb.query(sel.sql, sel.params);
589
+ var ids = ((rows && rows.rows) || []).map(function (r) { return r.delivery_id; });
590
+ if (ids.length === 0) return [];
591
+ var mark = sql.update(deliveriesTable, { dialect: dialect })
592
+ .set({ status: "in-flight", claimed_at: _nowDate() })
593
+ .whereRaw("status = 'pending'", [], { allowLiterals: true })
594
+ .whereInArray("delivery_id", ids)
595
+ .toExternalSql(dialect);
596
+ await xdb.query(mark.sql, mark.params);
597
+ var after = sql.select(deliveriesTable, { dialect: dialect })
598
+ .columns(["delivery_id"])
599
+ .whereRaw("status = 'in-flight'", [], { allowLiterals: true })
600
+ .whereInArray("delivery_id", ids)
601
+ .toExternalSql(dialect);
602
+ var afterRows = await xdb.query(after.sql, after.params);
603
+ return ((afterRows && afterRows.rows) || []).map(function (r) { return r.delivery_id; });
604
+ });
605
+
606
+ var attempted = 0, delivered = 0, dead = 0;
607
+ for (var i = 0; i < claimed.length; i += 1) {
608
+ var r = await _attemptDelivery(claimed[i]);
609
+ attempted += 1;
610
+ if (r.ok) delivered += 1;
611
+ if (r.dead) dead += 1;
612
+ }
613
+ return { attempted: attempted, delivered: delivered, dead: dead };
614
+ }
615
+
616
+ // ---- operator-console surface ----
617
+ function _mapDelivery(r) {
618
+ return {
619
+ deliveryId: r.delivery_id,
620
+ endpointId: r.endpoint_id,
621
+ eventType: r.event_type,
622
+ status: r.status,
623
+ attempts: _intOf(r.attempts),
624
+ nextAttemptAt: r.next_attempt_at,
625
+ deliveredAt: r.delivered_at,
626
+ responseStatus: _intOrNull(r.response_status),
627
+ lastError: r.last_error,
628
+ };
629
+ }
630
+
631
+ var DELIVERY_VIEW_COLS = ["delivery_id", "endpoint_id", "event_type", "status",
632
+ "attempts", "next_attempt_at", "delivered_at", "response_status", "last_error"];
633
+
634
+ async function _listDeliveries(filter) {
635
+ filter = filter || {};
636
+ var dialect = _sqlDialect(externalDb);
637
+ var builder = sql.select(deliveriesTable, { dialect: dialect }).columns(DELIVERY_VIEW_COLS);
638
+ if (filter.endpointId) builder.where("endpoint_id", filter.endpointId);
639
+ if (filter.status) builder.where("status", filter.status);
640
+ builder.orderBy("id").limit(filter.limit || DEFAULT_BATCH_SIZE);
641
+ var stmt = builder.toExternalSql(dialect);
642
+ var res = await externalDb.query(stmt.sql, stmt.params);
643
+ return ((res && res.rows) || []).map(_mapDelivery);
644
+ }
645
+
646
+ async function _getDelivery(deliveryId) {
647
+ var dialect = _sqlDialect(externalDb);
648
+ var stmt = sql.select(deliveriesTable, { dialect: dialect })
649
+ .columns(DELIVERY_VIEW_COLS)
650
+ .where("delivery_id", deliveryId)
651
+ .limit(1)
652
+ .toExternalSql(dialect);
653
+ var res = await externalDb.query(stmt.sql, stmt.params);
654
+ var row = res && res.rows && res.rows[0];
655
+ return row ? _mapDelivery(row) : null;
656
+ }
657
+
658
+ // Reset a delivery to the pending pool for an immediate re-attempt. Used by
659
+ // both deliveries.retry (any status) and dlq.replay (dead → pending).
660
+ async function _requeue(deliveryId) {
661
+ validateOpts.requireNonEmptyString(deliveryId, "retry: deliveryId",
662
+ WebhookDispatcherError, "webhook-dispatcher/bad-opts");
663
+ var dialect = _sqlDialect(externalDb);
664
+ var stmt = sql.update(deliveriesTable, { dialect: dialect })
665
+ .set({ status: "pending", attempts: 0, next_attempt_at: _nowDate(), claimed_at: null, last_error: null })
666
+ .where("delivery_id", deliveryId)
667
+ .toExternalSql(dialect);
668
+ await externalDb.query(stmt.sql, stmt.params);
669
+ return await _attemptDelivery(deliveryId);
670
+ }
671
+
672
+ function _emitAudit(action, outcome, metadata) {
673
+ try {
674
+ audit().safeEmit({ action: action, outcome: outcome, metadata: metadata || {} });
675
+ } catch (_e) { /* audit is a drop-silent hot-path sink — never crash the delivery */ }
676
+ try {
677
+ observability().safeEvent(action, 1, metadata || {});
678
+ } catch (_e) { /* drop-silent */ }
679
+ }
680
+
681
+ return {
682
+ declareSchema: declareSchema,
683
+ registerEndpoint: registerEndpoint,
684
+ removeEndpoint: removeEndpoint,
685
+ listEndpoints: listEndpoints,
686
+ dispatch: dispatch,
687
+ processRetries: processRetries,
688
+ deliveries: {
689
+ list: _listDeliveries,
690
+ get: _getDelivery,
691
+ retry: _requeue,
692
+ },
693
+ dlq: {
694
+ list: function (filter) {
695
+ filter = filter || {};
696
+ return _listDeliveries({ status: "dead", limit: filter.limit, endpointId: filter.endpointId });
697
+ },
698
+ replay: _requeue,
699
+ },
700
+ close: function () { _signerCache = Object.create(null); },
701
+ };
702
+ }
703
+
704
+ function _isTruthy(v) { return v === true || v === 1 || v === "1" || v === "true"; }
705
+
706
+ module.exports = { dispatcher: dispatcher };