@blamejs/blamejs-shop 0.4.31 → 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 (336) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/vendor/MANIFEST.json +392 -278
  4. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  5. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  6. package/lib/vendor/blamejs/.gitignore +6 -0
  7. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  8. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  9. package/lib/vendor/blamejs/README.md +8 -6
  10. package/lib/vendor/blamejs/SECURITY.md +19 -3
  11. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  12. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  13. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  14. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  15. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  16. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  17. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  18. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  19. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  20. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  23. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  24. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  25. package/lib/vendor/blamejs/index.js +4 -0
  26. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  27. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  28. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  29. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  30. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  31. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  32. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  33. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  34. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  35. package/lib/vendor/blamejs/lib/audit.js +259 -123
  36. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  37. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  38. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  39. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  40. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  41. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  42. package/lib/vendor/blamejs/lib/cache.js +174 -105
  43. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  44. package/lib/vendor/blamejs/lib/cli.js +19 -14
  45. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  46. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  47. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  48. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  49. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  50. package/lib/vendor/blamejs/lib/consent.js +82 -29
  51. package/lib/vendor/blamejs/lib/constants.js +27 -11
  52. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  53. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  54. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  55. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  56. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  57. package/lib/vendor/blamejs/lib/db.js +249 -99
  58. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  59. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  60. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  61. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  62. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  63. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  64. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  65. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  66. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  67. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  68. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  69. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  70. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  71. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  72. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  73. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  74. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  75. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  76. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  77. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  78. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  79. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  80. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  81. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  82. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  83. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  84. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  85. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  86. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  87. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  88. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  89. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  90. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  91. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  92. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  93. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  95. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  96. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  97. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  98. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  99. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  100. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  101. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  102. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  103. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  104. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  107. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  109. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  110. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  111. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  112. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  113. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  114. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  115. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  116. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  117. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  118. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  119. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  120. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  121. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  122. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  123. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  125. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  132. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  133. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  135. package/lib/vendor/blamejs/lib/mail.js +8 -4
  136. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  137. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  138. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  139. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  141. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  142. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  143. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  144. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  145. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  146. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  147. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  148. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  149. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  150. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  151. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  152. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  153. package/lib/vendor/blamejs/lib/observability.js +124 -0
  154. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  155. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  156. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  157. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  158. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  159. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  160. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  161. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  162. package/lib/vendor/blamejs/lib/queue.js +7 -0
  163. package/lib/vendor/blamejs/lib/redact.js +68 -11
  164. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  165. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  166. package/lib/vendor/blamejs/lib/retention.js +101 -40
  167. package/lib/vendor/blamejs/lib/router.js +212 -5
  168. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  169. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  170. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  171. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  172. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  173. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  174. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  175. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  176. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  177. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  178. package/lib/vendor/blamejs/lib/session.js +175 -77
  179. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  180. package/lib/vendor/blamejs/lib/sse.js +26 -0
  181. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  182. package/lib/vendor/blamejs/lib/static.js +177 -34
  183. package/lib/vendor/blamejs/lib/subject.js +96 -49
  184. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  185. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  186. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  187. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  188. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  189. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  190. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  191. package/lib/vendor/blamejs/package.json +2 -2
  192. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  193. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  194. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  200. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  201. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  202. package/lib/vendor/blamejs/scripts/release.js +398 -38
  203. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  204. package/lib/vendor/blamejs/test/10-state.js +140 -14
  205. package/lib/vendor/blamejs/test/20-db.js +65 -2
  206. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  207. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  208. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  209. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  210. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  211. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  212. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  213. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  214. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  215. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  220. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  221. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  222. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  223. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  224. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  225. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  226. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  227. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  228. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  229. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  230. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  231. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  232. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  233. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  234. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  235. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  236. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  237. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  238. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  248. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  258. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  270. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  271. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  272. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  273. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  277. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  278. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  291. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  302. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  308. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  310. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  311. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  312. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  313. package/lib/vendor/blamejs/test/smoke.js +79 -21
  314. package/package.json +1 -1
  315. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  316. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  317. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  318. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  319. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  320. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  321. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  322. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  323. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  324. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  325. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  326. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  327. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  328. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  329. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  330. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  331. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  332. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  334. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  335. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  336. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -7510,6 +7510,53 @@ async function testOAuthVerifyIdTokenRoundTrip() {
7510
7510
  threw = null;
7511
7511
  try { await oa.verifyIdToken(wrongIss, {}); } catch (e) { threw = e; }
7512
7512
  check("verifyIdToken: wrong iss rejected", threw && threw.code === "auth-oauth/iss-mismatch");
7513
+
7514
+ // #137 — skipExpCheck is ONLY valid for OIDC Back-Channel-Logout tokens
7515
+ // (no exp claim per §2.4). A caller must NOT be able to disable exp
7516
+ // validation on a regular ID token — that verifies an expired/replayed
7517
+ // credential clean. The option is gated to tokens carrying the logout
7518
+ // event claim, and even then bounded by an iat freshness floor.
7519
+ var LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout";
7520
+ threw = null;
7521
+ try { await oa.verifyIdToken(expired, { skipExpCheck: true }); } catch (e) { threw = e; }
7522
+ check("#137 verifyIdToken: skipExpCheck on a non-logout token is rejected",
7523
+ threw && threw.code === "auth-oauth/skip-exp-check-not-allowed");
7524
+
7525
+ // A genuine logout token (events claim, recent iat, no exp) still verifies
7526
+ // under skipExpCheck — the internal back-channel-logout path relies on it.
7527
+ var freshLogout = _signRs256({
7528
+ iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec,
7529
+ events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
7530
+ }, { kid: "test-kid-1" }, kp.privateKey);
7531
+ var logoutOk = await oa.verifyIdToken(freshLogout, { skipExpCheck: true, skipNonceCheck: true });
7532
+ check("#137 verifyIdToken: a fresh logout token verifies under skipExpCheck",
7533
+ logoutOk && logoutOk.claims && logoutOk.claims.sub === "user-1");
7534
+
7535
+ // A stale logout token (iat older than the freshness floor) is rejected —
7536
+ // skipExpCheck removes exp, so iat is the only freshness bound.
7537
+ var staleLogout = _signRs256({
7538
+ iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec - 86400,
7539
+ events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
7540
+ }, { kid: "test-kid-1" }, kp.privateKey);
7541
+ threw = null;
7542
+ try { await oa.verifyIdToken(staleLogout, { skipExpCheck: true, skipNonceCheck: true }); } catch (e) { threw = e; }
7543
+ check("#137 verifyIdToken: a stale logout token is rejected on iat floor",
7544
+ threw && threw.code === "auth-oauth/logout-token-stale");
7545
+
7546
+ // The iat floor must honor a configured maxAgeSec (back-channel-logout
7547
+ // deployments can widen the replay window). A 10-min-old logout token is
7548
+ // rejected under the 5-min default but accepted under maxAgeSec: 20 min.
7549
+ var agedLogout = _signRs256({
7550
+ iss: issuerUrl, sub: "user-1", aud: clientId, iat: nowSec - 600,
7551
+ events: (function () { var e = {}; e[LOGOUT_EVENT] = {}; return e; })(),
7552
+ }, { kid: "test-kid-1" }, kp.privateKey);
7553
+ threw = null;
7554
+ try { await oa.verifyIdToken(agedLogout, { skipExpCheck: true, skipNonceCheck: true }); } catch (e) { threw = e; }
7555
+ check("#137 verifyIdToken: a 10-min-old logout token is stale under the 5-min default",
7556
+ threw && threw.code === "auth-oauth/logout-token-stale");
7557
+ var agedOk = await oa.verifyIdToken(agedLogout, { skipExpCheck: true, skipNonceCheck: true, maxAgeSec: 1200 });
7558
+ check("#137 verifyIdToken: the configured maxAgeSec widens the iat freshness window",
7559
+ agedOk && agedOk.claims && agedOk.claims.sub === "user-1");
7513
7560
  } finally { server.close(); }
7514
7561
  }
7515
7562
 
@@ -12418,6 +12465,74 @@ function testErrorsPageHookOptsRejectNonFunction() {
12418
12465
  check("renderHtml non-function throws at config time", threwHtml);
12419
12466
  }
12420
12467
 
12468
+ async function testErrorsPageAuditRedactsSecretsInStackAndReason() {
12469
+ // CWE-532: a secret embedded in an exception message/stack (e.g. a
12470
+ // database connection string a driver dropped into Error.message) must
12471
+ // NOT be persisted verbatim in the tamper-evident audit chain. The 5xx
12472
+ // audit emission goes through audit.safeEmit, whose b.redact.redact()
12473
+ // pass scrubs connection-string / JWT / PEM / AWS-key shapes — including
12474
+ // nested metadata.stack — before the record reaches the chain.
12475
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-errpg-"));
12476
+ try {
12477
+ await setupTestDb(tmpDir);
12478
+ // The default audit action is "request.error"; its namespace is not a
12479
+ // framework namespace, so register it the way an operator would.
12480
+ b.audit.registerNamespace("request");
12481
+
12482
+ var secret = "postgres://user:s3cr3t@db.internal/app";
12483
+ var handler = b.errorPage.create({ mode: "prod" }); // audit on by default
12484
+ var req = { method: "POST", url: "/api/widget", headers: { accept: "application/json" }, id: "req-redact-1" };
12485
+ var res = _makeFakeRes();
12486
+ // Generic Error → 500. Its message (and therefore its stack) carries
12487
+ // the secret-shaped connection string.
12488
+ handler(new Error("connect failed for " + secret), req, res);
12489
+
12490
+ // Response behavior is unchanged: 500 status, JSON body, and the
12491
+ // generic message — the original (secret-bearing) message never
12492
+ // reaches the client on an unclassified 500.
12493
+ check("audit-redact: response is 500", res.statusCode === 500);
12494
+ check("audit-redact: response is JSON", /application\/json/.test(res.headers["Content-Type"]));
12495
+ var payload = JSON.parse(res.body);
12496
+ check("audit-redact: response hides operator message", res.body.indexOf("s3cr3t") === -1);
12497
+ check("audit-redact: response shows generic message",
12498
+ payload.error.message === "Internal Server Error");
12499
+
12500
+ await b.audit.flush();
12501
+ await helpers.waitUntil(async function () {
12502
+ var rows = await b.audit.query({ action: "request.error" });
12503
+ return rows.length >= 1;
12504
+ }, { timeoutMs: 5000, label: "errors-page audit: request.error row persisted" });
12505
+
12506
+ var events = await b.audit.query({ action: "request.error" });
12507
+ check("audit-redact: 5xx audit row persisted", events.length === 1);
12508
+
12509
+ var row = events[0];
12510
+ var meta = row.metadata;
12511
+ if (typeof meta === "string") meta = JSON.parse(meta);
12512
+
12513
+ // The whole persisted row must be free of the secret — neither the
12514
+ // nested metadata.stack nor the reason (original error message) may
12515
+ // carry it verbatim.
12516
+ var rowJson = JSON.stringify(row);
12517
+ check("audit-redact: secret absent from full persisted row",
12518
+ rowJson.indexOf("s3cr3t") === -1);
12519
+ check("audit-redact: secret absent from metadata.stack",
12520
+ !meta || typeof meta.stack !== "string" || meta.stack.indexOf("s3cr3t") === -1);
12521
+ check("audit-redact: secret absent from reason",
12522
+ typeof row.reason !== "string" || row.reason.indexOf("s3cr3t") === -1);
12523
+ // The connection-string redactor leaves a marker so triage stays
12524
+ // useful — confirm the field was scanned, not merely dropped.
12525
+ check("audit-redact: stack carries the connection-string marker",
12526
+ !!meta && typeof meta.stack === "string" &&
12527
+ meta.stack.indexOf("[REDACTED-CONN-STRING]") !== -1);
12528
+ // Non-secret triage fields survive redaction.
12529
+ check("audit-redact: non-secret metadata preserved",
12530
+ !!meta && meta.status === 500 && meta.method === "POST" && meta.url === "/api/widget");
12531
+ } finally {
12532
+ await teardownTestDb(tmpDir);
12533
+ }
12534
+ }
12535
+
12421
12536
  // ---- log ----
12422
12537
  //
12423
12538
  // Each test creates an instance with a captured-buffer destination so
@@ -18347,6 +18462,7 @@ async function run() {
18347
18462
  testErrorsPageJsonFormatterTakesOverBody();
18348
18463
  testErrorsPageRenderHtmlHook();
18349
18464
  testErrorsPageHookOptsRejectNonFunction();
18465
+ await testErrorsPageAuditRedactsSecretsInStackAndReason();
18350
18466
  // log — structured JSON logging with request-id correlation
18351
18467
  testLogSurface();
18352
18468
  testLogEmitsJsonLineToStdout();
@@ -19048,6 +19164,7 @@ module.exports = {
19048
19164
  testErrorsPageJsonFormatterTakesOverBody: testErrorsPageJsonFormatterTakesOverBody,
19049
19165
  testErrorsPageRenderHtmlHook: testErrorsPageRenderHtmlHook,
19050
19166
  testErrorsPageHookOptsRejectNonFunction: testErrorsPageHookOptsRejectNonFunction,
19167
+ testErrorsPageAuditRedactsSecretsInStackAndReason: testErrorsPageAuditRedactsSecretsInStackAndReason,
19051
19168
  testLogSurface: testLogSurface,
19052
19169
  testLogEmitsJsonLineToStdout: testLogEmitsJsonLineToStdout,
19053
19170
  testLogRoutesErrorAndFatalToStderr: testLogRoutesErrorAndFatalToStderr,
@@ -372,9 +372,16 @@ async function testClusterProviderMysqlDialect() {
372
372
  check("mysql provider: emitted INSERT...ON DUPLICATE KEY",
373
373
  sqlSeen.some(function (e) { return /ON DUPLICATE KEY UPDATE/.test(e.sql); }));
374
374
  check("mysql provider: uses ? placeholders not $1",
375
- sqlSeen.some(function (e) { return /VALUES\s*\(\s*'leader',\s*\?,/.test(e.sql); }));
375
+ sqlSeen.some(function (e) {
376
+ // The provider composes the upsert through b.sql, which binds
377
+ // every value (scope included) as a `?` rather than inlining
378
+ // the 'leader' literal — so the MySQL form is an all-`?` VALUES
379
+ // list with no Postgres `$1` numbering.
380
+ return /INSERT INTO _blamejs_leader[\s\S]*VALUES\s*\(\s*\?(?:\s*,\s*\?)*\s*\)/.test(e.sql) &&
381
+ !/\$1/.test(e.sql);
382
+ }));
376
383
  check("mysql provider: gates fencingToken with IF(expiresAt < ?, ...)",
377
- sqlSeen.some(function (e) { return /fencingToken = IF\(expiresAt < \?, fencingToken \+ 1, fencingToken\)/.test(e.sql); }));
384
+ sqlSeen.some(function (e) { return /`fencingToken` = IF\(`expiresAt` < \?, `fencingToken` \+ 1, `fencingToken`\)/.test(e.sql); }));
378
385
 
379
386
  // Second node blocked while A holds.
380
387
  var leaseB = await pB.acquireLease("node-B", b.constants.TIME.seconds(30));
@@ -558,20 +565,137 @@ function testFrameworkSchemaTableNameMapping() {
558
565
  b.frameworkSchema.tableName("custom_table") === "custom_table");
559
566
  check("LOCAL_TO_EXTERNAL is frozen",
560
567
  Object.isFrozen(b.frameworkSchema.LOCAL_TO_EXTERNAL));
568
+
569
+ // ---- Configurable table prefix ----
570
+ // Default prefix is byte-identical to the historical names.
571
+ check("getTablePrefix default is _blamejs_",
572
+ b.frameworkSchema.getTablePrefix() === "_blamejs_");
573
+ check("DEFAULT_TABLE_PREFIX is _blamejs_",
574
+ b.frameworkSchema.DEFAULT_TABLE_PREFIX === "_blamejs_");
575
+
576
+ // setTablePrefix throws config-time on a non-identifier prefix.
577
+ var threwEmpty = false;
578
+ try { b.frameworkSchema.setTablePrefix(""); }
579
+ catch (e) { threwEmpty = e.code === "framework-schema/invalid-prefix"; }
580
+ check("setTablePrefix('') throws invalid-prefix", threwEmpty);
581
+ var threwBad = false;
582
+ try { b.frameworkSchema.setTablePrefix("bad-prefix!"); }
583
+ catch (e) { threwBad = e.code === "framework-schema/invalid-prefix"; }
584
+ check("setTablePrefix('bad-prefix!') throws invalid-prefix", threwBad);
585
+ var threwType = false;
586
+ try { b.frameworkSchema.setTablePrefix(123); }
587
+ catch (e) { threwType = e.code === "framework-schema/invalid-prefix"; }
588
+ check("setTablePrefix(non-string) throws invalid-prefix", threwType);
589
+ // A bad prefix must not have mutated state.
590
+ check("getTablePrefix unchanged after rejected prefix",
591
+ b.frameworkSchema.getTablePrefix() === "_blamejs_");
592
+
593
+ // A valid prefix swaps the leading default across every framework name.
594
+ try {
595
+ b.frameworkSchema.setTablePrefix("acme_");
596
+ check("getTablePrefix reflects configured prefix",
597
+ b.frameworkSchema.getTablePrefix() === "acme_");
598
+ check("tableName('audit_log') honors configured prefix",
599
+ b.frameworkSchema.tableName("audit_log") === "acme_audit_log");
600
+ check("tableName('_blamejs_sessions') honors configured prefix",
601
+ b.frameworkSchema.tableName("_blamejs_sessions") === "acme_sessions");
602
+ check("tableName(unknown) still identity under configured prefix",
603
+ b.frameworkSchema.tableName("custom_table") === "custom_table");
604
+ } finally {
605
+ // Restore the default so the rest of the suite (and the running DB)
606
+ // see the historical names — this state is module-global.
607
+ b.frameworkSchema.setTablePrefix("_blamejs_");
608
+ }
609
+ check("getTablePrefix restored to default",
610
+ b.frameworkSchema.getTablePrefix() === "_blamejs_");
561
611
  }
562
612
 
563
- function testFrameworkSchemaInvalidDialect() {
564
- // The throw can come either sync (config validation) or async (per-
565
- // table execution). The .then(success, failure) handler covers both.
566
- return b.frameworkSchema.ensureSchema({
567
- externalDbBackend: "ops",
568
- dialect: "mysql",
569
- }).then(function () {
570
- check("ensureSchema mysql rejects sync or async succeeded unexpectedly", false);
571
- }, function (e) {
572
- check("ensureSchema rejects unsupported dialect (mysql)",
573
- e.code === "framework-schema/unsupported-dialect");
574
- });
613
+ // resolveTables rewrites bare framework table names to their prefixed
614
+ // external form ONLY in cluster mode, and honors a configured tablePrefix
615
+ // (set config-time). Proves _REWRITE_TABLE is prefix-aware (rebuilt when the
616
+ // prefix changes), so cluster-mode DML targets the same prefixed tables the
617
+ // DDL builders create — both the local-mapped names (audit_log) and the
618
+ // already-`_blamejs_`-prefixed identity names (_blamejs_scheduler_ticks),
619
+ // which only need rewriting under a custom prefix.
620
+ async function testClusterStoragePrefixRewrite() {
621
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-clpfx-"));
622
+ var dbPath = path.join(tmpDir, "cluster.db");
623
+ var driver = _makeSqliteDriver(dbPath);
624
+ try {
625
+ b.externalDb.init({
626
+ backends: {
627
+ "ops": { connect: driver.connect, query: driver.query, close: driver.close },
628
+ },
629
+ });
630
+ b.cluster._resetForTest();
631
+ await b.cluster.init({
632
+ nodeId: "pfx-node-1",
633
+ externalDbBackend: "ops",
634
+ dialect: "sqlite",
635
+ leaseTtl: b.constants.TIME.seconds(30),
636
+ heartbeatInterval: b.constants.TIME.seconds(10),
637
+ });
638
+
639
+ // Default prefix: local-mapped name rewrites to its _blamejs_ external;
640
+ // identity-mapped name passes through unchanged.
641
+ check("resolveTables rewrites audit_log under default prefix",
642
+ b.clusterStorage.resolveTables("SELECT id FROM audit_log WHERE n > ?") ===
643
+ "SELECT id FROM _blamejs_audit_log WHERE n > ?");
644
+ check("resolveTables leaves _blamejs_scheduler_ticks under default prefix",
645
+ b.clusterStorage.resolveTables("DELETE FROM _blamejs_scheduler_ticks") ===
646
+ "DELETE FROM _blamejs_scheduler_ticks");
647
+
648
+ // Custom prefix: BOTH the local-mapped and the identity-mapped names
649
+ // rewrite to <prefix>* so cluster DML matches the prefixed DDL. App
650
+ // tables are never rewritten.
651
+ try {
652
+ b.frameworkSchema.setTablePrefix("acme_");
653
+ check("resolveTables rewrites audit_log to the configured prefix",
654
+ b.clusterStorage.resolveTables("SELECT id FROM audit_log WHERE n > ?") ===
655
+ "SELECT id FROM acme_audit_log WHERE n > ?");
656
+ check("resolveTables rewrites identity name _blamejs_scheduler_ticks to the prefix",
657
+ b.clusterStorage.resolveTables("DELETE FROM _blamejs_scheduler_ticks") ===
658
+ "DELETE FROM acme_scheduler_ticks");
659
+ check("resolveTables leaves a non-framework table untouched under prefix",
660
+ b.clusterStorage.resolveTables("SELECT * FROM my_app_table") ===
661
+ "SELECT * FROM my_app_table");
662
+ } finally {
663
+ b.frameworkSchema.setTablePrefix("_blamejs_");
664
+ }
665
+ } finally {
666
+ try { await b.cluster.shutdown(); } catch (_e) {}
667
+ b.cluster._resetForTest();
668
+ try { await b.externalDb.shutdown(); } catch (_e) {}
669
+ driver._close();
670
+ fs.rmSync(tmpDir, { recursive: true, force: true });
671
+ }
672
+ }
673
+
674
+ async function testFrameworkSchemaInvalidDialect() {
675
+ // postgres, sqlite, and mysql are the supported dialects. A dialect
676
+ // outside that set is rejected at the config-validation gate with
677
+ // framework-schema/unsupported-dialect, BEFORE any backend dispatch — so
678
+ // an operator typo surfaces at boot. (The mysql DDL path itself is proven
679
+ // end-to-end against a live server in framework-schema-mysql.test.js.)
680
+ var unsupportedErr = null;
681
+ try {
682
+ await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "oracle" });
683
+ } catch (e) { unsupportedErr = e; }
684
+ check("ensureSchema rejects an unsupported dialect at the config gate",
685
+ unsupportedErr !== null &&
686
+ unsupportedErr.code === "framework-schema/unsupported-dialect");
687
+
688
+ // mysql is now a SUPPORTED dialect, so it passes the dialect-validation
689
+ // gate — a mysql ensureSchema no longer fails with the unsupported-dialect
690
+ // code (it proceeds to backend dispatch; the live DDL is covered in the
691
+ // integration suite). Assert the gate no longer rejects it.
692
+ var mysqlErr = null;
693
+ try {
694
+ await b.frameworkSchema.ensureSchema({ externalDbBackend: "ops", dialect: "mysql" });
695
+ } catch (e) { mysqlErr = e; }
696
+ check("ensureSchema accepts mysql at the dialect gate (no unsupported-dialect rejection)",
697
+ mysqlErr === null ||
698
+ mysqlErr.code !== "framework-schema/unsupported-dialect");
575
699
  }
576
700
 
577
701
  // ---- run() ----
@@ -597,6 +721,7 @@ async function run() {
597
721
  // framework-schema (DDL emitter + table-name resolver)
598
722
  await testFrameworkSchemaEnsure();
599
723
  testFrameworkSchemaTableNameMapping();
724
+ await testClusterStoragePrefixRewrite();
600
725
  await testFrameworkSchemaInvalidDialect();
601
726
  }
602
727
 
@@ -618,5 +743,6 @@ module.exports = {
618
743
  testClusterInitAndRequireLeader: testClusterInitAndRequireLeader,
619
744
  testFrameworkSchemaEnsure: testFrameworkSchemaEnsure,
620
745
  testFrameworkSchemaTableNameMapping: testFrameworkSchemaTableNameMapping,
746
+ testClusterStoragePrefixRewrite: testClusterStoragePrefixRewrite,
621
747
  testFrameworkSchemaInvalidDialect: testFrameworkSchemaInvalidDialect,
622
748
  };
@@ -46,7 +46,7 @@ async function testDbBasic() {
46
46
  var rawRow = rawStmt.get(inserted._id);
47
47
  check("on-disk email is sealed", typeof rawRow.email === "string" && rawRow.email.startsWith("vault:"));
48
48
  check("on-disk name is sealed", typeof rawRow.name === "string" && rawRow.name.startsWith("vault:"));
49
- check("emailHash is computed", typeof rawRow.emailHash === "string" && rawRow.emailHash.length === 128);
49
+ check("emailHash is computed", typeof rawRow.emailHash === "string" && /^[0-9a-f]{32,}$/.test(rawRow.emailHash));
50
50
  check("emailHash is normalized", rawRow.emailHash === b.db.hashFor("users", "email", "ALICE@example.com"));
51
51
 
52
52
  // Query via plain field name (sealed → translated to emailHash)
@@ -510,7 +510,8 @@ async function testEncryptedTmpfsCorruptionAutoRecovers() {
510
510
  // must be discarded and db.enc re-decrypted — boot must succeed.
511
511
  setTestPassphraseEnv();
512
512
  b.audit._resetForTest();
513
- await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema });
513
+ await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema,
514
+ allowNonTmpfsTmpDir: true }); // .test-output scratch is not a tmpfs mount (v0.15.0 refusal opt-out)
514
515
  var row = b.db.prepare("SELECT v FROM recovery_t WHERE _id = ?").get("r1");
515
516
  check("corrupt tmpfs working copy auto-recovers from db.enc (no crash loop)",
516
517
  row && row.v === "survives");
@@ -583,6 +584,7 @@ async function testTmpfsLowSpaceRefusesWritesFailClear() {
583
584
  await b.db.init({
584
585
  dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: schema,
585
586
  minFreeBytes: BYTES.mib(16), _statfsForTest: fakeStatfs,
587
+ allowNonTmpfsTmpDir: true, // .test-output scratch is not a tmpfs mount (v0.15.0 refusal opt-out)
586
588
  });
587
589
  check("low-space test runs in encrypted mode", b.db.getMode() === "encrypted");
588
590
 
@@ -643,6 +645,66 @@ async function testExitHandlerRegisteredOnce() {
643
645
  check("exit handler registered once across init/close cycles (no listener leak)", added <= 1);
644
646
  }
645
647
 
648
+ async function testEncryptedNonTmpfsTmpDirRefusedByDefault() {
649
+ // v0.15.0: encrypted mode REFUSES a tmpDir that does not resolve under a
650
+ // recognized tmpfs mount (a persistent-disk working copy leaks the
651
+ // decrypted DB into backups / forensic images). The documented opt-out
652
+ // is allowNonTmpfsTmpDir:true. The heuristic is Linux-only — on other
653
+ // platforms the gate emits a warning and does not throw, so the
654
+ // fail-closed assertion only runs on Linux. A repo-local .test-output
655
+ // path is provably outside /dev/shm /run/shm /run/user /tmp.
656
+ if (process.platform !== "linux") {
657
+ check("non-tmpfs tmpDir gate is Linux-only (skipped off-Linux)", true);
658
+ return;
659
+ }
660
+ var scratchBase = path.join(__dirname, "..", ".test-output");
661
+ fs.mkdirSync(scratchBase, { recursive: true });
662
+ var schema = [{ name: "tmpfs_gate_t", columns: { _id: "TEXT PRIMARY KEY", v: "TEXT" } }];
663
+
664
+ // --- default: refuse ---
665
+ var tmpDir = fs.mkdtempSync(path.join(scratchBase, "db-nontmpfs-refuse-"));
666
+ try {
667
+ process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
668
+ setTestPassphraseEnv();
669
+ b.cluster._resetForTest();
670
+ b.audit._resetForTest();
671
+ b.vault._resetForTest();
672
+ b.db._resetForTest();
673
+ await b.vault.init({ dataDir: tmpDir });
674
+ var refuseErr = null;
675
+ try {
676
+ await b.db.init({ dataDir: tmpDir, tmpDir: path.join(tmpDir, "persist"), schema: schema });
677
+ } catch (e) { refuseErr = e; }
678
+ check("encrypted non-tmpfs tmpDir refused by default (fail-closed)",
679
+ refuseErr && refuseErr.code === "db/tmpdir-not-tmpfs");
680
+ } finally {
681
+ try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
682
+ await teardownTestDb(tmpDir);
683
+ }
684
+
685
+ // --- documented opt-out: allowNonTmpfsTmpDir:true boots ---
686
+ var tmpDir2 = fs.mkdtempSync(path.join(scratchBase, "db-nontmpfs-allow-"));
687
+ try {
688
+ process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
689
+ setTestPassphraseEnv();
690
+ b.cluster._resetForTest();
691
+ b.audit._resetForTest();
692
+ b.vault._resetForTest();
693
+ b.db._resetForTest();
694
+ await b.vault.init({ dataDir: tmpDir2 });
695
+ var allowErr = null;
696
+ try {
697
+ await b.db.init({ dataDir: tmpDir2, tmpDir: path.join(tmpDir2, "persist"),
698
+ schema: schema, allowNonTmpfsTmpDir: true });
699
+ } catch (e) { allowErr = e; }
700
+ check("allowNonTmpfsTmpDir:true opts out of the refusal (boots)", allowErr === null);
701
+ check("opt-out boot runs in encrypted mode", b.db.getMode() === "encrypted");
702
+ } finally {
703
+ try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
704
+ await teardownTestDb(tmpDir2);
705
+ }
706
+ }
707
+
646
708
  // ---- run() ----
647
709
 
648
710
  async function run() {
@@ -652,6 +714,7 @@ async function run() {
652
714
  await testEncryptedTmpfsCorruptionAutoRecovers();
653
715
  await testEncryptedCloseKeepsPlaintextWhenEncryptFails();
654
716
  await testTmpfsLowSpaceRefusesWritesFailClear();
717
+ await testEncryptedNonTmpfsTmpDirRefusedByDefault();
655
718
  await testExitHandlerRegisteredOnce();
656
719
  await testDbWriteOps();
657
720
  await testDbSealedWithoutDerived();
@@ -39,6 +39,12 @@ async function setupTestDb(tmpDir, schemaOverrides) {
39
39
  await b.db.init({
40
40
  dataDir: tmpDir,
41
41
  tmpDir: path.join(tmpDir, "tmpfs"),
42
+ // Test scratch dir is a plain directory, not a real tmpfs mount, and may
43
+ // live under the repo-local .test-output (outside the /tmp heuristic) for
44
+ // tests that corrupt their working file in place. Encrypted mode now
45
+ // refuses a non-tmpfs tmpDir by default (v0.15.0); the fixture knowingly
46
+ // uses one, so opt out explicitly.
47
+ allowNonTmpfsTmpDir: true,
42
48
  schema: schemaOverrides || [
43
49
  {
44
50
  name: "users",
@@ -82,6 +88,9 @@ async function setupTestDbForMW() {
82
88
  await b.db.init({
83
89
  dataDir: tmpDir,
84
90
  tmpDir: path.join(tmpDir, "tmpfs"),
91
+ // Plain scratch directory, not a real tmpfs mount — opt out of the
92
+ // v0.15.0 encrypted-mode non-tmpfs refusal for the test fixture.
93
+ allowNonTmpfsTmpDir: true,
85
94
  schema: [],
86
95
  });
87
96
  global._mwTmpDir = tmpDir;
@@ -118,18 +118,24 @@ function _makeFakeMysqlDriver() {
118
118
  var inLeader = /INSERT INTO _blamejs_leader/i.test(t) &&
119
119
  /ON DUPLICATE KEY UPDATE/i.test(t);
120
120
  if (inLeader) {
121
- // params: [nodeId, leaseId, acquiredAt, expiresAt, endpoint,
122
- // nowMs, nowMs, nowMs, nowMs, nowMs, nowMs]
121
+ // The provider composes this upsert through b.sql, which binds the
122
+ // VALUES list (scope first) then the ON DUPLICATE KEY UPDATE IF()
123
+ // params. Layout:
124
+ // VALUES: [0]=scope 'leader', [1]=nodeId, [2]=leaseId,
125
+ // [3]=acquiredAt, [4]=expiresAt, [5]=fencingToken(1),
126
+ // [6]=endpoint
127
+ // then the IF(expiresAt < ?, <new>, <old>) guards + proposed
128
+ // values; every guard binds nowMs, the first at [7].
123
129
  var newRow = {
124
- scope: "leader", nodeId: params[0], leaseId: params[1],
125
- acquiredAt: params[2], expiresAt: params[3], endpoint: params[4],
130
+ scope: "leader", nodeId: params[1], leaseId: params[2],
131
+ acquiredAt: params[3], expiresAt: params[4], endpoint: params[6],
126
132
  fencingToken: 1,
127
133
  };
128
134
  var existing = rows._blamejs_leader;
129
135
  if (!existing) {
130
136
  rows._blamejs_leader = newRow;
131
137
  } else {
132
- var nowPredicate = params[5];
138
+ var nowPredicate = params[7];
133
139
  if (existing.expiresAt < nowPredicate) {
134
140
  // Steal — IF() → use VALUES(*) for non-fencingToken cols,
135
141
  // bump fencingToken.
@@ -145,12 +151,15 @@ function _makeFakeMysqlDriver() {
145
151
  return { rows: [], affectedRows: 1 };
146
152
  }
147
153
 
148
- // UPDATE _blamejs_leader SET expiresAt = ?, endpoint = ?
149
- // WHERE scope='leader' AND nodeId=? AND leaseId=?
150
- var renewMatch = /^UPDATE _blamejs_leader SET[\s\S]*expiresAt = \?[\s\S]*WHERE scope = 'leader' AND nodeId = \? AND leaseId = \?/i.test(t);
154
+ // UPDATE _blamejs_leader SET `expiresAt` = ?, `endpoint` = ?
155
+ // WHERE `scope` = ? AND `nodeId` = ? AND `leaseId` = ? (renew).
156
+ // The provider composes this through b.sql: identifiers are
157
+ // backtick-quoted and the scope value binds (params[2]='leader')
158
+ // ahead of nodeId (params[3]) / leaseId (params[4]).
159
+ var renewMatch = /^UPDATE _blamejs_leader SET[\s\S]*`?expiresAt`? = \?[\s\S]*`?endpoint`? = \?[\s\S]*WHERE `?scope`? = \? AND `?nodeId`? = \? AND `?leaseId`? = \?/i.test(t);
151
160
  if (renewMatch) {
152
161
  var r = rows._blamejs_leader;
153
- if (r && r.nodeId === params[2] && r.leaseId === params[3]) {
162
+ if (r && r.nodeId === params[3] && r.leaseId === params[4]) {
154
163
  r.expiresAt = params[0];
155
164
  r.endpoint = params[1];
156
165
  return { rows: [], affectedRows: 1 };
@@ -158,18 +167,21 @@ function _makeFakeMysqlDriver() {
158
167
  return { rows: [], affectedRows: 0 };
159
168
  }
160
169
 
161
- // UPDATE _blamejs_leader SET expiresAt = 0 WHERE ... (release)
162
- var releaseMatch = /^UPDATE _blamejs_leader SET[\s\S]*expiresAt = 0/i.test(t);
170
+ // UPDATE _blamejs_leader SET `expiresAt` = ? WHERE ... (release).
171
+ // b.sql binds expiresAt=0 (params[0]) + scope (params[1]) + nodeId
172
+ // (params[2]) + leaseId (params[3]).
173
+ var releaseMatch = /^UPDATE _blamejs_leader SET[\s\S]*`?expiresAt`? = \?\s+WHERE/i.test(t);
163
174
  if (releaseMatch) {
164
175
  var rr = rows._blamejs_leader;
165
- if (rr && rr.nodeId === params[0] && rr.leaseId === params[1]) {
166
- rr.expiresAt = 0;
176
+ if (rr && rr.nodeId === params[2] && rr.leaseId === params[3]) {
177
+ rr.expiresAt = params[0];
167
178
  }
168
179
  return { rows: [], affectedRows: 1 };
169
180
  }
170
181
 
171
- // SELECT FROM _blamejs_leader WHERE scope = 'leader'
172
- if (/^SELECT[\s\S]+FROM _blamejs_leader WHERE scope = 'leader'/i.test(t)) {
182
+ // SELECT ... FROM _blamejs_leader WHERE `scope` = ? (b.sql binds the
183
+ // scope value rather than inlining the 'leader' literal).
184
+ if (/^SELECT[\s\S]+FROM _blamejs_leader WHERE `?scope`? = (?:\?|'leader')/i.test(t)) {
173
185
  var rl = rows._blamejs_leader;
174
186
  if (!rl) return { rows: [], affectedRows: 0 };
175
187
  return { rows: [Object.assign({}, rl)], affectedRows: 1 };
@@ -135,6 +135,27 @@ var DEFAULTS = {
135
135
  mitmproxyV6: "http://[::1]:8090",
136
136
  mitmproxyWebV6: "http://[::1]:8091",
137
137
  squidV6: "http://[::1]:3128",
138
+
139
+ // ---- cloud-storage / telemetry / AWS emulators (test-CA TLS) ----
140
+ azurite: "https://127.0.0.1:10000", // Azure Blob (path-style)
141
+ azuriteV6: "https://[::1]:10000",
142
+ gcs: "https://127.0.0.1:4443", // fake-gcs-server
143
+ gcsV6: "https://[::1]:4443",
144
+ localstack: "https://127.0.0.1:4566", // CloudWatch Logs + SQS (Caddy-terminated)
145
+ localstackV6: "https://[::1]:4566",
146
+ otelOtlp: "https://127.0.0.1:4318", // OTLP/HTTP receiver
147
+ otelOtlpV6: "https://[::1]:4318",
148
+ otelHealth: "http://127.0.0.1:13133/", // OTel health_check extension
149
+ otelHealthV6: "http://[::1]:13133/",
150
+ toxiproxy: "http://127.0.0.1:8474", // fault-injection API
151
+ toxiproxyV6: "http://[::1]:8474",
152
+ toxiproxyRedis: "redis://127.0.0.1:16379", // proxied redis
153
+ toxiproxyPg: "postgres://blamejs:blamejs_test@127.0.0.1:15432/blamejs_test",
154
+
155
+ // ---- postgres streaming replica (hot standby) ----
156
+ postgresReplica: "postgres://blamejs:blamejs_test@127.0.0.1:5433/blamejs_test",
157
+ postgresReplicaV6: "postgres://blamejs:blamejs_test@[::1]:5433/blamejs_test",
158
+ postgresReplicaTls: "postgres://blamejs:blamejs_test@127.0.0.1:5433/blamejs_test?sslmode=require",
138
159
  };
139
160
 
140
161
  function _envOverride(name) {