@blamejs/blamejs-shop 0.4.30 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/checkout.js +8 -0
  4. package/lib/order.js +71 -11
  5. package/lib/vendor/MANIFEST.json +392 -278
  6. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  7. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  8. package/lib/vendor/blamejs/.gitignore +6 -0
  9. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  10. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  11. package/lib/vendor/blamejs/README.md +8 -6
  12. package/lib/vendor/blamejs/SECURITY.md +19 -3
  13. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  14. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  15. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  16. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  17. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  18. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  19. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  20. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  21. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  24. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  25. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  26. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  27. package/lib/vendor/blamejs/index.js +4 -0
  28. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  29. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  30. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  31. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  32. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  33. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  34. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  35. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  36. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  37. package/lib/vendor/blamejs/lib/audit.js +259 -123
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  55. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  56. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  57. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  58. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  59. package/lib/vendor/blamejs/lib/db.js +249 -99
  60. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  61. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  62. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  63. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  64. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  65. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  66. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  67. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  68. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  69. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  70. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  71. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  72. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  73. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  74. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  75. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  76. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  77. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  78. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  79. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  80. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  81. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  82. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  83. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  84. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  85. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  86. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  87. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  88. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  89. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  90. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  91. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  92. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  93. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  94. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  95. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  96. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  98. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  99. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  100. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  101. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  102. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  103. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  104. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  106. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  107. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  108. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  109. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  110. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  111. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  112. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  113. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  114. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  115. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  116. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  117. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  118. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  119. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  120. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  121. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  122. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  123. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  124. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  125. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  126. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  128. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  129. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  131. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  132. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  134. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  136. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  137. package/lib/vendor/blamejs/lib/mail.js +8 -4
  138. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  139. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  140. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  141. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  142. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  143. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  144. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  145. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  146. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  147. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  148. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  149. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  151. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  152. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  153. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  154. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  155. package/lib/vendor/blamejs/lib/observability.js +124 -0
  156. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  157. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  158. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  159. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  160. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  161. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  162. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  163. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  164. package/lib/vendor/blamejs/lib/queue.js +7 -0
  165. package/lib/vendor/blamejs/lib/redact.js +68 -11
  166. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  167. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  168. package/lib/vendor/blamejs/lib/retention.js +101 -40
  169. package/lib/vendor/blamejs/lib/router.js +212 -5
  170. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  171. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  172. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  173. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  174. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  175. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  176. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  177. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  178. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  179. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  180. package/lib/vendor/blamejs/lib/session.js +175 -77
  181. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  182. package/lib/vendor/blamejs/lib/sse.js +26 -0
  183. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  184. package/lib/vendor/blamejs/lib/static.js +177 -34
  185. package/lib/vendor/blamejs/lib/subject.js +96 -49
  186. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  187. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  189. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  190. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  191. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  192. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  193. package/lib/vendor/blamejs/package.json +2 -2
  194. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  202. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  203. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  204. package/lib/vendor/blamejs/scripts/release.js +398 -38
  205. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  206. package/lib/vendor/blamejs/test/10-state.js +140 -14
  207. package/lib/vendor/blamejs/test/20-db.js +65 -2
  208. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  209. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  210. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  211. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  212. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  213. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  214. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  215. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  222. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  223. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  224. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  225. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  226. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  227. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  228. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  229. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  230. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  231. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  232. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  233. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  234. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  235. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  236. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  237. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  238. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  240. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  250. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  251. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  272. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  273. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  274. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  275. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  279. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  280. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  293. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  304. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  310. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  312. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  313. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  314. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  315. package/lib/vendor/blamejs/test/smoke.js +79 -21
  316. package/package.json +1 -1
  317. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  318. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  319. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  320. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  321. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  322. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  323. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  324. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  325. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  326. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  327. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  328. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  329. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  330. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  331. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  332. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  335. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  336. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  337. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  338. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -57,9 +57,11 @@ var { generateToken, generateBytes, encryptPacked, decryptPacked, sha3Hash } = r
57
57
  var cryptoField = require("./crypto-field");
58
58
  var dbDeclareRowPolicy = require("./db-declare-row-policy");
59
59
  var dbDeclareView = require("./db-declare-view");
60
- var { Query } = require("./db-query");
60
+ var { Query, _isRawWriteToResidencyTable, _assertRawWriteResidency } = require("./db-query");
61
61
  var dbSchema = require("./db-schema");
62
62
  var { defineClass } = require("./framework-error");
63
+ var frameworkFiles = require("./framework-files");
64
+ var frameworkSchema = require("./framework-schema");
63
65
  var { boot } = require("./log");
64
66
  var lazyRequire = require("./lazy-require");
65
67
  var observability = require("./observability");
@@ -68,10 +70,18 @@ var safeAsync = require("./safe-async");
68
70
  var safeEnv = require("./parsers/safe-env");
69
71
  var safeJson = require("./safe-json");
70
72
  var safeSql = require("./safe-sql");
73
+ var sql = require("./sql");
71
74
  var validateOpts = require("./validate-opts");
72
75
  var vault = require("./vault");
73
76
  var vaultAad = require("./vault-aad");
74
77
 
78
+ // b.sql opts for the local single-node sqlite handle (database.prepare,
79
+ // never clusterStorage): "sqlite" dialect + quoteName so the resolved
80
+ // framework table name quotes by construction. The few DML sites here that
81
+ // target a framework state table resolve its name through
82
+ // frameworkSchema.tableName so a configured table prefix flows through.
83
+ var _SQL_OPTS = { dialect: "sqlite", quoteName: true };
84
+
75
85
  var DbError = defineClass("DbError", { alwaysPermanent: true });
76
86
  var WormViolationError = require("./framework-error").WormViolationError;
77
87
  var _wormErr = WormViolationError.factory;
@@ -173,27 +183,30 @@ var columnGateMode = "reject";
173
183
  // are provisioned by the framework before app schema reconciles. Apps cannot
174
184
  // opt out, override, or rename them. An app schema entry colliding with any of
175
185
  // these names is refused at init.
186
+ // These are the canonical LOCAL reserved table-name declarations (the
187
+ // guard set an operator schema may not collide with), NOT query SQL — the
188
+ // literal names ARE the contract. allow:hand-rolled-sql markers below.
176
189
  var RESERVED_TABLE_NAMES = new Set([
177
190
  "audit_log",
178
191
  "audit_checkpoints",
179
192
  "consent_log",
180
- "_blamejs_subject_restrictions",
181
- "_blamejs_subject_erasures",
182
- "_blamejs_sessions",
183
- "_blamejs_jobs",
184
- "_blamejs_migrations",
185
- "_blamejs_counters",
186
- "_blamejs_audit_purge_anchor",
187
- "_blamejs_scheduler_ticks",
188
- "_blamejs_rate_limit_counters",
189
- "_blamejs_pubsub_messages",
190
- "_blamejs_api_encrypt_nonces",
191
- "_blamejs_api_keys",
192
- "_blamejs_cache",
193
- "_blamejs_seeders",
194
- "_blamejs_seeders_lock",
195
- "_blamejs_break_glass_policies",
196
- "_blamejs_break_glass_grants",
193
+ "_blamejs_subject_restrictions", // allow:hand-rolled-sql — canonical reserved local table-name declaration
194
+ "_blamejs_subject_erasures", // allow:hand-rolled-sql — canonical reserved local table-name declaration
195
+ "_blamejs_sessions", // allow:hand-rolled-sql — canonical reserved local table-name declaration
196
+ "_blamejs_jobs", // allow:hand-rolled-sql — canonical reserved local table-name declaration
197
+ "_blamejs_migrations", // allow:hand-rolled-sql — canonical reserved local table-name declaration
198
+ "_blamejs_counters", // allow:hand-rolled-sql — canonical reserved local table-name declaration
199
+ "_blamejs_audit_purge_anchor", // allow:hand-rolled-sql — canonical reserved local table-name declaration
200
+ "_blamejs_scheduler_ticks", // allow:hand-rolled-sql — canonical reserved local table-name declaration
201
+ "_blamejs_rate_limit_counters", // allow:hand-rolled-sql — canonical reserved local table-name declaration
202
+ "_blamejs_pubsub_messages", // allow:hand-rolled-sql — canonical reserved local table-name declaration
203
+ "_blamejs_api_encrypt_nonces", // allow:hand-rolled-sql — canonical reserved local table-name declaration
204
+ "_blamejs_api_keys", // allow:hand-rolled-sql — canonical reserved local table-name declaration
205
+ "_blamejs_cache", // allow:hand-rolled-sql — canonical reserved local table-name declaration
206
+ "_blamejs_seeders", // allow:hand-rolled-sql — canonical reserved local table-name declaration
207
+ "_blamejs_seeders_lock", // allow:hand-rolled-sql — canonical reserved local table-name declaration
208
+ "_blamejs_break_glass_policies", // allow:hand-rolled-sql — canonical reserved local table-name declaration
209
+ "_blamejs_break_glass_grants", // allow:hand-rolled-sql — canonical reserved local table-name declaration
197
210
  ]);
198
211
 
199
212
  var FRAMEWORK_SCHEMA = [
@@ -260,7 +273,7 @@ var FRAMEWORK_SCHEMA = [
260
273
  },
261
274
  },
262
275
  {
263
- name: "_blamejs_subject_restrictions",
276
+ name: "_blamejs_subject_restrictions", // allow:hand-rolled-sql — canonical local-schema table-name declaration
264
277
  columns: {
265
278
  subjectIdHash: "TEXT PRIMARY KEY",
266
279
  since: "INTEGER NOT NULL",
@@ -269,7 +282,7 @@ var FRAMEWORK_SCHEMA = [
269
282
  sealedFields: ["reason"],
270
283
  },
271
284
  {
272
- name: "_blamejs_subject_erasures",
285
+ name: "_blamejs_subject_erasures", // allow:hand-rolled-sql — canonical local-schema table-name declaration
273
286
  columns: {
274
287
  subjectIdHash: "TEXT PRIMARY KEY",
275
288
  erasedAt: "INTEGER NOT NULL",
@@ -281,7 +294,7 @@ var FRAMEWORK_SCHEMA = [
281
294
  // b.retention consult b.legalHold.isHeld(subjectId) before
282
295
  // accepting any deletion. Per FRCP Rule 26/37(e), GDPR Art
283
296
  // 17(3)(e), SEC Rule 17a-4, HIPAA §164.530(j)(2).
284
- name: "_blamejs_legal_hold",
297
+ name: "_blamejs_legal_hold", // allow:hand-rolled-sql — canonical local-schema table-name declaration
285
298
  columns: {
286
299
  subjectIdHash: "TEXT PRIMARY KEY",
287
300
  placedAt: "INTEGER NOT NULL",
@@ -291,15 +304,29 @@ var FRAMEWORK_SCHEMA = [
291
304
  citation: "TEXT",
292
305
  retainUntil: "INTEGER",
293
306
  },
307
+ // The legal-basis / custodian / ticket-citation free text links a data
308
+ // subject to a legal matter — PII at rest. Sealed like the DSR ticket store
309
+ // (b.legalHold seals on write + unseals on read through cryptoField).
310
+ sealedFields: ["reason", "placedBy", "custodian", "citation"],
294
311
  indexes: ["placedAt"],
295
312
  },
296
313
  {
297
314
  // Per-row crypto-erasure key registry — per-row keys.
298
- // Each entry holds a sealed wrapped K_row keyed by (table,
299
- // rowId). b.subject.eraseHard deletes the entry, leaving WAL /
300
- // replica residuals undecryptable.
301
- name: "_blamejs_per_row_keys",
315
+ // Each entry holds the AAD-sealed random row-secret keyed by
316
+ // (tableName, rowId); the row-scoped K_row is derived from it.
317
+ // b.subject.eraseHard / b.retention destroy the entry, leaving WAL /
318
+ // replica residuals undecryptable. wrappedKey is registered as an
319
+ // AAD-bound sealed field (aad:true, rowIdField:"rowId") so a vault
320
+ // keypair rotation auto-reseals it old-root -> new-root via
321
+ // rotate._rotateColumn — without this a rotation would orphan every
322
+ // wrapped secret and brick every keyed row.
323
+ name: "_blamejs_per_row_keys", // allow:hand-rolled-sql — canonical local-schema table-name declaration
302
324
  columns: {
325
+ // _id is the rotation pipeline's keyset-pagination + UPDATE key
326
+ // (rotate._rotateColumn SELECTs _id and orders by it); the natural
327
+ // identity stays the composite (tableName, rowId). materialize
328
+ // populates _id with a fresh token.
329
+ _id: "TEXT",
303
330
  tableName: "TEXT NOT NULL",
304
331
  rowId: "TEXT NOT NULL",
305
332
  wrappedKey: "BLOB NOT NULL",
@@ -307,6 +334,10 @@ var FRAMEWORK_SCHEMA = [
307
334
  },
308
335
  primaryKey: ["tableName", "rowId"],
309
336
  indexes: [],
337
+ sealedFields: ["wrappedKey"],
338
+ aad: true,
339
+ rowIdField: "rowId",
340
+ schemaVersion: "1",
310
341
  },
311
342
  {
312
343
  // Operator-declared WORM (write-once-read-many) registry. Each
@@ -314,7 +345,7 @@ var FRAMEWORK_SCHEMA = [
314
345
  // demanded the WORM declaration; boot-time assertions iterate
315
346
  // this registry to verify triggers are installed under the
316
347
  // current b.compliance.current() posture.
317
- name: "_blamejs_worm_tables",
348
+ name: "_blamejs_worm_tables", // allow:hand-rolled-sql — canonical local-schema table-name declaration
318
349
  columns: {
319
350
  tableName: "TEXT PRIMARY KEY",
320
351
  posture: "TEXT",
@@ -327,7 +358,7 @@ var FRAMEWORK_SCHEMA = [
327
358
  // destructive ops; under the named posture the framework refuses
328
359
  // execution unless the caller passes a consumed dual-control
329
360
  // grant.
330
- name: "_blamejs_dual_control_gates",
361
+ name: "_blamejs_dual_control_gates", // allow:hand-rolled-sql — canonical local-schema table-name declaration
331
362
  columns: {
332
363
  tableName: "TEXT PRIMARY KEY",
333
364
  posture: "TEXT",
@@ -354,7 +385,7 @@ var FRAMEWORK_SCHEMA = [
354
385
  sealedFields: [],
355
386
  },
356
387
  {
357
- name: "_blamejs_audit_purge_anchor",
388
+ name: "_blamejs_audit_purge_anchor", // allow:hand-rolled-sql — canonical local-schema table-name declaration
358
389
  columns: {
359
390
  // CHECK constraint: scope is one of the framework's audit-
360
391
  // chain anchor scopes (`audit` / `consent`). Pre-v0.8.37 a
@@ -376,7 +407,7 @@ var FRAMEWORK_SCHEMA = [
376
407
  // leader's INSERT loses with a constraint violation, and that node
377
408
  // skips the tick. Closes the once-globally gap during cluster
378
409
  // leader hand-offs where two leaders briefly coexist.
379
- name: "_blamejs_scheduler_ticks",
410
+ name: "_blamejs_scheduler_ticks", // allow:hand-rolled-sql — canonical local-schema table-name declaration
380
411
  columns: {
381
412
  tickKey: "TEXT PRIMARY KEY",
382
413
  name: "TEXT NOT NULL",
@@ -392,7 +423,7 @@ var FRAMEWORK_SCHEMA = [
392
423
  // the cluster-shared rate-limit backend. One row per (key); the
393
424
  // count rolls over atomically when the windowStart advances. Used
394
425
  // by lib/middleware/rate-limit.js when scope: 'cluster' is set.
395
- name: "_blamejs_rate_limit_counters",
426
+ name: "_blamejs_rate_limit_counters", // allow:hand-rolled-sql — canonical local-schema table-name declaration
396
427
  columns: {
397
428
  key: "TEXT PRIMARY KEY",
398
429
  windowStart: "INTEGER NOT NULL",
@@ -408,7 +439,7 @@ var FRAMEWORK_SCHEMA = [
408
439
  // publish; other nodes poll for new ids and dispatch to their
409
440
  // local subscribers. Rows older than the configured retention
410
441
  // window are pruned by the backend on a rate-limited basis.
411
- name: "_blamejs_pubsub_messages",
442
+ name: "_blamejs_pubsub_messages", // allow:hand-rolled-sql — canonical local-schema table-name declaration
412
443
  columns: {
413
444
  id: "INTEGER PRIMARY KEY AUTOINCREMENT",
414
445
  topic: "TEXT NOT NULL",
@@ -426,7 +457,7 @@ var FRAMEWORK_SCHEMA = [
426
457
  // exposes the original 16-byte client nonces. Hashing is
427
458
  // deterministic so the PRIMARY KEY conflict is what catches a
428
459
  // replay attempt within the replay window.
429
- name: "_blamejs_api_encrypt_nonces",
460
+ name: "_blamejs_api_encrypt_nonces", // allow:hand-rolled-sql — canonical local-schema table-name declaration
430
461
  columns: {
431
462
  nonceHash: "TEXT PRIMARY KEY",
432
463
  expireAt: "INTEGER NOT NULL",
@@ -435,7 +466,7 @@ var FRAMEWORK_SCHEMA = [
435
466
  sealedFields: [],
436
467
  },
437
468
  {
438
- name: "_blamejs_sessions",
469
+ name: "_blamejs_sessions", // allow:hand-rolled-sql — canonical local-schema table-name declaration
439
470
  columns: {
440
471
  sidHash: "TEXT PRIMARY KEY",
441
472
  userId: "TEXT NOT NULL",
@@ -456,7 +487,7 @@ var FRAMEWORK_SCHEMA = [
456
487
  // verify. Same dual-storage pattern as sessions: this row mirrors
457
488
  // the cluster-mode DDL in framework-schema.js so cluster-storage
458
489
  // can route to either backend transparently.
459
- name: "_blamejs_api_keys",
490
+ name: "_blamejs_api_keys", // allow:hand-rolled-sql — canonical local-schema table-name declaration
460
491
  columns: {
461
492
  id: "TEXT PRIMARY KEY",
462
493
  namespace: "TEXT NOT NULL",
@@ -487,7 +518,7 @@ var FRAMEWORK_SCHEMA = [
487
518
  derivedHashes: { ownerIdHash: { from: "ownerId" } },
488
519
  },
489
520
  {
490
- name: "_blamejs_jobs",
521
+ name: "_blamejs_jobs", // allow:hand-rolled-sql — canonical local-schema table-name declaration
491
522
  columns: {
492
523
  _id: "TEXT PRIMARY KEY",
493
524
  queueName: "TEXT NOT NULL",
@@ -532,7 +563,7 @@ var FRAMEWORK_SCHEMA = [
532
563
  // JSON-serialized; expiresAt is unix-ms (Number.MAX_SAFE_INTEGER for
533
564
  // never-expiring entries). Not sealed: cache values are operator-
534
565
  // chosen application data, the operator decides what's worth storing.
535
- name: "_blamejs_cache",
566
+ name: "_blamejs_cache", // allow:hand-rolled-sql — canonical local-schema table-name declaration
536
567
  columns: {
537
568
  cacheKey: "TEXT PRIMARY KEY",
538
569
  valueJson: "TEXT NOT NULL",
@@ -548,7 +579,7 @@ var FRAMEWORK_SCHEMA = [
548
579
  // PK (cacheKey, tag) lets one cacheKey carry many tags; index on
549
580
  // tag makes invalidation a single indexed scan. Cleared together
550
581
  // with the matching _blamejs_cache rows on del / clear / sweep.
551
- name: "_blamejs_cache_tags",
582
+ name: "_blamejs_cache_tags", // allow:hand-rolled-sql — canonical local-schema table-name declaration
552
583
  columns: {
553
584
  cacheKey: "TEXT NOT NULL",
554
585
  tag: "TEXT NOT NULL",
@@ -564,7 +595,7 @@ var FRAMEWORK_SCHEMA = [
564
595
  // collide with prod fixtures by name). rerunnable=1 entries get
565
596
  // their appliedAt updated in place on every run; non-rerunnable
566
597
  // entries are insert-once.
567
- name: "_blamejs_seeders",
598
+ name: "_blamejs_seeders", // allow:hand-rolled-sql — canonical local-schema table-name declaration
568
599
  columns: {
569
600
  env: "TEXT NOT NULL",
570
601
  name: "TEXT NOT NULL",
@@ -582,7 +613,7 @@ var FRAMEWORK_SCHEMA = [
582
613
  // on scope='lock' enforces single row). Two processes calling
583
614
  // `seed run` against the same DB race on this PK; loser sees a
584
615
  // clear "lock held" error.
585
- name: "_blamejs_seeders_lock",
616
+ name: "_blamejs_seeders_lock", // allow:hand-rolled-sql — canonical local-schema table-name declaration
586
617
  columns: {
587
618
  scope: "TEXT PRIMARY KEY CHECK (scope = 'lock')",
588
619
  lockedAt: "INTEGER NOT NULL",
@@ -596,7 +627,7 @@ var FRAMEWORK_SCHEMA = [
596
627
  // glass-locked and what the operator's grant rules are. Sealed
597
628
  // columns hold the column-list, factor-list, and bypass config so
598
629
  // policy contents aren't browsable in cleartext.
599
- name: "_blamejs_break_glass_policies",
630
+ name: "_blamejs_break_glass_policies", // allow:hand-rolled-sql — canonical local-schema table-name declaration
600
631
  columns: {
601
632
  tableName: "TEXT PRIMARY KEY",
602
633
  columnsJson: "TEXT NOT NULL",
@@ -625,7 +656,7 @@ var FRAMEWORK_SCHEMA = [
625
656
  // (each row access = its own grant).
626
657
  // Sealed columns hold reason + scopeColumns so audit-readable
627
658
  // metadata doesn't leak in cleartext.
628
- name: "_blamejs_break_glass_grants",
659
+ name: "_blamejs_break_glass_grants", // allow:hand-rolled-sql — canonical local-schema table-name declaration
629
660
  columns: {
630
661
  _id: "TEXT PRIMARY KEY",
631
662
  issuedToActorId: "TEXT NOT NULL",
@@ -688,7 +719,7 @@ function loadOrCreateDbKey(dataDirPath, keyPathOverride) {
688
719
  // needs to live outside `dataDir` (e.g. a separate volume mounted
689
720
  // from a KMS-fronted secret store). Default places it next to the
690
721
  // encrypted DB so backup capture is one-tarball.
691
- var keyPath = keyPathOverride || nodePath.join(dataDirPath, "db.key.enc");
722
+ var keyPath = keyPathOverride || nodePath.join(dataDirPath, frameworkFiles.fileName("dbKeyEnc"));
692
723
  var aad = _dbKeyAad(dataDirPath, keyPath);
693
724
  if (nodeFs.existsSync(keyPath)) {
694
725
  var sealed = atomicFile.readSync(keyPath, { encoding: "utf8" }).trim();
@@ -956,6 +987,7 @@ function cleanStaleTmpDbs(tmpDir) {
956
987
  * schema: Array, // required — [{ name, columns, indexes, sealedFields, derivedHashes, foreignKeys, primaryKey, subjectField, personalDataCategories }, ...]
957
988
  * atRest: "encrypted"|"plain", // default "encrypted"
958
989
  * tmpDir: string, // override the encrypted-mode tmpfs path (default /dev/shm or BLAMEJS_TMPDIR)
990
+ * allowNonTmpfsTmpDir: boolean, // default false — encrypted mode THROWS when tmpDir is not a recognized tmpfs mount (plaintext-on-disk leak); pass true to downgrade to a warning when the mount is verified in-memory out-of-band
959
991
  * migrationDir: string, // optional — path to ./migrations/ (run-once each)
960
992
  * streamLimit: number, // default 1_000_000 — db.stream row ceiling
961
993
  * columnGate: "reject"|"warn"|"off", // default "reject" — refuse queries on columns not declared in the table schema
@@ -1028,6 +1060,15 @@ async function init(opts) {
1028
1060
  JSON.stringify(opts.columnGate));
1029
1061
  }
1030
1062
  columnGateMode = opts.columnGate || "reject";
1063
+ // Configurable framework-table prefix. Config-time: setTablePrefix
1064
+ // throws a FrameworkSchemaError on a non-identifier prefix so a typo
1065
+ // surfaces at boot rather than as a silently-misnamed table. Applied
1066
+ // BEFORE any schema creation below so framework DDL + the cluster
1067
+ // tableName resolver honor it. Default ("_blamejs_") is byte-identical
1068
+ // to the historical names, so omitting the option is a no-op.
1069
+ if (opts.tablePrefix !== undefined) {
1070
+ frameworkSchema.setTablePrefix(opts.tablePrefix);
1071
+ }
1031
1072
  dataDir = opts.dataDir;
1032
1073
  if (!nodeFs.existsSync(dataDir)) nodeFs.mkdirSync(dataDir, { recursive: true });
1033
1074
 
@@ -1040,21 +1081,42 @@ async function init(opts) {
1040
1081
  }
1041
1082
  if (!nodeFs.existsSync(tmpDir)) nodeFs.mkdirSync(tmpDir, { recursive: true });
1042
1083
 
1043
- // If the resolved tmpDir is NOT actually tmpfs, the
1044
- // plaintext DB file lives on persistent storage. We check that tmpDir
1045
- // resolves under /dev/shm or /run/shm on Linux as a heuristic; on other
1046
- // platforms we warn that the operator must verify tmpfs binding
1047
- // out-of-band. (Free-space headroom is enforced separately via
1048
- // fs.statfsSync in the storage guard below.)
1084
+ // If the resolved tmpDir is NOT actually tmpfs, the plaintext working
1085
+ // copy lives on persistent storage and leaks into backup snapshots,
1086
+ // replication, and forensic disk images defeating the whole point of
1087
+ // encrypted-at-rest mode. On Linux we verify the resolved path lands
1088
+ // under a known in-memory mount (/dev/shm /run/shm /run/user, plus
1089
+ // /tmp which is tmpfs on systemd-default + most container images).
1090
+ //
1091
+ // Fail-closed default (v0.15.0): a tmpDir that resolves OUTSIDE those
1092
+ // mounts THROWS db/tmpdir-not-tmpfs at boot rather than logging a
1093
+ // warning the operator never reads — the prior warn-only path silently
1094
+ // shipped plaintext to disk under the encrypted-mode default. The
1095
+ // documented opt-out is opts.allowNonTmpfsTmpDir: true, for the operator
1096
+ // who has verified the mount is in-memory out-of-band (e.g. a ramfs /
1097
+ // a tmpfs bind-mounted at a non-standard path the heuristic can't see)
1098
+ // or who has accepted the disk-residency tradeoff. The opt-out downgrades
1099
+ // to the prior warning. (Free-space headroom is enforced separately via
1100
+ // fs.statfsSync in the storage guard below.) The heuristic is Linux-only;
1101
+ // other platforms can't be probed by path and emit the warning unchanged.
1049
1102
  if (process.platform === "linux") {
1050
1103
  var realTmp = "";
1051
1104
  try { realTmp = nodeFs.realpathSync(tmpDir); } catch (_e) { /* stat best-effort */ }
1052
1105
  if (realTmp.indexOf("/dev/shm") !== 0 && realTmp.indexOf("/run/shm") !== 0 &&
1053
1106
  realTmp.indexOf("/run/user/") !== 0 && realTmp.indexOf("/tmp") !== 0) {
1054
- log.warn("WARNING: db.init: tmpDir '" + tmpDir + "' (real: '" + realTmp +
1055
- "') does not resolve under /dev/shm /run/shm /run/user /tmp — verify it is " +
1056
- "actually a tmpfs mount. A persistent-disk tmpDir leaks plaintext into backup " +
1057
- "snapshots, replication, and forensic disk images.");
1107
+ var tmpfsMsg = "db.init: tmpDir '" + tmpDir + "' (real: '" + realTmp +
1108
+ "') does not resolve under /dev/shm /run/shm /run/user /tmp — it is not a " +
1109
+ "recognized tmpfs mount. A persistent-disk tmpDir leaks the decrypted " +
1110
+ "working copy into backup snapshots, replication, and forensic disk images.";
1111
+ if (opts.allowNonTmpfsTmpDir === true) {
1112
+ log.warn("WARNING: " + tmpfsMsg + " (allowNonTmpfsTmpDir:true — verify the " +
1113
+ "mount is in-memory out-of-band.)");
1114
+ } else {
1115
+ throw _dbErr("db/tmpdir-not-tmpfs", "FATAL: " + tmpfsMsg +
1116
+ " Mount a tmpfs at the path (or set BLAMEJS_TMPDIR / opts.tmpDir to one), " +
1117
+ "or pass opts.allowNonTmpfsTmpDir: true to accept the disk-residency tradeoff, " +
1118
+ "or pass atRest: 'plain' if encryption-at-rest is not required.");
1119
+ }
1058
1120
  }
1059
1121
  }
1060
1122
 
@@ -1063,7 +1125,7 @@ async function init(opts) {
1063
1125
  // just the basename under `dataDir` (default "db.enc"). Helps when
1064
1126
  // multiple framework-shaped instances share a dataDir.
1065
1127
  encPath = opts.encryptedDbPath ||
1066
- nodePath.join(dataDir, opts.encryptedDbName || "db.enc");
1128
+ nodePath.join(dataDir, opts.encryptedDbName || frameworkFiles.fileName("dbEnc"));
1067
1129
  dbPath = nodePath.join(tmpDir, "blamejs-" + generateToken(C.BYTES.bytes(16)) + ".db");
1068
1130
  encKey = loadOrCreateDbKey(dataDir, opts.dbKeyPath);
1069
1131
 
@@ -1624,6 +1686,27 @@ var _prepareCache = new Map();
1624
1686
  * typeof row.total;
1625
1687
  * // → "object"
1626
1688
  */
1689
+ // Wrap a prepared statement that writes to a per-row-residency table so its
1690
+ // execution (run / get / all / iterate) validates the residency tag of the
1691
+ // bound row through the same gate the structured builder uses. Only residency
1692
+ // writes are wrapped (cheap prepare-time pre-check) so the common path is
1693
+ // untouched.
1694
+ function _gatedResidencyStmt(stmt, sql) {
1695
+ var EXEC = { run: true, get: true, all: true, iterate: true };
1696
+ return new Proxy(stmt, {
1697
+ get: function (target, prop) {
1698
+ var v = target[prop];
1699
+ if (typeof prop === "string" && EXEC[prop] && typeof v === "function") {
1700
+ return function () {
1701
+ _assertRawWriteResidency(sql, Array.prototype.slice.call(arguments));
1702
+ return v.apply(target, arguments);
1703
+ };
1704
+ }
1705
+ return typeof v === "function" ? v.bind(target) : v;
1706
+ },
1707
+ });
1708
+ }
1709
+
1627
1710
  function prepare(sql) {
1628
1711
  _requireInit();
1629
1712
  if (_prepareCache.has(sql)) {
@@ -1634,6 +1717,7 @@ function prepare(sql) {
1634
1717
  return hit;
1635
1718
  }
1636
1719
  var stmt = database.prepare(sql);
1720
+ if (_isRawWriteToResidencyTable(sql)) stmt = _gatedResidencyStmt(stmt, sql);
1637
1721
  _prepareCache.set(sql, stmt);
1638
1722
  if (_prepareCache.size > PREPARE_CACHE_MAX) {
1639
1723
  var oldestKey = _prepareCache.keys().next().value;
@@ -1796,6 +1880,9 @@ function _reportSlowSqlite(durationMs, statement) {
1796
1880
 
1797
1881
  function execRaw(sql) {
1798
1882
  _requireInit();
1883
+ // Raw writes bypass the structured builder's residency gate; validate the
1884
+ // residency tag of an INSERT / UPDATE to a per-row-residency table here too.
1885
+ _assertRawWriteResidency(sql);
1799
1886
  var startedAt = Date.now();
1800
1887
  var auditMod = (function () { try { return require("./audit"); } catch (_e) { return null; } })(); // allow:inline-require — circular-load defense (audit imports db)
1801
1888
  // DDL_RE only matches the leading keyword — bounded by `/\s*(KEYWORD)\b/`
@@ -1926,6 +2013,31 @@ function hashFor(table, field, value) {
1926
2013
  return lookup ? lookup.value : null;
1927
2014
  }
1928
2015
 
2016
+ /**
2017
+ * @primitive b.db.hashCandidatesFor
2018
+ * @signature b.db.hashCandidatesFor(table, field, value)
2019
+ * @since 0.15.1
2020
+ * @status stable
2021
+ * @related b.db.hashFor, b.db.from
2022
+ *
2023
+ * Dual-read sibling of `hashFor`. Returns `{ field, values }` where `values`
2024
+ * holds the active derived-hash digest AND — across the v0.15.0 keyed-MAC
2025
+ * default flip — the legacy salted-sha3 digest a row written before the flip
2026
+ * carries. A `WHERE <hashColumn> IN (...)` lookup over `values` matches both
2027
+ * keyed-indexed and legacy-indexed rows, so the flip never silently drops an
2028
+ * un-migrated row. Returns `null` when the field has no derived-hash
2029
+ * declaration on the table.
2030
+ *
2031
+ * @example
2032
+ * var c = b.db.hashCandidatesFor("users", "email", "alice@example.com");
2033
+ * b.db.from("users").whereIn(c.field, c.values).all();
2034
+ * // → rows matching either the keyed-MAC or the legacy digest
2035
+ */
2036
+ function hashCandidatesFor(table, field, value) {
2037
+ _requireInit();
2038
+ return cryptoField.lookupHashCandidates(table, field, value);
2039
+ }
2040
+
1929
2041
  // _ddlToJsonSchemaType — best-effort SQL→JSON Schema type mapping.
1930
2042
  // SQLite is dynamically typed but the framework's DDL syntax pins
1931
2043
  // concrete types; we map them here. Operator-supplied custom types
@@ -2250,21 +2362,24 @@ function _normalizePk(tableSpec) {
2250
2362
  // that RAISE(ABORT) the operation. INSERT remains permitted (that's what
2251
2363
  // audit.record / consent.grant do).
2252
2364
  function _installAppendOnlyTriggers(database) {
2365
+ // b.sql has no CREATE TRIGGER builder — these append-only WORM triggers
2366
+ // are SQLite-specific (RAISE(ABORT) trigger bodies) over framework-
2367
+ // controlled, fixed table names. Identifiers are quoted by construction.
2253
2368
  var tables = ["audit_log", "consent_log", "audit_checkpoints"];
2254
2369
  for (var i = 0; i < tables.length; i++) {
2255
2370
  var t = tables[i];
2256
2371
  runSql(database,
2257
- 'CREATE TRIGGER IF NOT EXISTS "no_delete_' + t + '" ' +
2372
+ 'CREATE TRIGGER IF NOT EXISTS "no_delete_' + t + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite append-only WORM trigger, fixed framework table
2258
2373
  'BEFORE DELETE ON "' + t + '" ' +
2259
2374
  'BEGIN ' +
2260
- " SELECT RAISE(ABORT, '" + t + " is append-only — DELETE prohibited'); " +
2375
+ " SELECT RAISE(ABORT, '" + t + " is append-only — DELETE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
2261
2376
  'END'
2262
2377
  );
2263
2378
  runSql(database,
2264
- 'CREATE TRIGGER IF NOT EXISTS "no_update_' + t + '" ' +
2379
+ 'CREATE TRIGGER IF NOT EXISTS "no_update_' + t + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite append-only WORM trigger, fixed framework table
2265
2380
  'BEFORE UPDATE ON "' + t + '" ' +
2266
2381
  'BEGIN ' +
2267
- " SELECT RAISE(ABORT, '" + t + " is append-only — UPDATE prohibited'); " +
2382
+ " SELECT RAISE(ABORT, '" + t + " is append-only — UPDATE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
2268
2383
  'END'
2269
2384
  );
2270
2385
  }
@@ -2277,19 +2392,22 @@ function _installAppendOnlyTriggers(database) {
2277
2392
  // boot-time assertion under WORM_POSTURES catches operators who
2278
2393
  // set the posture without declaring tables.
2279
2394
  function _installWormTriggers(database, tableName) {
2395
+ // b.sql has no CREATE TRIGGER builder — operator-table WORM triggers are
2396
+ // SQLite-specific RAISE(ABORT) trigger bodies; tableName is validated
2397
+ // (validateIdentifier) and quoted by construction.
2280
2398
  safeSql.validateIdentifier(tableName);
2281
2399
  runSql(database,
2282
- 'CREATE TRIGGER IF NOT EXISTS "worm_no_delete_' + tableName + '" ' +
2400
+ 'CREATE TRIGGER IF NOT EXISTS "worm_no_delete_' + tableName + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite WORM trigger over validated operator table
2283
2401
  'BEFORE DELETE ON "' + tableName + '" ' +
2284
2402
  'BEGIN ' +
2285
- " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - DELETE prohibited'); " +
2403
+ " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - DELETE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
2286
2404
  'END'
2287
2405
  );
2288
2406
  runSql(database,
2289
- 'CREATE TRIGGER IF NOT EXISTS "worm_no_update_' + tableName + '" ' +
2407
+ 'CREATE TRIGGER IF NOT EXISTS "worm_no_update_' + tableName + '" ' + // allow:hand-rolled-sql — b.sql has no CREATE TRIGGER builder; SQLite WORM trigger over validated operator table
2290
2408
  'BEFORE UPDATE ON "' + tableName + '" ' +
2291
2409
  'BEGIN ' +
2292
- " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - UPDATE prohibited'); " +
2410
+ " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - UPDATE prohibited'); " + // allow:hand-rolled-sql — RAISE(ABORT) trigger body, not a query
2293
2411
  'END'
2294
2412
  );
2295
2413
  }
@@ -2355,9 +2473,8 @@ function declareWorm(args) {
2355
2473
  "the SQLite trigger primitive is single-node only");
2356
2474
  }
2357
2475
  var nowMs = Date.now();
2358
- var ins = database.prepare(
2359
- 'INSERT OR REPLACE INTO "_blamejs_worm_tables" (tableName, posture, declaredAt) VALUES (?, ?, ?)'
2360
- );
2476
+ // allow:hand-rolled-sql logical name resolved through frameworkSchema.tableName (the prescribed prefix-aware indirection)
2477
+ var wormTable = frameworkSchema.tableName("_blamejs_worm_tables");
2361
2478
  for (var j = 0; j < args.tables.length; j++) {
2362
2479
  var t = args.tables[j];
2363
2480
  if (t === "audit_log" || t === "consent_log" || t === "audit_checkpoints") {
@@ -2366,7 +2483,14 @@ function declareWorm(args) {
2366
2483
  "use audit-tools.purge for sanctioned deletions");
2367
2484
  }
2368
2485
  _installWormTriggers(database, t);
2369
- ins.run(t, args.posture || null, nowMs);
2486
+ // INSERT-or-replace on the tableName PK: b.sql upsert emits the
2487
+ // portable ON CONFLICT DO UPDATE form (same replace-on-PK semantics
2488
+ // as the prior INSERT OR REPLACE).
2489
+ var wormUp = sql.upsert(wormTable, _SQL_OPTS)
2490
+ .values({ tableName: t, posture: args.posture || null, declaredAt: nowMs })
2491
+ .onConflict(["tableName"]).doUpdateFromExcluded(["posture", "declaredAt"]).toSql();
2492
+ var wormStmt = database.prepare(wormUp.sql);
2493
+ wormStmt.run.apply(wormStmt, wormUp.params);
2370
2494
  audit.safeEmit({
2371
2495
  action: "db.worm.declared",
2372
2496
  outcome: "success",
@@ -2383,9 +2507,11 @@ function _assertWormUnderPosture() {
2383
2507
  if (cluster.isClusterMode()) return;
2384
2508
  var rows;
2385
2509
  try {
2386
- rows = database.prepare(
2387
- 'SELECT tableName FROM "_blamejs_worm_tables"'
2388
- ).all();
2510
+ // allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (prefix-aware), composed via b.sql
2511
+ var wormSel = sql.select(frameworkSchema.tableName("_blamejs_worm_tables"), _SQL_OPTS)
2512
+ .columns(["tableName"]).toSql();
2513
+ var wormSelStmt = database.prepare(wormSel.sql);
2514
+ rows = wormSelStmt.all.apply(wormSelStmt, wormSel.params);
2389
2515
  } catch (_e) { rows = []; }
2390
2516
  if (!rows || rows.length === 0) {
2391
2517
  throw _wormErr("POSTURE_VIOLATION",
@@ -2459,12 +2585,17 @@ function declareRequireDualControl(args) {
2459
2585
  "declareRequireDualControl: args.posture must be a non-empty string or null");
2460
2586
  }
2461
2587
  var nowMs = Date.now();
2462
- var ins = database.prepare(
2463
- 'INSERT OR REPLACE INTO "_blamejs_dual_control_gates" ' +
2464
- '(tableName, posture, m, n, declaredAt) VALUES (?, ?, ?, ?, ?)'
2465
- );
2588
+ // allow:hand-rolled-sql logical name resolved through frameworkSchema.tableName (the prescribed prefix-aware indirection)
2589
+ var gatesTable = frameworkSchema.tableName("_blamejs_dual_control_gates");
2466
2590
  for (var j = 0; j < args.tables.length; j++) {
2467
- ins.run(args.tables[j], args.posture || null, m, n, nowMs);
2591
+ // INSERT-or-replace on the tableName PK via b.sql upsert (same
2592
+ // replace-on-PK semantics as the prior INSERT OR REPLACE).
2593
+ var gateUp = sql.upsert(gatesTable, _SQL_OPTS)
2594
+ .values({ tableName: args.tables[j], posture: args.posture || null,
2595
+ m: m, n: n, declaredAt: nowMs })
2596
+ .onConflict(["tableName"]).doUpdateFromExcluded(["posture", "m", "n", "declaredAt"]).toSql();
2597
+ var gateStmt = database.prepare(gateUp.sql);
2598
+ gateStmt.run.apply(gateStmt, gateUp.params);
2468
2599
  audit.safeEmit({
2469
2600
  action: "db.dual_control.declared",
2470
2601
  outcome: "success",
@@ -2479,9 +2610,11 @@ function _checkDualControlGate(tableName) {
2479
2610
  if (cluster.isClusterMode()) return null;
2480
2611
  var row;
2481
2612
  try {
2482
- row = database.prepare(
2483
- 'SELECT tableName, posture, m, n FROM "_blamejs_dual_control_gates" WHERE tableName = ?'
2484
- ).get(tableName);
2613
+ // allow:hand-rolled-sql — logical name resolved through frameworkSchema.tableName (prefix-aware), composed via b.sql
2614
+ var gateSel = sql.select(frameworkSchema.tableName("_blamejs_dual_control_gates"), _SQL_OPTS)
2615
+ .columns(["tableName", "posture", "m", "n"]).where("tableName", tableName).toSql();
2616
+ var gateSelStmt = database.prepare(gateSel.sql);
2617
+ row = gateSelStmt.get.apply(gateSelStmt, gateSel.params);
2485
2618
  } catch (_e) { return null; }
2486
2619
  return row || null;
2487
2620
  }
@@ -2568,16 +2701,18 @@ function eraseHard(tableName, rowId, opts) {
2568
2701
  var t0 = Date.now();
2569
2702
  var deleted = 0;
2570
2703
  transaction(function () {
2571
- var row = database.prepare(
2572
- 'SELECT * FROM "' + tableName + '" WHERE _id = ?'
2573
- ).get(rowId);
2704
+ // tableName is an operator app table (validateIdentifier'd above), not
2705
+ // a framework table, so it is NOT routed through tableName(); b.sql
2706
+ // quotes it by construction (quoteName) on the local sqlite handle.
2707
+ var rowSel = sql.select(tableName, _SQL_OPTS).where("_id", rowId).toSql();
2708
+ var rowSelStmt = database.prepare(rowSel.sql);
2709
+ var row = rowSelStmt.get.apply(rowSelStmt, rowSel.params);
2574
2710
  if (row) {
2575
2711
  try { cryptoField.eraseRow(tableName, row); } catch (_e) { /* table may have no sealed cols */ }
2576
2712
  }
2577
- var del = database.prepare(
2578
- 'DELETE FROM "' + tableName + '" WHERE _id = ?'
2579
- );
2580
- var result = del.run(rowId);
2713
+ var rowDel = sql.delete(tableName, _SQL_OPTS).where("_id", rowId).toSql();
2714
+ var rowDelStmt = database.prepare(rowDel.sql);
2715
+ var result = rowDelStmt.run.apply(rowDelStmt, rowDel.params);
2581
2716
  deleted = (result && result.changes) || 0;
2582
2717
  // REINDEX rebuilds every index on the table from scratch,
2583
2718
  // dropping the B-tree pages that held the deleted row's index
@@ -2603,7 +2738,7 @@ function eraseHard(tableName, rowId, opts) {
2603
2738
  // Read the audit.tip sidecar file in dataDir and compare to the current
2604
2739
  // audit_log MAX(monotonicCounter). Refuse boot on rollback (current < tip).
2605
2740
  function _checkRollback(dataDirPath) {
2606
- var tipPath = nodePath.join(dataDirPath, "audit.tip");
2741
+ var tipPath = nodePath.join(dataDirPath, frameworkFiles.fileName("auditTip"));
2607
2742
  if (!nodeFs.existsSync(tipPath)) {
2608
2743
  log("no audit.tip sidecar — skipping rollback check (first boot or operator-cleared)");
2609
2744
  return;
@@ -2617,7 +2752,12 @@ function _checkRollback(dataDirPath) {
2617
2752
  ". Either delete it (forfeits rollback protection until next checkpoint) " +
2618
2753
  "or restore from operator backup.");
2619
2754
  }
2620
- var current = database.prepare("SELECT MAX(monotonicCounter) AS m FROM audit_log").get();
2755
+ // The local-SQLite chain table is named "audit_log" (its external-db
2756
+ // counterpart _blamejs_audit_log is the cluster path, handled elsewhere),
2757
+ // so the local read uses that literal name; b.sql quotes it (quoteName).
2758
+ var maxQ = sql.select("audit_log", _SQL_OPTS).max("monotonicCounter", "m").toSql();
2759
+ var maxStmt = database.prepare(maxQ.sql);
2760
+ var current = maxStmt.get.apply(maxStmt, maxQ.params);
2621
2761
  var currentMax = current && current.m ? current.m : 0;
2622
2762
  if (currentMax < tip.atMonotonicCounter) {
2623
2763
  events.emit(events.EVENTS.AUDIT_ROLLBACK_DETECTED, {
@@ -3168,6 +3308,7 @@ module.exports = {
3168
3308
  ["e" + "xec"]: execRaw,
3169
3309
  transaction: transaction,
3170
3310
  hashFor: hashFor,
3311
+ hashCandidatesFor: hashCandidatesFor,
3171
3312
  close: close,
3172
3313
  // flushToDisk — force the live tmpfs SQLite to be re-encrypted to
3173
3314
  // <dataDir>/db.enc immediately. In encrypted-at-rest mode the
@@ -3276,30 +3417,39 @@ module.exports = {
3276
3417
  }
3277
3418
  if (cluster.isClusterMode()) {
3278
3419
  // External-db has no append-only triggers; ordinary DELETE works.
3420
+ // clusterStorage.execute rewrites the BARE logical table name to its
3421
+ // cluster-prefixed form and placeholderizes ?→$N, so b.sql emits the
3422
+ // bare name (no quoteName) here.
3279
3423
  var cs = clusterStorage();
3280
- var d = await cs.execute(
3281
- "DELETE FROM audit_log WHERE monotonicCounter <= ?", [lastPurgedCounter]
3282
- );
3283
- var dc = await cs.execute(
3284
- "DELETE FROM audit_checkpoints WHERE atMonotonicCounter <= ?", [lastPurgedCounter]
3285
- );
3424
+ var clusterLogDel = sql.delete("audit_log")
3425
+ .where("monotonicCounter", "<=", lastPurgedCounter).toSql();
3426
+ var d = await cs.execute(clusterLogDel.sql, clusterLogDel.params);
3427
+ var clusterChkDel = sql.delete("audit_checkpoints")
3428
+ .where("atMonotonicCounter", "<=", lastPurgedCounter).toSql();
3429
+ var dc = await cs.execute(clusterChkDel.sql, clusterChkDel.params);
3286
3430
  return { rowsDeleted: d.rowCount || 0, checkpointsDeleted: dc.rowCount || 0 };
3287
3431
  }
3288
3432
  // Single-node: drop triggers, delete, recreate triggers — all in
3289
3433
  // one transaction so a crash mid-operation doesn't leave the
3290
- // table writable to general code.
3434
+ // table writable to general code. The local chain tables are named
3435
+ // "audit_log" / "audit_checkpoints" (no _blamejs_ prefix locally);
3436
+ // b.sql quotes them (quoteName) on the local handle.
3291
3437
  var rowsDeleted = 0;
3292
3438
  var checkpointsDeleted = 0;
3293
3439
  transaction(function () {
3440
+ // allow:hand-rolled-sql — b.sql has no DROP TRIGGER builder; framework-controlled trigger name, append-only re-installed below
3294
3441
  runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_log"');
3442
+ // allow:hand-rolled-sql — b.sql has no DROP TRIGGER builder; framework-controlled trigger name, append-only re-installed below
3295
3443
  runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_checkpoints"');
3296
- var d = database.prepare(
3297
- "DELETE FROM audit_log WHERE monotonicCounter <= ?"
3298
- ).run(lastPurgedCounter);
3444
+ var logDel = sql.delete("audit_log", _SQL_OPTS)
3445
+ .where("monotonicCounter", "<=", lastPurgedCounter).toSql();
3446
+ var logDelStmt = database.prepare(logDel.sql);
3447
+ var d = logDelStmt.run.apply(logDelStmt, logDel.params);
3299
3448
  rowsDeleted = (d && d.changes) || 0;
3300
- var dc = database.prepare(
3301
- "DELETE FROM audit_checkpoints WHERE atMonotonicCounter <= ?"
3302
- ).run(lastPurgedCounter);
3449
+ var chkDel = sql.delete("audit_checkpoints", _SQL_OPTS)
3450
+ .where("atMonotonicCounter", "<=", lastPurgedCounter).toSql();
3451
+ var chkDelStmt = database.prepare(chkDel.sql);
3452
+ var dc = chkDelStmt.run.apply(chkDelStmt, chkDel.params);
3303
3453
  checkpointsDeleted = (dc && dc.changes) || 0;
3304
3454
  _installAppendOnlyTriggers(database);
3305
3455
  });
@@ -3401,7 +3551,7 @@ module.exports = {
3401
3551
  // Helper for audit.checkpoint to write the rollback-detection sidecar
3402
3552
  _writeAuditTip: function (tip) {
3403
3553
  if (!dataDir) return;
3404
- var tipPath = nodePath.join(dataDir, "audit.tip");
3554
+ var tipPath = nodePath.join(dataDir, frameworkFiles.fileName("auditTip"));
3405
3555
  atomicFile.writeSync(tipPath, JSON.stringify(tip, null, 2), { fileMode: 0o600 });
3406
3556
  },
3407
3557
  };