@blamejs/blamejs-shop 0.4.30 → 0.4.32

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 (338) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/checkout.js +8 -0
  4. package/lib/order.js +71 -11
  5. package/lib/vendor/MANIFEST.json +392 -278
  6. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  7. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  8. package/lib/vendor/blamejs/.gitignore +6 -0
  9. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  10. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  11. package/lib/vendor/blamejs/README.md +8 -6
  12. package/lib/vendor/blamejs/SECURITY.md +19 -3
  13. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  14. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  15. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  16. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  17. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  18. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  19. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  20. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  21. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  24. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  25. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  26. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  27. package/lib/vendor/blamejs/index.js +4 -0
  28. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  29. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  30. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  31. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  32. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  33. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  34. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  35. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  36. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  37. package/lib/vendor/blamejs/lib/audit.js +259 -123
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  55. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  56. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  57. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  58. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  59. package/lib/vendor/blamejs/lib/db.js +249 -99
  60. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  61. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  62. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  63. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  64. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  65. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  66. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  67. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  68. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  69. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  70. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  71. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  72. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  73. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  74. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  75. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  76. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  77. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  78. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  79. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  80. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  81. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  82. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  83. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  84. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  85. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  86. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  87. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  88. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  89. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  90. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  91. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  92. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  93. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  94. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  95. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  96. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  98. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  99. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  100. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  101. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  102. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  103. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  104. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  106. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  107. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  108. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  109. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  110. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  111. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  112. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  113. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  114. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  115. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  116. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  117. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  118. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  119. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  120. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  121. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  122. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  123. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  124. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  125. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  126. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  128. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  129. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  131. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  132. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  134. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  136. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  137. package/lib/vendor/blamejs/lib/mail.js +8 -4
  138. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  139. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  140. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  141. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  142. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  143. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  144. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  145. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  146. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  147. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  148. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  149. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  151. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  152. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  153. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  154. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  155. package/lib/vendor/blamejs/lib/observability.js +124 -0
  156. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  157. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  158. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  159. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  160. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  161. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  162. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  163. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  164. package/lib/vendor/blamejs/lib/queue.js +7 -0
  165. package/lib/vendor/blamejs/lib/redact.js +68 -11
  166. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  167. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  168. package/lib/vendor/blamejs/lib/retention.js +101 -40
  169. package/lib/vendor/blamejs/lib/router.js +212 -5
  170. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  171. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  172. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  173. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  174. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  175. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  176. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  177. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  178. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  179. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  180. package/lib/vendor/blamejs/lib/session.js +175 -77
  181. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  182. package/lib/vendor/blamejs/lib/sse.js +26 -0
  183. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  184. package/lib/vendor/blamejs/lib/static.js +177 -34
  185. package/lib/vendor/blamejs/lib/subject.js +96 -49
  186. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  187. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  189. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  190. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  191. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  192. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  193. package/lib/vendor/blamejs/package.json +2 -2
  194. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  202. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  203. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  204. package/lib/vendor/blamejs/scripts/release.js +398 -38
  205. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  206. package/lib/vendor/blamejs/test/10-state.js +140 -14
  207. package/lib/vendor/blamejs/test/20-db.js +65 -2
  208. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  209. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  210. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  211. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  212. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  213. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  214. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  215. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  222. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  223. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  224. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  225. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  226. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  227. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  228. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  229. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  230. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  231. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  232. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  233. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  234. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  235. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  236. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  237. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  238. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  240. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  250. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  251. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  272. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  273. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  274. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  275. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  279. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  280. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  293. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  304. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  310. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  312. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  313. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  314. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  315. package/lib/vendor/blamejs/test/smoke.js +79 -21
  316. package/package.json +1 -1
  317. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  318. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  319. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  320. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  321. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  322. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  323. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  324. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  325. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  326. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  327. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  328. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  329. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  330. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  331. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  332. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  335. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  336. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  337. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  338. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -50,16 +50,88 @@ var lazyRequire = require("./lazy-require");
50
50
  var { boot } = require("./log");
51
51
  var safeAsync = require("./safe-async");
52
52
  var safeJson = require("./safe-json");
53
- var safeSql = require("./safe-sql");
54
53
  var safeUrl = require("./safe-url");
55
54
  var validateOpts = require("./validate-opts");
56
55
  var { FrameworkError, ClusterError } = require("./framework-error");
57
56
 
57
+ // The external-DB schema quotes every column identifier, so Postgres
58
+ // stores them case-preserving. The boot-time chain-tip + vault-key-
59
+ // consistency statements compose through b.sql, which quotes every
60
+ // identifier by construction (double-quote on Postgres / SQLite, backtick
61
+ // on MySQL) so an unquoted fold-to-lowercase reference can't miss the
62
+ // column.
63
+
58
64
  // Lazy: vault → db → cluster forms a load-time chain, and external-db is
59
65
  // loaded before its init has run; both are safe to call once cluster
60
66
  // reaches runtime, but eager require here would deadlock the load order.
61
67
  var externalDb = lazyRequire(function () { return require("./external-db"); });
62
68
  var vault = lazyRequire(function () { return require("./vault"); });
69
+ // b.sql builder + the `?`->`$N` placeholderizer + the framework-table
70
+ // name resolver. clusterStorage requires cluster, so these are lazy to
71
+ // stay clear of the load cycle; resolved at runtime when the boot-time
72
+ // rollback / vault-key-consistency checks run.
73
+ var sql = lazyRequire(function () { return require("./sql"); });
74
+ var clusterStorage = lazyRequire(function () { return require("./cluster-storage"); });
75
+ var frameworkSchema = lazyRequire(function () { return require("./framework-schema"); });
76
+
77
+ // b.sql speaks postgres | sqlite | mysql; the cluster's configuredDialect
78
+ // is one of those (validated at init). Used so the boot-time chain-tip /
79
+ // vault-key-consistency statements emit the right identifier quoting.
80
+ function _bDialect() {
81
+ return configuredDialect === "mysql" ? "mysql"
82
+ : configuredDialect === "sqlite" ? "sqlite" : "postgres";
83
+ }
84
+
85
+ // Emit a b.sql builder + run it against the configured external-DB
86
+ // backend. b.sql emits `?` placeholders; the externalDb driver receives
87
+ // the SQL verbatim, so translate to `$N` for Postgres (passthrough for
88
+ // SQLite / MySQL).
89
+ function _runClusterQuery(builder) {
90
+ var built = builder.toSql();
91
+ return externalDb().query(
92
+ clusterStorage().placeholderize(built.sql, configuredDialect),
93
+ built.params,
94
+ { backend: configuredExternalDbBackend }
95
+ );
96
+ }
97
+
98
+ // "The framework-internal table this check needs does not exist yet" —
99
+ // the signal that a gates-only cluster (leader election wired, but
100
+ // framework state still resident in per-node SQLite without
101
+ // frameworkSchema.ensureSchema) should SKIP the boot-time rollback /
102
+ // vault-key-consistency check instead of FATAL-refusing boot. Each
103
+ // backend phrases the missing-relation fault differently, and not all
104
+ // drivers carry a stable structured code, so this matches BOTH the
105
+ // driver phrasing AND the portable code/SQLSTATE when present:
106
+ //
107
+ // - SQLite: "no such table: X"
108
+ // - Postgres: "relation "X" does not exist" (SQLSTATE 42P01)
109
+ // - MySQL: "Table 'db.X' doesn't exist" (errno 1146, SQLSTATE 42S02)
110
+ //
111
+ // The earlier message-only test recognized the SQLite/Postgres phrasing
112
+ // ("no such table" / "does not exist") but NOT MySQL's "doesn't exist"
113
+ // (the apostrophe-contracted form), so a gates-only MySQL cluster boot
114
+ // mis-fired the skip and surfaced ER_NO_SUCH_TABLE instead of completing.
115
+ function _isMissingTableError(e) {
116
+ if (!e) return false;
117
+ // Structured code / SQLSTATE first — driver-stable, locale-independent.
118
+ // mysql2-shape: e.errno === 1146 / e.code === "ER_NO_SUCH_TABLE";
119
+ // the docker-exec shim + ANSI drivers surface SQLSTATE 42S02 (MySQL) /
120
+ // 42P01 (Postgres) on e.code / e.sqlState.
121
+ var code = (e.code != null) ? String(e.code) : "";
122
+ var sqlState = (e.sqlState != null) ? String(e.sqlState) : "";
123
+ if (e.errno === 1146) return true;
124
+ if (code === "ER_NO_SUCH_TABLE" || code === "42S02" || code === "42P01" ||
125
+ sqlState === "42S02" || sqlState === "42P01") {
126
+ return true;
127
+ }
128
+ // Driver phrasing fallback. "doesn't exist" (MySQL apostrophe form) is
129
+ // covered alongside "does not exist" (Postgres) and "no such table"
130
+ // (SQLite). The MySQL message embeds the table name in quotes, so the
131
+ // bare "doesn't exist" substring is the portable anchor.
132
+ var msg = e.message || "";
133
+ return /no such table|does not exist|doesn't exist|relation .* does not exist/i.test(msg);
134
+ }
63
135
 
64
136
  var DEFAULT_LEASE_TTL = C.TIME.seconds(30);
65
137
  var DEFAULT_HEARTBEAT = C.TIME.seconds(10);
@@ -324,8 +396,16 @@ async function init(opts) {
324
396
  // framework state — the operator owns rollback detection in that
325
397
  // case.
326
398
  if (configuredExternalDbBackend) {
327
- await _checkChainTipRollback("audit", "_blamejs_audit_log", "_blamejs_audit_tip");
328
- await _checkChainTipRollback("consent", "_blamejs_consent_log", "_blamejs_consent_tip");
399
+ // Resolve the chain + tip table names through frameworkSchema so the
400
+ // configurable framework-table prefix is honored (the names are
401
+ // `_blamejs_`-prefixed and self-mapped, so the resolve is a no-op under
402
+ // the default prefix).
403
+ await _checkChainTipRollback("audit",
404
+ frameworkSchema().tableName("audit_log"), // allow:hand-rolled-sql — logical-name reference
405
+ frameworkSchema().tableName("_blamejs_audit_tip")); // allow:hand-rolled-sql — logical-name reference
406
+ await _checkChainTipRollback("consent",
407
+ frameworkSchema().tableName("consent_log"), // allow:hand-rolled-sql — logical-name reference
408
+ frameworkSchema().tableName("_blamejs_consent_tip")); // allow:hand-rolled-sql — logical-name reference
329
409
  // Vault-key consistency: every node in a cluster must hold the
330
410
  // SAME vault key. A node booting with a different key would seal
331
411
  // new writes under a key the rest of the cluster can't unseal,
@@ -373,26 +453,16 @@ async function init(opts) {
373
453
  // hash → FATAL via process.exit(1). Same posture as the
374
454
  // single-node audit.tip sidecar rollback check.
375
455
  async function _checkChainTipRollback(chainName, logTable, tipTable) {
376
- // Both tables are framework-internal constants from the call sites
377
- // (`_blamejs_audit_log`, `_blamejs_consent_log`, etc.). Validate +
378
- // quote per the framework's identifier-quoting convention so a
379
- // future rename can't silently break the query.
380
- safeSql.validateIdentifier(logTable, { allowReserved: true });
381
- safeSql.validateIdentifier(tipTable, { allowReserved: true });
382
- var qLogTable = safeSql.quoteIdentifier(logTable);
383
- var qTipTable = safeSql.quoteIdentifier(tipTable);
384
-
456
+ // Both tables are framework-internal constants resolved at the call
457
+ // sites through frameworkSchema. b.sql quotes every identifier by
458
+ // construction; the dialect-final SQL is placeholderized to `$N` for
459
+ // Postgres (passthrough for SQLite).
385
460
  var tipRows;
386
461
  try {
387
- tipRows = await externalDb().query(
388
- "SELECT atMonotonicCounter, rowHash FROM " + qTipTable +
389
- " WHERE scope = " + (configuredDialect === "postgres" ? "$1" : "?"),
390
- [chainName],
391
- { backend: configuredExternalDbBackend }
392
- );
462
+ tipRows = await _runClusterQuery(sql().select(tipTable, { dialect: _bDialect() })
463
+ .columns(["atMonotonicCounter", "rowHash"]).where("scope", chainName));
393
464
  } catch (e) {
394
- var msg = (e && e.message) || "";
395
- if (/no such table|does not exist|relation .* does not exist/i.test(msg)) {
465
+ if (_isMissingTableError(e)) {
396
466
  log(chainName + "-tip table not present — skipping rollback check (cluster gates-only mode)");
397
467
  return;
398
468
  }
@@ -406,11 +476,8 @@ async function _checkChainTipRollback(chainName, logTable, tipTable) {
406
476
  var tipCounter = Number(tip.atMonotonicCounter);
407
477
  var tipHash = tip.rowHash;
408
478
 
409
- var currentRows = await externalDb().query(
410
- "SELECT MAX(monotonicCounter) AS m FROM " + qLogTable,
411
- [],
412
- { backend: configuredExternalDbBackend }
413
- );
479
+ var currentRows = await _runClusterQuery(sql().select(logTable, { dialect: _bDialect() })
480
+ .max("monotonicCounter", "m"));
414
481
  var currentMax = (currentRows.rows && currentRows.rows[0] && currentRows.rows[0].m)
415
482
  ? Number(currentRows.rows[0].m)
416
483
  : 0;
@@ -426,12 +493,8 @@ async function _checkChainTipRollback(chainName, logTable, tipTable) {
426
493
  }
427
494
 
428
495
  if (tipHash) {
429
- var hashRows = await externalDb().query(
430
- "SELECT rowHash FROM " + qLogTable + " WHERE monotonicCounter = " +
431
- (configuredDialect === "postgres" ? "$1" : "?"),
432
- [tipCounter],
433
- { backend: configuredExternalDbBackend }
434
- );
496
+ var hashRows = await _runClusterQuery(sql().select(logTable, { dialect: _bDialect() })
497
+ .columns(["rowHash"]).where("monotonicCounter", tipCounter));
435
498
  if (hashRows.rows && hashRows.rows.length > 0) {
436
499
  var rowAtTip = hashRows.rows[0].rowHash;
437
500
  if (rowAtTip !== tipHash) {
@@ -492,12 +555,13 @@ function _vaultKeyFingerprint() {
492
555
  // non-constant, so the column is nullable and treated as epoch 0 when
493
556
  // absent on legacy rows.
494
557
  async function _ensureRotationEpochColumn() {
558
+ var stateTable = frameworkSchema().tableName("_blamejs_cluster_state"); // allow:hand-rolled-sql — logical-name reference
495
559
  try {
496
- await externalDb().query(
497
- "ALTER TABLE _blamejs_cluster_state ADD COLUMN rotationEpoch BIGINT",
498
- [],
499
- { backend: configuredExternalDbBackend }
500
- );
560
+ var alter = sql().alterTable(stateTable,
561
+ { addColumn: { name: "rotationEpoch", type: "BIGINT" } },
562
+ { dialect: _bDialect() }).sql;
563
+ await externalDb().query(clusterStorage().placeholderize(alter, configuredDialect), [],
564
+ { backend: configuredExternalDbBackend });
501
565
  } catch (_e) { /* column already exists (or table absent — caught upstream) */ }
502
566
  }
503
567
 
@@ -533,29 +597,23 @@ async function _checkVaultKeyConsistency() {
533
597
  return;
534
598
  }
535
599
  var nowMs = Date.now();
536
- var ph = configuredDialect === "postgres";
600
+ var stateTable = frameworkSchema().tableName("_blamejs_cluster_state"); // allow:hand-rolled-sql — logical-name reference
537
601
 
538
602
  // First boot: try to record THIS node's fingerprint. ON CONFLICT DO
539
603
  // NOTHING means the FIRST node to boot wins; subsequent nodes
540
604
  // observe whatever's already there. Every node then SELECTs and
541
605
  // compares — any mismatch (including ours after a losing race)
542
- // surfaces the drift.
606
+ // surfaces the drift. The scope value binds like any other param; b.sql
607
+ // folds DO NOTHING to the MySQL `scope = scope` no-op automatically.
543
608
  try {
544
- await externalDb().query(
545
- "INSERT INTO _blamejs_cluster_state " +
546
- " (scope, vaultKeyFp, recordedAt, recordedByNode) " +
547
- "VALUES ('state', " +
548
- (ph ? "$1, $2, $3" : "?, ?, ?") + ") " +
549
- "ON CONFLICT (scope) DO NOTHING",
550
- [localFp, nowMs, nodeId],
551
- { backend: configuredExternalDbBackend }
552
- );
609
+ await _runClusterQuery(sql().upsert(stateTable, { dialect: _bDialect() })
610
+ .values({ scope: "state", vaultKeyFp: localFp, recordedAt: nowMs, recordedByNode: nodeId })
611
+ .onConflict(["scope"]).doNothing());
553
612
  } catch (e) {
554
613
  // Table missing → the cluster-provider-db ensureSchema didn't run
555
614
  // (custom provider that doesn't create _blamejs_cluster_state).
556
615
  // Skip silently — same defensive posture as the audit-tip check.
557
- var msg = (e && e.message) || "";
558
- if (/no such table|does not exist|relation .* does not exist/i.test(msg)) {
616
+ if (_isMissingTableError(e)) {
559
617
  log("cluster-state table not present — skipping vault-key consistency check (custom provider)");
560
618
  return;
561
619
  }
@@ -569,12 +627,9 @@ async function _checkVaultKeyConsistency() {
569
627
 
570
628
  // Read whatever fingerprint is canonical (ours if first boot,
571
629
  // someone else's if we lost the race or are joining an existing cluster).
572
- var rows = await externalDb().query(
573
- "SELECT vaultKeyFp, recordedByNode, recordedAt, rotationEpoch FROM _blamejs_cluster_state " +
574
- "WHERE scope = 'state'",
575
- [],
576
- { backend: configuredExternalDbBackend }
577
- );
630
+ var rows = await _runClusterQuery(sql().select(stateTable, { dialect: _bDialect() })
631
+ .columns(["vaultKeyFp", "recordedByNode", "recordedAt", "rotationEpoch"])
632
+ .where("scope", "state"));
578
633
  if (!rows.rows || rows.rows.length === 0) {
579
634
  // Should never happen — we just INSERTed. Surface as fatal so the
580
635
  // condition isn't silently ignored.
@@ -628,27 +683,20 @@ async function _checkVaultKeyConsistency() {
628
683
  var priorEpoch = (canonical.rotationEpoch != null) ? Number(canonical.rotationEpoch) : 0;
629
684
  if (!isFinite(priorEpoch) || priorEpoch < 0) priorEpoch = 0;
630
685
  var nextEpoch = priorEpoch + 1;
631
- await externalDb().query(
632
- "UPDATE _blamejs_cluster_state SET " +
633
- " vaultKeyFp = " + (ph ? "$1" : "?") + ", " +
634
- " recordedAt = " + (ph ? "$2" : "?") + ", " +
635
- " recordedByNode = " + (ph ? "$3" : "?") + ", " +
636
- " rotationEpoch = " + (ph ? "$4" : "?") + " " +
637
- "WHERE scope = 'state' AND vaultKeyFp = " + (ph ? "$5" : "?"),
638
- [localFp, nowMs, nodeId, nextEpoch, canonical.vaultKeyFp],
639
- { backend: configuredExternalDbBackend }
640
- );
686
+ await _runClusterQuery(sql().update(stateTable, { dialect: _bDialect() })
687
+ .set({
688
+ vaultKeyFp: localFp, recordedAt: nowMs,
689
+ recordedByNode: nodeId, rotationEpoch: nextEpoch,
690
+ })
691
+ .where("scope", "state").where("vaultKeyFp", canonical.vaultKeyFp));
641
692
  // Re-read so the post-adopt state reflects whoever actually won the
642
693
  // advance (this node, or a peer that adopted the SAME rotated key a
643
694
  // beat earlier). A surviving mismatch here means the row now carries a
644
695
  // fingerprint that is neither the old one nor ours — a real drift that
645
696
  // the rotation declaration does not cover, so fail closed.
646
- var after = await externalDb().query(
647
- "SELECT vaultKeyFp, recordedByNode, rotationEpoch FROM _blamejs_cluster_state " +
648
- "WHERE scope = 'state'",
649
- [],
650
- { backend: configuredExternalDbBackend }
651
- );
697
+ var after = await _runClusterQuery(sql().select(stateTable, { dialect: _bDialect() })
698
+ .columns(["vaultKeyFp", "recordedByNode", "rotationEpoch"])
699
+ .where("scope", "state"));
652
700
  var post = (after.rows && after.rows[0]) || canonical;
653
701
  if (post.vaultKeyFp !== localFp) {
654
702
  throw _err("VAULT_KEY_DRIFT",
@@ -271,7 +271,30 @@ function escapeRegExp(s) {
271
271
  // lives once.
272
272
  var HEX_PAIR_RE = /^[0-9A-Fa-f]{2}$/;
273
273
 
274
+ // isAsciiAlnum — codepoint is an ASCII letter or digit (A-Z / a-z / 0-9).
275
+ // The ASCII-alphanumeric range test recurs across every byte-class parser
276
+ // (URL unreserved, XML name chars, header tokens); centralized so the three
277
+ // range literals live once rather than as a re-rolled `cc>=0x41&&...` chain.
278
+ function isAsciiAlnum(cc) {
279
+ return (cc >= 0x41 && cc <= 0x5a) || // A-Z
280
+ (cc >= 0x61 && cc <= 0x7a) || // a-z
281
+ (cc >= 0x30 && cc <= 0x39); // 0-9
282
+ }
283
+
284
+ // isUnreserved — codepoint is in the RFC 3986 §2.3 unreserved set:
285
+ // ALPHA / DIGIT / "-" / "." / "_" / "~". A percent-escape of an unreserved
286
+ // character is over-encoding the URI spec says SHOULD be decoded (§6.2.2.3).
287
+ function isUnreserved(cc) {
288
+ return isAsciiAlnum(cc) ||
289
+ cc === 0x2d || // -
290
+ cc === 0x2e || // .
291
+ cc === 0x5f || // _
292
+ cc === 0x7e; // ~
293
+ }
294
+
274
295
  module.exports = {
296
+ isAsciiAlnum: isAsciiAlnum,
297
+ isUnreserved: isUnreserved,
275
298
  hex4: hex4,
276
299
  charClass: charClass,
277
300
  fromCp: fromCp,
@@ -50,6 +50,18 @@ var audit = lazyRequire(function () { return require("./audit"); });
50
50
  var retentionMod = lazyRequire(function () { return require("./retention"); });
51
51
  var db = lazyRequire(function () { return require("./db"); });
52
52
  var cryptoField = lazyRequire(function () { return require("./crypto-field"); });
53
+ var redact = lazyRequire(function () { return require("./redact"); });
54
+
55
+ // Postures whose floor implies an outbound-DLP gate (b.redact's
56
+ // classifier presets cover exactly these regimes). Pinning one of these
57
+ // does NOT auto-install outbound DLP — the compliance coordinator holds
58
+ // no httpClient / mail / webhook handles — so set() emits a one-time
59
+ // `compliance.posture.outbound_dlp_unwired` warning when none is wired,
60
+ // so the gap is grep-able in the audit chain instead of a silent paper-
61
+ // compliance hole (CWE-200 / CWE-201 outbound data exposure).
62
+ var OUTBOUND_DLP_FLOOR_POSTURES = Object.freeze([
63
+ "hipaa", "pci-dss", "gdpr", "soc2", "fapi-2.0", "fapi-2.0-message-signing",
64
+ ]);
53
65
 
54
66
  // Recognised posture names. Aligns with the compliance-posture
55
67
  // vocabulary every guard / retention floor / etc. accepts. Operators
@@ -445,6 +457,24 @@ function set(posture) {
445
457
  "warning");
446
458
  }
447
459
  }
460
+
461
+ // Outbound-DLP wiring signal. A posture whose floor implies an
462
+ // outbound-DLP gate is being pinned, but set() cannot install the
463
+ // interceptors itself (no httpClient / mail / webhook handles). Warn
464
+ // once when nothing is wired so the gap is visible in the audit chain
465
+ // rather than a silent paper-compliance hole. Fires at most once per
466
+ // pin (set() is idempotent for the same posture).
467
+ if (OUTBOUND_DLP_FLOOR_POSTURES.indexOf(posture) !== -1) {
468
+ var dlpInstalled = false;
469
+ try { dlpInstalled = redact().isOutboundDlpInstalled() === true; }
470
+ catch (_e) { dlpInstalled = false; }
471
+ if (!dlpInstalled) {
472
+ _emitAudit("compliance.posture.outbound_dlp_unwired",
473
+ { posture: posture,
474
+ recommendation: "compliance.set does not auto-install outbound DLP — it holds no httpClient / mail / webhook handles. Call b.redact.installForPosture('" + posture + "', { httpClient, mail, webhook }) with your primitive instances so outbound payloads are classified (CWE-200 / CWE-201)." },
475
+ "warning");
476
+ }
477
+ }
448
478
  }
449
479
 
450
480
  // _applyPostureCascade — walks every primitive that
@@ -948,6 +978,25 @@ function describe(posture) {
948
978
  // + DPDP §12 + LGPD-BR Art. 18 + PIPL-CN
949
979
  // Art. 47 all require effective erasure;
950
980
  // leftover index residue defeats it.
981
+ // sealEnvelopeFloor — minimum field-level seal envelope a
982
+ // sealed-column table may declare under
983
+ // this posture: "plain" (vault.seal, no
984
+ // AAD), "aad" (AEAD-bound to table/row/
985
+ // column via b.vault.aad), or "per-row-key"
986
+ // (K_row crypto-shred). cryptoField.
987
+ // registerTable refuses a table whose
988
+ // declared envelope is below the floor when
989
+ // this posture is the globally-pinned one.
990
+ // PCI-DSS Req. 3.5/3.6 (PAN render
991
+ // unreadable, key-management binding) and
992
+ // HIPAA 45 CFR 164.312(a)(2)(iv) +
993
+ // 164.312(e)(2)(ii) (encryption that
994
+ // resists ciphertext relocation, CWE-311 /
995
+ // CWE-326) need an AAD-bound envelope at
996
+ // minimum so a DB-write attacker cannot
997
+ // copy a sealed cell between rows. Absent
998
+ // on a posture → no floor (back-compat;
999
+ // plain envelopes keep registering).
951
1000
  //
952
1001
  // This table is the single source-of-truth — duplicating values into
953
1002
  // per-primitive defaults would drift the moment a regulator updates.
@@ -957,12 +1006,22 @@ var POSTURE_DEFAULTS = Object.freeze({
957
1006
  auditChainSignedRequired: true,
958
1007
  tlsMinVersion: "TLSv1.3",
959
1008
  requireVacuumAfterErase: true,
1009
+ // 45 CFR 164.312(a)(2)(iv) + (e)(2)(ii) — ePHI encryption must
1010
+ // resist ciphertext relocation; a plain vault.seal cell can be
1011
+ // copied between rows undetected (CWE-311 / CWE-326). AAD-bound
1012
+ // envelope is the floor.
1013
+ sealEnvelopeFloor: "aad",
960
1014
  }),
961
1015
  "pci-dss": Object.freeze({
962
1016
  backupEncryptionRequired: true,
963
1017
  auditChainSignedRequired: true,
964
1018
  tlsMinVersion: "TLSv1.3",
965
1019
  requireVacuumAfterErase: false,
1020
+ // PCI-DSS v4 Req. 3.5 (PAN unreadable) + Req. 3.6 (key-management
1021
+ // binding) — the seal must bind cardholder data to its storage
1022
+ // location so a relocated ciphertext fails to verify. AAD-bound
1023
+ // envelope is the floor.
1024
+ sealEnvelopeFloor: "aad",
966
1025
  }),
967
1026
  "gdpr": Object.freeze({
968
1027
  backupEncryptionRequired: false, // GDPR Art. 32 says "appropriate" — not mandatory floor
@@ -1006,6 +1065,28 @@ var POSTURE_DEFAULTS = Object.freeze({
1006
1065
  tlsMinVersion: "TLSv1.3",
1007
1066
  requireVacuumAfterErase: true,
1008
1067
  }),
1068
+ // UK GDPR (DPA 2018 + retained EU GDPR) — Art. 17 right to erasure
1069
+ // applies identically to GDPR, including residual B-tree pages.
1070
+ "uk-gdpr": Object.freeze({
1071
+ backupEncryptionRequired: false,
1072
+ auditChainSignedRequired: true,
1073
+ tlsMinVersion: "TLSv1.3",
1074
+ requireVacuumAfterErase: true,
1075
+ }),
1076
+ // Japan APPI — deletion/cessation right with residue-cleanup floor.
1077
+ "appi-jp": Object.freeze({
1078
+ backupEncryptionRequired: false,
1079
+ auditChainSignedRequired: true,
1080
+ tlsMinVersion: "TLSv1.3",
1081
+ requireVacuumAfterErase: true,
1082
+ }),
1083
+ // Singapore PDPA — right to erasure with effectiveness floor.
1084
+ "pdpa-sg": Object.freeze({
1085
+ backupEncryptionRequired: false,
1086
+ auditChainSignedRequired: true,
1087
+ tlsMinVersion: "TLSv1.3",
1088
+ requireVacuumAfterErase: true,
1089
+ }),
1009
1090
  // v0.8.70 — 2026 effective deadlines
1010
1091
  "modpa": Object.freeze({
1011
1092
  // Maryland Online Data Privacy Act (effective 2026-10-01) —
@@ -1357,10 +1438,13 @@ var POSTURE_DEFAULTS = Object.freeze({
1357
1438
  * where `set()` would over-pin the process.
1358
1439
  *
1359
1440
  * Recognised keys per posture include `backupEncryptionRequired`,
1360
- * `auditChainSignedRequired`, `tlsMinVersion`, and
1361
- * `requireVacuumAfterErase` — the floors enforced by `b.backup`,
1362
- * `b.audit`, the TLS minimum-version gate, and `b.cryptoField`'s
1363
- * residual-erasure pass.
1441
+ * `auditChainSignedRequired`, `tlsMinVersion`,
1442
+ * `requireVacuumAfterErase`, and `sealEnvelopeFloor` — the floors
1443
+ * enforced by `b.backup`, `b.audit`, the TLS minimum-version gate,
1444
+ * `b.cryptoField`'s residual-erasure pass, and `b.cryptoField`'s
1445
+ * field-level seal-envelope gate. Keys not declared for a posture
1446
+ * return `null` (no floor), so reading `sealEnvelopeFloor` for a
1447
+ * posture that doesn't pin one is the back-compat no-op signal.
1364
1448
  *
1365
1449
  * @example
1366
1450
  * b.compliance.postureDefault("hipaa", "tlsMinVersion");
@@ -1580,9 +1664,127 @@ function fipsMode(enable) {
1580
1664
  return STATE.fipsMode;
1581
1665
  }
1582
1666
 
1667
+ // Postures whose jurisdictions restrict cross-border data transfer
1668
+ // (GDPR Art 44-46 / UK-GDPR / DPDP §16 / PIPL Art 38 / LGPD Art 33 /
1669
+ // APPI Art 28 / PDPA §26). The residency write gates (db-query local,
1670
+ // external-db backend/replica) refuse mismatched writes under these;
1671
+ // other postures observe-and-audit only.
1672
+ var CROSS_BORDER_REGULATED_POSTURES = Object.freeze([
1673
+ "gdpr", "uk-gdpr", "dpdp", "pipl-cn", "lgpd-br", "appi-jp", "pdpa-sg",
1674
+ ]);
1675
+
1676
+ /**
1677
+ * @primitive b.compliance.isCrossBorderRegulated
1678
+ * @signature b.compliance.isCrossBorderRegulated(posture)
1679
+ * @since 0.14.24
1680
+ * @compliance gdpr
1681
+ * @related b.compliance.current, b.cryptoField.declarePerRowResidency
1682
+ *
1683
+ * Returns true when `posture` is one of the cross-border regulated
1684
+ * postures (gdpr / uk-gdpr / dpdp / pipl-cn / lgpd-br / appi-jp /
1685
+ * pdpa-sg) — the jurisdictions whose transfer restrictions flip the
1686
+ * data-residency write gates from advisory to refusing. The set
1687
+ * itself is exported as `CROSS_BORDER_REGULATED_POSTURES`; this
1688
+ * helper is the membership test the local (`b.db.from`) and external
1689
+ * (`b.externalDb.query`) gates share. Non-string and unknown postures
1690
+ * return false.
1691
+ *
1692
+ * @example
1693
+ * b.compliance.isCrossBorderRegulated("gdpr"); // → true
1694
+ * b.compliance.isCrossBorderRegulated("soc2"); // → false
1695
+ * b.compliance.isCrossBorderRegulated(null); // → false
1696
+ */
1697
+ function isCrossBorderRegulated(posture) {
1698
+ if (typeof posture !== "string" || posture.length === 0) return false;
1699
+ return CROSS_BORDER_REGULATED_POSTURES.indexOf(posture) !== -1;
1700
+ }
1701
+
1702
+ // Region-tag wildcards. Both spellings mean "no residency constraint"
1703
+ // across the framework — the external-db gate uses "unrestricted" as
1704
+ // its default + wildcard, while the local db-query / external-db row
1705
+ // gates also accept "global" as the region-neutral row tag. Normalizing
1706
+ // folds both to "unrestricted" so callers reason about one wildcard.
1707
+ var _REGION_WILDCARDS = Object.freeze(["global", "unrestricted", "any", "*"]);
1708
+
1709
+ /**
1710
+ * @primitive b.compliance.normalizeRegionTag
1711
+ * @signature b.compliance.normalizeRegionTag(tag)
1712
+ * @since 0.14.27
1713
+ * @compliance gdpr
1714
+ * @related b.compliance.isRegionCompatible, b.compliance.isCrossBorderRegulated
1715
+ *
1716
+ * Canonicalize an operator-supplied residency region tag so the same
1717
+ * region declared as `"EU"`, `"eu"`, or `" Eu "` compares equal. Lower-
1718
+ * cases and trims the tag; folds the no-constraint wildcards
1719
+ * (`"global"` / `"unrestricted"` / `"any"` / `"*"`) to `"unrestricted"`.
1720
+ * Returns `null` for non-string / empty input.
1721
+ *
1722
+ * This is an ADDITIVE helper composed OVER the residency write gates
1723
+ * (`b.db.from` local, `b.externalDb.query` backend/replica) — it does
1724
+ * not change the gate internals. Callers normalize their tags with it
1725
+ * BEFORE handing them to the gate so case / wildcard drift (`"EU"` vs
1726
+ * `"eu"` vs `"global"`) doesn't read as a region mismatch.
1727
+ *
1728
+ * @example
1729
+ * b.compliance.normalizeRegionTag("EU"); // → "eu"
1730
+ * b.compliance.normalizeRegionTag(" eu "); // → "eu"
1731
+ * b.compliance.normalizeRegionTag("global"); // → "unrestricted"
1732
+ * b.compliance.normalizeRegionTag("unrestricted"); // → "unrestricted"
1733
+ * b.compliance.normalizeRegionTag(null); // → null
1734
+ */
1735
+ function normalizeRegionTag(tag) {
1736
+ if (typeof tag !== "string") return null;
1737
+ var t = tag.trim().toLowerCase();
1738
+ if (t.length === 0) return null;
1739
+ if (_REGION_WILDCARDS.indexOf(t) !== -1) return "unrestricted";
1740
+ return t;
1741
+ }
1742
+
1743
+ /**
1744
+ * @primitive b.compliance.isRegionCompatible
1745
+ * @signature b.compliance.isRegionCompatible(a, b)
1746
+ * @since 0.14.27
1747
+ * @compliance gdpr
1748
+ * @related b.compliance.normalizeRegionTag, b.compliance.isCrossBorderRegulated
1749
+ *
1750
+ * Returns `true` when two residency region tags are compatible for a
1751
+ * same-region write/replication after normalization: identical
1752
+ * normalized regions are compatible, and a wildcard (`"global"` /
1753
+ * `"unrestricted"`) on EITHER side is compatible. Different concrete
1754
+ * regions (`"eu"` vs `"us"`) are NOT compatible — a cross-border
1755
+ * transfer the operator must opt into explicitly at the gate.
1756
+ *
1757
+ * Mirrors the residency gate's compatibility rule (identical-or-
1758
+ * wildcard) but over NORMALIZED tags, so it is case- and wildcard-drift
1759
+ * insensitive. ADDITIVE helper composed over the gate — it does not
1760
+ * change `_residencyCompatible` in db-query.js / external-db.js.
1761
+ * Missing/non-string tags on either side normalize to `null`, treated
1762
+ * as "no constraint" → compatible (matches the gate's
1763
+ * `!primaryTag || !replicaTag` short-circuit).
1764
+ *
1765
+ * @example
1766
+ * b.compliance.isRegionCompatible("EU", "eu"); // → true
1767
+ * b.compliance.isRegionCompatible("eu", "global"); // → true
1768
+ * b.compliance.isRegionCompatible("unrestricted", "us"); // → true
1769
+ * b.compliance.isRegionCompatible("eu", "us"); // → false
1770
+ * b.compliance.isRegionCompatible("EU", null); // → true
1771
+ */
1772
+ function isRegionCompatible(a, b) {
1773
+ var na = normalizeRegionTag(a);
1774
+ var nb = normalizeRegionTag(b);
1775
+ if (na === null || nb === null) return true; // no constraint either side
1776
+ if (na === nb) return true; // identical region (post-normalize)
1777
+ if (na === "unrestricted" || nb === "unrestricted") return true; // wildcard either side
1778
+ return false;
1779
+ }
1780
+
1583
1781
  module.exports = {
1584
1782
  set: set,
1585
1783
  current: current,
1784
+ isCrossBorderRegulated: isCrossBorderRegulated,
1785
+ normalizeRegionTag: normalizeRegionTag,
1786
+ isRegionCompatible: isRegionCompatible,
1787
+ CROSS_BORDER_REGULATED_POSTURES: CROSS_BORDER_REGULATED_POSTURES,
1586
1788
  assert: assert,
1587
1789
  clear: clear,
1588
1790
  describe: describe,