@blamejs/blamejs-shop 0.4.31 → 0.4.33

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 (343) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +400 -282
  5. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  6. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  7. package/lib/vendor/blamejs/.gitignore +6 -0
  8. package/lib/vendor/blamejs/CHANGELOG.md +28 -0
  9. package/lib/vendor/blamejs/MIGRATING.md +55 -0
  10. package/lib/vendor/blamejs/README.md +8 -6
  11. package/lib/vendor/blamejs/SECURITY.md +19 -3
  12. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  13. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  14. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  15. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  16. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  17. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  18. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  19. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  20. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  24. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  25. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  26. package/lib/vendor/blamejs/index.js +4 -0
  27. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  28. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  29. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  30. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  31. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  32. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  33. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  34. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  35. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  36. package/lib/vendor/blamejs/lib/audit.js +259 -123
  37. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +6 -2
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +66 -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 +36 -7
  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 +210 -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/credential-hash.js +9 -0
  55. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  56. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  57. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  58. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  59. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  60. package/lib/vendor/blamejs/lib/db.js +249 -99
  61. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  62. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  63. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  64. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  65. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  66. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  67. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  68. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  69. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  70. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  71. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  72. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  73. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  74. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  75. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  76. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  77. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  78. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  79. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  80. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  81. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  82. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  83. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  84. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  85. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  86. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  87. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  88. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  89. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  90. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  91. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  92. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  93. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  95. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  96. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  98. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  99. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  100. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  101. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  102. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  103. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  104. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  105. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  106. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  107. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  109. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  110. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  111. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  112. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  113. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  114. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  115. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  116. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  117. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  118. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  119. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  120. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  121. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  122. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  123. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  124. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  125. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  126. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  127. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  129. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  132. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  135. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  136. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  137. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  138. package/lib/vendor/blamejs/lib/mail.js +8 -4
  139. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  140. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  141. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  142. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  143. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  144. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  145. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  146. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  147. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  148. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  149. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  151. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  152. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  153. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  154. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  155. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  156. package/lib/vendor/blamejs/lib/observability.js +124 -0
  157. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  158. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  159. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  160. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  161. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  162. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  163. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  164. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  165. package/lib/vendor/blamejs/lib/queue.js +7 -0
  166. package/lib/vendor/blamejs/lib/redact.js +68 -11
  167. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  168. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  169. package/lib/vendor/blamejs/lib/retention.js +117 -42
  170. package/lib/vendor/blamejs/lib/router.js +212 -5
  171. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  172. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  173. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  174. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  175. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  176. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  177. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  178. package/lib/vendor/blamejs/lib/scheduler.js +47 -12
  179. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  180. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  181. package/lib/vendor/blamejs/lib/session.js +175 -77
  182. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  183. package/lib/vendor/blamejs/lib/sse.js +26 -0
  184. package/lib/vendor/blamejs/lib/ssrf-guard.js +169 -4
  185. package/lib/vendor/blamejs/lib/static.js +177 -34
  186. package/lib/vendor/blamejs/lib/subject.js +96 -49
  187. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  189. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  190. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  191. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  192. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  193. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  194. package/lib/vendor/blamejs/package.json +2 -2
  195. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  202. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  203. package/lib/vendor/blamejs/release-notes/v0.15.7.json +43 -0
  204. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  205. package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
  206. package/lib/vendor/blamejs/scripts/release.js +398 -38
  207. package/lib/vendor/blamejs/test/00-primitives.js +168 -0
  208. package/lib/vendor/blamejs/test/10-state.js +140 -14
  209. package/lib/vendor/blamejs/test/20-db.js +65 -2
  210. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  211. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  212. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  213. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  214. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  215. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  216. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  217. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  222. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  223. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  224. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  225. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  226. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  227. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  228. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  229. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  230. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  231. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  232. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  233. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  234. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  235. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  236. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  237. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  238. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  239. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  240. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  242. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  251. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  252. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1196 -14
  253. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  259. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  267. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  275. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  276. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  277. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  278. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  282. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  283. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  286. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  287. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  288. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  296. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +59 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +362 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-watchdog-stale-settle.test.js +71 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  309. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  315. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  317. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  318. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  319. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  320. package/lib/vendor/blamejs/test/smoke.js +79 -21
  321. package/package.json +2 -2
  322. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  323. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  324. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  325. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  326. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  327. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  328. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  329. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  330. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  331. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  332. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  333. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  335. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  336. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  337. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  338. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  339. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  340. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  341. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  342. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  343. 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
@@ -564,6 +594,10 @@ function clear() {
564
594
  }
565
595
  STATE.posture = null;
566
596
  STATE.setAt = null;
597
+ // Cascade the reset the same way set() cascades the posture — otherwise a
598
+ // primitive that inherits the active posture (e.g. retention.complianceFloor)
599
+ // keeps applying the stale floor after the global posture was cleared.
600
+ _applyPostureCascade(null);
567
601
  }
568
602
 
569
603
  function _resetForTest() {
@@ -948,6 +982,25 @@ function describe(posture) {
948
982
  // + DPDP §12 + LGPD-BR Art. 18 + PIPL-CN
949
983
  // Art. 47 all require effective erasure;
950
984
  // leftover index residue defeats it.
985
+ // sealEnvelopeFloor — minimum field-level seal envelope a
986
+ // sealed-column table may declare under
987
+ // this posture: "plain" (vault.seal, no
988
+ // AAD), "aad" (AEAD-bound to table/row/
989
+ // column via b.vault.aad), or "per-row-key"
990
+ // (K_row crypto-shred). cryptoField.
991
+ // registerTable refuses a table whose
992
+ // declared envelope is below the floor when
993
+ // this posture is the globally-pinned one.
994
+ // PCI-DSS Req. 3.5/3.6 (PAN render
995
+ // unreadable, key-management binding) and
996
+ // HIPAA 45 CFR 164.312(a)(2)(iv) +
997
+ // 164.312(e)(2)(ii) (encryption that
998
+ // resists ciphertext relocation, CWE-311 /
999
+ // CWE-326) need an AAD-bound envelope at
1000
+ // minimum so a DB-write attacker cannot
1001
+ // copy a sealed cell between rows. Absent
1002
+ // on a posture → no floor (back-compat;
1003
+ // plain envelopes keep registering).
951
1004
  //
952
1005
  // This table is the single source-of-truth — duplicating values into
953
1006
  // per-primitive defaults would drift the moment a regulator updates.
@@ -957,12 +1010,22 @@ var POSTURE_DEFAULTS = Object.freeze({
957
1010
  auditChainSignedRequired: true,
958
1011
  tlsMinVersion: "TLSv1.3",
959
1012
  requireVacuumAfterErase: true,
1013
+ // 45 CFR 164.312(a)(2)(iv) + (e)(2)(ii) — ePHI encryption must
1014
+ // resist ciphertext relocation; a plain vault.seal cell can be
1015
+ // copied between rows undetected (CWE-311 / CWE-326). AAD-bound
1016
+ // envelope is the floor.
1017
+ sealEnvelopeFloor: "aad",
960
1018
  }),
961
1019
  "pci-dss": Object.freeze({
962
1020
  backupEncryptionRequired: true,
963
1021
  auditChainSignedRequired: true,
964
1022
  tlsMinVersion: "TLSv1.3",
965
1023
  requireVacuumAfterErase: false,
1024
+ // PCI-DSS v4 Req. 3.5 (PAN unreadable) + Req. 3.6 (key-management
1025
+ // binding) — the seal must bind cardholder data to its storage
1026
+ // location so a relocated ciphertext fails to verify. AAD-bound
1027
+ // envelope is the floor.
1028
+ sealEnvelopeFloor: "aad",
966
1029
  }),
967
1030
  "gdpr": Object.freeze({
968
1031
  backupEncryptionRequired: false, // GDPR Art. 32 says "appropriate" — not mandatory floor
@@ -1006,6 +1069,28 @@ var POSTURE_DEFAULTS = Object.freeze({
1006
1069
  tlsMinVersion: "TLSv1.3",
1007
1070
  requireVacuumAfterErase: true,
1008
1071
  }),
1072
+ // UK GDPR (DPA 2018 + retained EU GDPR) — Art. 17 right to erasure
1073
+ // applies identically to GDPR, including residual B-tree pages.
1074
+ "uk-gdpr": Object.freeze({
1075
+ backupEncryptionRequired: false,
1076
+ auditChainSignedRequired: true,
1077
+ tlsMinVersion: "TLSv1.3",
1078
+ requireVacuumAfterErase: true,
1079
+ }),
1080
+ // Japan APPI — deletion/cessation right with residue-cleanup floor.
1081
+ "appi-jp": Object.freeze({
1082
+ backupEncryptionRequired: false,
1083
+ auditChainSignedRequired: true,
1084
+ tlsMinVersion: "TLSv1.3",
1085
+ requireVacuumAfterErase: true,
1086
+ }),
1087
+ // Singapore PDPA — right to erasure with effectiveness floor.
1088
+ "pdpa-sg": Object.freeze({
1089
+ backupEncryptionRequired: false,
1090
+ auditChainSignedRequired: true,
1091
+ tlsMinVersion: "TLSv1.3",
1092
+ requireVacuumAfterErase: true,
1093
+ }),
1009
1094
  // v0.8.70 — 2026 effective deadlines
1010
1095
  "modpa": Object.freeze({
1011
1096
  // Maryland Online Data Privacy Act (effective 2026-10-01) —
@@ -1357,10 +1442,13 @@ var POSTURE_DEFAULTS = Object.freeze({
1357
1442
  * where `set()` would over-pin the process.
1358
1443
  *
1359
1444
  * 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.
1445
+ * `auditChainSignedRequired`, `tlsMinVersion`,
1446
+ * `requireVacuumAfterErase`, and `sealEnvelopeFloor` — the floors
1447
+ * enforced by `b.backup`, `b.audit`, the TLS minimum-version gate,
1448
+ * `b.cryptoField`'s residual-erasure pass, and `b.cryptoField`'s
1449
+ * field-level seal-envelope gate. Keys not declared for a posture
1450
+ * return `null` (no floor), so reading `sealEnvelopeFloor` for a
1451
+ * posture that doesn't pin one is the back-compat no-op signal.
1364
1452
  *
1365
1453
  * @example
1366
1454
  * b.compliance.postureDefault("hipaa", "tlsMinVersion");
@@ -1580,9 +1668,127 @@ function fipsMode(enable) {
1580
1668
  return STATE.fipsMode;
1581
1669
  }
1582
1670
 
1671
+ // Postures whose jurisdictions restrict cross-border data transfer
1672
+ // (GDPR Art 44-46 / UK-GDPR / DPDP §16 / PIPL Art 38 / LGPD Art 33 /
1673
+ // APPI Art 28 / PDPA §26). The residency write gates (db-query local,
1674
+ // external-db backend/replica) refuse mismatched writes under these;
1675
+ // other postures observe-and-audit only.
1676
+ var CROSS_BORDER_REGULATED_POSTURES = Object.freeze([
1677
+ "gdpr", "uk-gdpr", "dpdp", "pipl-cn", "lgpd-br", "appi-jp", "pdpa-sg",
1678
+ ]);
1679
+
1680
+ /**
1681
+ * @primitive b.compliance.isCrossBorderRegulated
1682
+ * @signature b.compliance.isCrossBorderRegulated(posture)
1683
+ * @since 0.14.24
1684
+ * @compliance gdpr
1685
+ * @related b.compliance.current, b.cryptoField.declarePerRowResidency
1686
+ *
1687
+ * Returns true when `posture` is one of the cross-border regulated
1688
+ * postures (gdpr / uk-gdpr / dpdp / pipl-cn / lgpd-br / appi-jp /
1689
+ * pdpa-sg) — the jurisdictions whose transfer restrictions flip the
1690
+ * data-residency write gates from advisory to refusing. The set
1691
+ * itself is exported as `CROSS_BORDER_REGULATED_POSTURES`; this
1692
+ * helper is the membership test the local (`b.db.from`) and external
1693
+ * (`b.externalDb.query`) gates share. Non-string and unknown postures
1694
+ * return false.
1695
+ *
1696
+ * @example
1697
+ * b.compliance.isCrossBorderRegulated("gdpr"); // → true
1698
+ * b.compliance.isCrossBorderRegulated("soc2"); // → false
1699
+ * b.compliance.isCrossBorderRegulated(null); // → false
1700
+ */
1701
+ function isCrossBorderRegulated(posture) {
1702
+ if (typeof posture !== "string" || posture.length === 0) return false;
1703
+ return CROSS_BORDER_REGULATED_POSTURES.indexOf(posture) !== -1;
1704
+ }
1705
+
1706
+ // Region-tag wildcards. Both spellings mean "no residency constraint"
1707
+ // across the framework — the external-db gate uses "unrestricted" as
1708
+ // its default + wildcard, while the local db-query / external-db row
1709
+ // gates also accept "global" as the region-neutral row tag. Normalizing
1710
+ // folds both to "unrestricted" so callers reason about one wildcard.
1711
+ var _REGION_WILDCARDS = Object.freeze(["global", "unrestricted", "any", "*"]);
1712
+
1713
+ /**
1714
+ * @primitive b.compliance.normalizeRegionTag
1715
+ * @signature b.compliance.normalizeRegionTag(tag)
1716
+ * @since 0.14.27
1717
+ * @compliance gdpr
1718
+ * @related b.compliance.isRegionCompatible, b.compliance.isCrossBorderRegulated
1719
+ *
1720
+ * Canonicalize an operator-supplied residency region tag so the same
1721
+ * region declared as `"EU"`, `"eu"`, or `" Eu "` compares equal. Lower-
1722
+ * cases and trims the tag; folds the no-constraint wildcards
1723
+ * (`"global"` / `"unrestricted"` / `"any"` / `"*"`) to `"unrestricted"`.
1724
+ * Returns `null` for non-string / empty input.
1725
+ *
1726
+ * This is an ADDITIVE helper composed OVER the residency write gates
1727
+ * (`b.db.from` local, `b.externalDb.query` backend/replica) — it does
1728
+ * not change the gate internals. Callers normalize their tags with it
1729
+ * BEFORE handing them to the gate so case / wildcard drift (`"EU"` vs
1730
+ * `"eu"` vs `"global"`) doesn't read as a region mismatch.
1731
+ *
1732
+ * @example
1733
+ * b.compliance.normalizeRegionTag("EU"); // → "eu"
1734
+ * b.compliance.normalizeRegionTag(" eu "); // → "eu"
1735
+ * b.compliance.normalizeRegionTag("global"); // → "unrestricted"
1736
+ * b.compliance.normalizeRegionTag("unrestricted"); // → "unrestricted"
1737
+ * b.compliance.normalizeRegionTag(null); // → null
1738
+ */
1739
+ function normalizeRegionTag(tag) {
1740
+ if (typeof tag !== "string") return null;
1741
+ var t = tag.trim().toLowerCase();
1742
+ if (t.length === 0) return null;
1743
+ if (_REGION_WILDCARDS.indexOf(t) !== -1) return "unrestricted";
1744
+ return t;
1745
+ }
1746
+
1747
+ /**
1748
+ * @primitive b.compliance.isRegionCompatible
1749
+ * @signature b.compliance.isRegionCompatible(a, b)
1750
+ * @since 0.14.27
1751
+ * @compliance gdpr
1752
+ * @related b.compliance.normalizeRegionTag, b.compliance.isCrossBorderRegulated
1753
+ *
1754
+ * Returns `true` when two residency region tags are compatible for a
1755
+ * same-region write/replication after normalization: identical
1756
+ * normalized regions are compatible, and a wildcard (`"global"` /
1757
+ * `"unrestricted"`) on EITHER side is compatible. Different concrete
1758
+ * regions (`"eu"` vs `"us"`) are NOT compatible — a cross-border
1759
+ * transfer the operator must opt into explicitly at the gate.
1760
+ *
1761
+ * Mirrors the residency gate's compatibility rule (identical-or-
1762
+ * wildcard) but over NORMALIZED tags, so it is case- and wildcard-drift
1763
+ * insensitive. ADDITIVE helper composed over the gate — it does not
1764
+ * change `_residencyCompatible` in db-query.js / external-db.js.
1765
+ * Missing/non-string tags on either side normalize to `null`, treated
1766
+ * as "no constraint" → compatible (matches the gate's
1767
+ * `!primaryTag || !replicaTag` short-circuit).
1768
+ *
1769
+ * @example
1770
+ * b.compliance.isRegionCompatible("EU", "eu"); // → true
1771
+ * b.compliance.isRegionCompatible("eu", "global"); // → true
1772
+ * b.compliance.isRegionCompatible("unrestricted", "us"); // → true
1773
+ * b.compliance.isRegionCompatible("eu", "us"); // → false
1774
+ * b.compliance.isRegionCompatible("EU", null); // → true
1775
+ */
1776
+ function isRegionCompatible(a, b) {
1777
+ var na = normalizeRegionTag(a);
1778
+ var nb = normalizeRegionTag(b);
1779
+ if (na === null || nb === null) return true; // no constraint either side
1780
+ if (na === nb) return true; // identical region (post-normalize)
1781
+ if (na === "unrestricted" || nb === "unrestricted") return true; // wildcard either side
1782
+ return false;
1783
+ }
1784
+
1583
1785
  module.exports = {
1584
1786
  set: set,
1585
1787
  current: current,
1788
+ isCrossBorderRegulated: isCrossBorderRegulated,
1789
+ normalizeRegionTag: normalizeRegionTag,
1790
+ isRegionCompatible: isRegionCompatible,
1791
+ CROSS_BORDER_REGULATED_POSTURES: CROSS_BORDER_REGULATED_POSTURES,
1586
1792
  assert: assert,
1587
1793
  clear: clear,
1588
1794
  describe: describe,