@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
@@ -55,7 +55,9 @@ var cluster = require("./cluster");
55
55
  var clusterStorage = require("./cluster-storage");
56
56
  var { generateToken } = require("./crypto");
57
57
  var cryptoField = require("./crypto-field");
58
+ var frameworkSchema = require("./framework-schema");
58
59
  var safeSql = require("./safe-sql");
60
+ var sql = require("./sql");
59
61
  var dbRoleContext = require("./db-role-context");
60
62
  var handlers = require("./handlers");
61
63
  var { boot } = require("./log");
@@ -101,19 +103,36 @@ var _externalStore = null;
101
103
  // the worst case.
102
104
  var FRAMEWORK_SQL_TIMEOUT_MS = C.TIME.seconds(30);
103
105
 
106
+ // b.sql opts for every framework-table statement: thread the ACTIVE backend
107
+ // dialect (clusterStorage.dialect() — "sqlite" single-node, "postgres" |
108
+ // "mysql" in cluster mode) so the emitted identifier quoting + dialect
109
+ // idioms (ON CONFLICT vs ON DUPLICATE KEY) match the backend the SQL
110
+ // dispatches to. Defaulting to "sqlite" works on Postgres only by accident
111
+ // (both double-quote identifiers) and emits the wrong quoting on MySQL.
112
+ // clusterStorage.execute still rewrites table names + translates `?`
113
+ // placeholders at dispatch; this controls only the builder-side quoting +
114
+ // idiom selection.
115
+ function _sqlOpts() { return { dialect: clusterStorage.dialect() }; }
116
+
104
117
  // ---- Resilience-wrapped SQL operations (audit-specific reads) ----
105
118
  // Chain APPEND lives in chain-writer (race-safe via mutex, retry, timeout).
106
119
  // The wrappers below cover audit-specific reads/writes that aren't part
107
120
  // of the chain append: checkpoint queries, verifyCheckpoints reads,
108
121
  // audit-tip cluster-row updates.
109
122
 
123
+ // Framework-state reads compose b.sql with BARE logical table names —
124
+ // clusterStorage rewrites the framework names to their configured-prefix
125
+ // form and placeholderizes per dialect; b.sql quotes the camelCase columns
126
+ // and runs the output validator.
110
127
  async function _readLastCheckpointCounter() {
128
+ var built = sql.select("audit_checkpoints", _sqlOpts())
129
+ .columns(["atMonotonicCounter"])
130
+ .orderBy("atMonotonicCounter", "desc")
131
+ .limit(1)
132
+ .toSql();
111
133
  return await safeAsync.withTimeout(
112
134
  safeAsync.asyncRetry(function () {
113
- return clusterStorage.executeOne(
114
- "SELECT atMonotonicCounter FROM audit_checkpoints " +
115
- "ORDER BY atMonotonicCounter DESC LIMIT 1"
116
- );
135
+ return clusterStorage.executeOne(built.sql, built.params);
117
136
  }),
118
137
  FRAMEWORK_SQL_TIMEOUT_MS,
119
138
  { name: "audit.readLastCheckpoint" }
@@ -121,11 +140,12 @@ async function _readLastCheckpointCounter() {
121
140
  }
122
141
 
123
142
  async function _readAllAuditRowsAsc() {
143
+ var built = sql.select("audit_log", _sqlOpts())
144
+ .orderBy("monotonicCounter", "asc")
145
+ .toSql();
124
146
  return await safeAsync.withTimeout(
125
147
  safeAsync.asyncRetry(function () {
126
- return clusterStorage.executeAll(
127
- 'SELECT * FROM "audit_log" ORDER BY monotonicCounter ASC'
128
- );
148
+ return clusterStorage.executeAll(built.sql, built.params);
129
149
  }),
130
150
  FRAMEWORK_SQL_TIMEOUT_MS,
131
151
  { name: "audit.readAllRowsAsc" }
@@ -133,11 +153,12 @@ async function _readAllAuditRowsAsc() {
133
153
  }
134
154
 
135
155
  async function _readAllCheckpointsAsc() {
156
+ var built = sql.select("audit_checkpoints", _sqlOpts())
157
+ .orderBy("atMonotonicCounter", "asc")
158
+ .toSql();
136
159
  return await safeAsync.withTimeout(
137
160
  safeAsync.asyncRetry(function () {
138
- return clusterStorage.executeAll(
139
- "SELECT * FROM audit_checkpoints ORDER BY atMonotonicCounter ASC"
140
- );
161
+ return clusterStorage.executeAll(built.sql, built.params);
141
162
  }),
142
163
  FRAMEWORK_SQL_TIMEOUT_MS,
143
164
  { name: "audit.readAllCheckpoints" }
@@ -145,12 +166,13 @@ async function _readAllCheckpointsAsc() {
145
166
  }
146
167
 
147
168
  async function _readAuditRowHashAtCounter(counter) {
169
+ var built = sql.select("audit_log", _sqlOpts())
170
+ .columns(["rowHash"])
171
+ .where("monotonicCounter", counter)
172
+ .toSql();
148
173
  return await safeAsync.withTimeout(
149
174
  safeAsync.asyncRetry(function () {
150
- return clusterStorage.executeOne(
151
- "SELECT rowHash FROM audit_log WHERE monotonicCounter = ?",
152
- [counter]
153
- );
175
+ return clusterStorage.executeOne(built.sql, built.params);
154
176
  }),
155
177
  FRAMEWORK_SQL_TIMEOUT_MS,
156
178
  { name: "audit.readRowHashAtCounter" }
@@ -158,26 +180,37 @@ async function _readAuditRowHashAtCounter(counter) {
158
180
  }
159
181
 
160
182
  async function _insertAuditRow(allCols, values) {
161
- // No retry — non-idempotent. Timeout only.
162
- var placeholders = allCols.map(function () { return "?"; }).join(", ");
163
- var quoted = allCols.map(function (c) { return '"' + c + '"'; }).join(", ");
183
+ // No retry — non-idempotent. Timeout only. Map each column to its
184
+ // positional value and bind as a row object (the unambiguous b.sql form;
185
+ // a flat value array whose first element is a Buffer would be misread as
186
+ // an array-of-rows). BARE logical table name → clusterStorage rewrites.
187
+ var rowObj = {};
188
+ for (var i = 0; i < allCols.length; i++) rowObj[allCols[i]] = values[i];
189
+ var built = sql.insert("audit_log", _sqlOpts())
190
+ .columns(allCols)
191
+ .values(rowObj)
192
+ .toSql();
164
193
  return await safeAsync.withTimeout(
165
- clusterStorage.execute(
166
- "INSERT INTO audit_log (" + quoted + ") VALUES (" + placeholders + ")",
167
- values
168
- ),
194
+ clusterStorage.execute(built.sql, built.params),
169
195
  FRAMEWORK_SQL_TIMEOUT_MS,
170
196
  { name: "audit.insertRow" }
171
197
  );
172
198
  }
173
199
 
200
+ var _CHECKPOINT_COLS = [
201
+ "_id", "createdAt", "atMonotonicCounter", "atRowHash",
202
+ "signature", "publicKeyFingerprint", "fencingToken",
203
+ ];
204
+
174
205
  async function _insertCheckpoint(values) {
206
+ var rowObj = {};
207
+ for (var i = 0; i < _CHECKPOINT_COLS.length; i++) rowObj[_CHECKPOINT_COLS[i]] = values[i];
208
+ var built = sql.insert("audit_checkpoints", _sqlOpts())
209
+ .columns(_CHECKPOINT_COLS)
210
+ .values(rowObj)
211
+ .toSql();
175
212
  return await safeAsync.withTimeout(
176
- clusterStorage.execute(
177
- "INSERT INTO audit_checkpoints (_id, createdAt, atMonotonicCounter, atRowHash, signature, publicKeyFingerprint, fencingToken) " +
178
- "VALUES (?, ?, ?, ?, ?, ?, ?)",
179
- values
180
- ),
213
+ clusterStorage.execute(built.sql, built.params),
181
214
  FRAMEWORK_SQL_TIMEOUT_MS,
182
215
  { name: "audit.insertCheckpoint" }
183
216
  );
@@ -198,24 +231,79 @@ async function _upsertAuditTip(counter, rowHash, signedAt, fencingToken) {
198
231
  // as ClusterError(code='FENCED_OUT', permanent=true) so the
199
232
  // dispatching node knows it's been superseded and should step down
200
233
  // rather than retry.
201
- var result = await safeAsync.withTimeout(
202
- clusterStorage.execute(
203
- "INSERT INTO _blamejs_audit_tip " +
204
- " (scope, atMonotonicCounter, rowHash, signedAt, fencingToken) " +
205
- "VALUES ('audit', ?, ?, ?, ?) " +
206
- "ON CONFLICT (scope) DO UPDATE SET " +
207
- " atMonotonicCounter = EXCLUDED.atMonotonicCounter, " +
208
- " rowHash = EXCLUDED.rowHash, " +
209
- " signedAt = EXCLUDED.signedAt, " +
210
- " fencingToken = EXCLUDED.fencingToken " +
211
- "WHERE _blamejs_audit_tip.fencingToken <= EXCLUDED.fencingToken " +
212
- "RETURNING fencingToken",
213
- [counter, rowHash, signedAt, fencingToken]
214
- ),
215
- FRAMEWORK_SQL_TIMEOUT_MS,
216
- { name: "audit.upsertAuditTip" }
217
- );
218
- if (!result.rows || result.rows.length === 0) {
234
+ // Single atomic INSERT … ON CONFLICT(scope) DO UPDATE … WHERE … RETURNING
235
+ // via b.sql. BARE logical table name (`_blamejs_audit_tip`) — clusterStorage
236
+ // rewrites it (and the same bare name inside the conflictWhere fence) to
237
+ // the configured prefix and placeholderizes. The fenced WHERE enforces the
238
+ // monotonic-non-decreasing fencingToken at the DB level; on rejection
239
+ // RETURNING produces 0 rows.
240
+ // The audit-tip is external-only; its LOGICAL name IS the
241
+ // `_blamejs_`-prefixed name (self-mapped in LOCAL_TO_EXTERNAL), passed
242
+ // bare to b.sql so clusterStorage rewrites it (and the same bare name
243
+ // inside the guarded fence) to the configured prefix.
244
+ //
245
+ // The fence `<table>.<fencingToken> <= EXCLUDED.<fencingToken>` references
246
+ // both the EXISTING row (table-qualified) and the PROPOSED row (EXCLUDED on
247
+ // Postgres/SQLite, VALUES() on MySQL ON DUPLICATE KEY UPDATE), and the
248
+ // identifier quoting differs per dialect — so the raw fragment is built
249
+ // dialect-aware: safeSql.quoteQualified for the table-qualified existing
250
+ // column (backticks on MySQL, double-quotes on PG/SQLite), and the
251
+ // proposed-row reference spelled per dialect (the EXCLUDED keyword has no
252
+ // MySQL equivalent — ON DUPLICATE KEY UPDATE uses VALUES(col)). guardColumn
253
+ // tells the MySQL upsert renderer that the fence protects `fencingToken`,
254
+ // so the IF(<fence>, …, col) wrap on the other SET targets evaluates
255
+ // against the fencingToken column's PRE-update value (the IF-eval-order
256
+ // hazard) and the guard column is assigned last.
257
+ var dialect = clusterStorage.dialect();
258
+ var fenceExisting = safeSql.quoteQualified(["_blamejs_audit_tip", "fencingToken"], dialect); // allow:hand-rolled-sql
259
+ var fenceProposed = dialect === "mysql"
260
+ ? "VALUES(" + safeSql.quoteIdentifier("fencingToken", "mysql") + ")"
261
+ : "EXCLUDED." + safeSql.quoteIdentifier("fencingToken", dialect);
262
+ var tipFence = fenceExisting + " <= " + fenceProposed;
263
+ var tipBuilt = sql.upsert("_blamejs_audit_tip", { dialect: dialect }) // allow:hand-rolled-sql
264
+ .columns(["scope", "atMonotonicCounter", "rowHash", "signedAt", "fencingToken"])
265
+ .values({
266
+ scope: "audit",
267
+ atMonotonicCounter: counter,
268
+ rowHash: rowHash,
269
+ signedAt: signedAt,
270
+ fencingToken: fencingToken,
271
+ })
272
+ .onConflict(["scope"])
273
+ .doUpdateFromExcluded(["atMonotonicCounter", "rowHash", "signedAt", "fencingToken"])
274
+ .conflictWhere(tipFence, [], { guardColumn: "fencingToken" })
275
+ .returning(["fencingToken"])
276
+ .toSql();
277
+ var fenced;
278
+ if (dialect === "mysql") {
279
+ // MySQL ON DUPLICATE KEY UPDATE has no WHERE + no RETURNING — the fence
280
+ // becomes an IF(<guard>, VALUES(col), col) per SET target, so a fenced-out
281
+ // (strictly-lower) token leaves every column at its stored value and the
282
+ // statement still "succeeds". Detection therefore can't read RETURNING
283
+ // rows: run the upsert, then read the stored fencingToken back (the b.sql
284
+ // builder hands us the keyed readback SELECT) and compare. If the stored
285
+ // token is ABOVE our incoming one, a higher-token successor won and we
286
+ // were fenced out.
287
+ await safeAsync.withTimeout(
288
+ clusterStorage.execute(tipBuilt.sql, tipBuilt.params),
289
+ FRAMEWORK_SQL_TIMEOUT_MS,
290
+ { name: "audit.upsertAuditTip" }
291
+ );
292
+ var back = await safeAsync.withTimeout(
293
+ clusterStorage.executeOne(tipBuilt.readbackSql.sql, tipBuilt.readbackSql.params),
294
+ FRAMEWORK_SQL_TIMEOUT_MS,
295
+ { name: "audit.upsertAuditTip.readback" }
296
+ );
297
+ fenced = !back || Number(back.fencingToken) > Number(fencingToken);
298
+ } else {
299
+ var result = await safeAsync.withTimeout(
300
+ clusterStorage.execute(tipBuilt.sql, tipBuilt.params),
301
+ FRAMEWORK_SQL_TIMEOUT_MS,
302
+ { name: "audit.upsertAuditTip" }
303
+ );
304
+ fenced = !result.rows || result.rows.length === 0;
305
+ }
306
+ if (fenced) {
219
307
  throw new ClusterError(
220
308
  "FENCED_OUT",
221
309
  "audit-tip update rejected: incoming fencingToken=" + fencingToken +
@@ -617,11 +705,18 @@ function useStore(store) {
617
705
  //
618
706
  // Self-logging (PCI DSS 10.2.3): every read of audit_log is itself recorded
619
707
  // as an 'audit.read' event before the query runs, so an exfiltration attempt
620
- // is forensically visible. The recursion guard (_selfLogging flag) prevents
621
- // the audit.read recording from triggering its own self-log; queries
622
- // SPECIFICALLY filtering for action='audit.read' don't auto-log either
623
- // (otherwise legitimate audit auditing produces a Russell-set spiral).
624
- var _selfLogging = false;
708
+ // is forensically visible. The self-log goes through record() (which never
709
+ // re-enters query()), so the only re-entrancy to suppress is a query whose
710
+ // own criteria filters for action='audit.read' — those don't auto-log
711
+ // (otherwise legitimate audit-of-audits produces a Russell-set spiral, and
712
+ // the self-read itself would log a read). That suppression is decided PER
713
+ // INVOCATION from the call's own criteria.action, never from shared mutable
714
+ // state: a prior design used a module-global `_selfLogging` boolean toggled
715
+ // across record()'s await (chain mutex + SQL yield), so a CONCURRENT
716
+ // query() racing a mid-flight self-log observed the flag set and silently
717
+ // skipped emitting its own audit.read — under-logging reads exactly when
718
+ // load is highest. b.audit.query is reachable from concurrent request
719
+ // handlers, so the guard MUST be invocation-local.
625
720
 
626
721
  /**
627
722
  * @primitive b.audit.query
@@ -633,9 +728,10 @@ var _selfLogging = false;
633
728
  * Read audit rows matching the criteria, returning unsealed rows for
634
729
  * the auditor's view. Every call self-logs an `audit.read` event before
635
730
  * returning (PCI DSS 10.2.3) so exfiltration attempts are forensically
636
- * visible; recursion is guarded so the self-log doesn't trigger its own
637
- * self-log. Plain-field criteria translate into derived-hash equality
638
- * where the column is sealed.
731
+ * visible; the self-log is suppressed per-invocation only for a query
732
+ * whose own criteria targets `action: "audit.read"`, so concurrent reads
733
+ * each record their own `audit.read`. Plain-field criteria translate into
734
+ * derived-hash equality where the column is sealed.
639
735
  *
640
736
  * @opts
641
737
  * from: number | Date | string, // recordedAt >=
@@ -658,21 +754,21 @@ var _selfLogging = false;
658
754
  */
659
755
  async function query(criteria) {
660
756
  criteria = criteria || {};
661
- if (!_selfLogging && criteria.action !== "audit.read") {
662
- _selfLogging = true;
663
- try {
664
- await record({
665
- actor: criteria.actor || {},
666
- action: "audit.read",
667
- outcome: "success",
668
- metadata: {
669
- criteria: _redactCriteria(criteria),
670
- traceId: criteria.traceId || null,
671
- },
672
- });
673
- } finally {
674
- _selfLogging = false;
675
- }
757
+ // Suppress the self-log ONLY for a query whose own criteria targets
758
+ // action='audit.read' (the self-read's shape + audit-of-audits). This is
759
+ // derived from THIS call's criteria — no shared flag — so concurrent
760
+ // reads each emit their own audit.read instead of one racing read
761
+ // swallowing another's self-log.
762
+ if (criteria.action !== "audit.read") {
763
+ await record({
764
+ actor: criteria.actor || {},
765
+ action: "audit.read",
766
+ outcome: "success",
767
+ metadata: {
768
+ criteria: _redactCriteria(criteria),
769
+ traceId: criteria.traceId || null,
770
+ },
771
+ });
676
772
  }
677
773
 
678
774
  // In single-node mode the query builder gives us field-crypto unsealing
@@ -700,35 +796,41 @@ async function query(criteria) {
700
796
  }
701
797
 
702
798
  async function _queryCluster(criteria) {
703
- var conds = [];
704
- var params = [];
705
- if (criteria.from) {
706
- conds.push("recordedAt >= ?");
707
- params.push(_toMs(criteria.from));
708
- }
709
- if (criteria.to) {
710
- conds.push("recordedAt <= ?");
711
- params.push(_toMs(criteria.to));
712
- }
799
+ // Compose the criteria onto a b.sql SELECT with a BARE logical table name
800
+ // (clusterStorage rewrites + placeholderizes). Sealed-field criteria
801
+ // translate to the derived-hash column via cryptoField.lookupHash exactly
802
+ // as before; b.sql quotes every identifier and binds every value.
803
+ var qb = sql.select("audit_log", _sqlOpts());
804
+ if (criteria.from) qb.whereOp("recordedAt", ">=", _toMs(criteria.from));
805
+ if (criteria.to) qb.whereOp("recordedAt", "<=", _toMs(criteria.to));
713
806
  if (criteria.actorUserId) {
714
807
  var auh = cryptoField.lookupHash("audit_log", "actorUserId", criteria.actorUserId);
715
- if (auh) { conds.push(auh.field + " = ?"); params.push(auh.value); }
808
+ if (auh) {
809
+ // Dual-read across the keyed-MAC flip so an actor query still returns
810
+ // audit rows written under the legacy salted-sha3 actor digest.
811
+ var auv = [auh.value];
812
+ if (auh.legacyValue != null && auh.legacyValue !== auh.value) auv.push(auh.legacyValue);
813
+ qb.whereIn(auh.field, auv);
814
+ }
716
815
  }
717
816
  if (criteria.resourceId) {
718
817
  var rh = cryptoField.lookupHash("audit_log", "resourceId", criteria.resourceId);
719
- if (rh) { conds.push(rh.field + " = ?"); params.push(rh.value); }
818
+ if (rh) {
819
+ var rhv = [rh.value];
820
+ if (rh.legacyValue != null && rh.legacyValue !== rh.value) rhv.push(rh.legacyValue);
821
+ qb.whereIn(rh.field, rhv);
822
+ }
720
823
  }
721
- if (criteria.action) { conds.push("action = ?"); params.push(criteria.action); }
722
- if (criteria.resourceKind) { conds.push("resourceKind = ?"); params.push(criteria.resourceKind); }
723
- if (criteria.outcome) { conds.push("outcome = ?"); params.push(criteria.outcome); }
824
+ if (criteria.action) qb.where("action", criteria.action);
825
+ if (criteria.resourceKind) qb.where("resourceKind", criteria.resourceKind);
826
+ if (criteria.outcome) qb.where("outcome", criteria.outcome);
724
827
 
725
- var sql = "SELECT * FROM audit_log";
726
- if (conds.length > 0) sql += " WHERE " + conds.join(" AND ");
727
- sql += " ORDER BY monotonicCounter ASC";
728
- if (criteria.limit != null) { sql += " LIMIT ?"; params.push(criteria.limit); }
729
- if (criteria.offset != null) { sql += " OFFSET ?"; params.push(criteria.offset); }
828
+ qb.orderBy("monotonicCounter", "asc");
829
+ if (criteria.limit != null) qb.limit(criteria.limit);
830
+ if (criteria.offset != null) qb.offset(criteria.offset);
730
831
 
731
- var rows = await clusterStorage.executeAll(sql, params);
832
+ var built = qb.toSql();
833
+ var rows = await clusterStorage.executeAll(built.sql, built.params);
732
834
  return rows.map(function (row) { return cryptoField.unsealRow("audit_log", row); });
733
835
  }
734
836
 
@@ -840,12 +942,14 @@ async function checkpoint(opts) {
840
942
  cluster.requireLeader();
841
943
  opts = opts || {};
842
944
 
945
+ var tipReadBuilt = sql.select("audit_log", _sqlOpts())
946
+ .columns(["_id", "monotonicCounter", "rowHash"])
947
+ .orderBy("monotonicCounter", "desc")
948
+ .limit(1)
949
+ .toSql();
843
950
  var tip = await safeAsync.withTimeout(
844
951
  safeAsync.asyncRetry(function () {
845
- return clusterStorage.executeOne(
846
- "SELECT _id, monotonicCounter, rowHash FROM audit_log " +
847
- "ORDER BY monotonicCounter DESC LIMIT 1"
848
- );
952
+ return clusterStorage.executeOne(tipReadBuilt.sql, tipReadBuilt.params);
849
953
  }),
850
954
  FRAMEWORK_SQL_TIMEOUT_MS,
851
955
  { name: "audit.checkpoint.readTip" }
@@ -890,16 +994,31 @@ async function checkpoint(opts) {
890
994
  if (cluster.isClusterMode()) {
891
995
  await _upsertAuditTip(counter, tip.rowHash, String(createdAt), fencingToken);
892
996
  } else {
893
- try {
894
- db()._writeAuditTip({
895
- atMonotonicCounter: counter,
896
- atRowHash: tip.rowHash,
897
- anchoredAt: createdAt,
898
- checkpointId: ckptId,
899
- publicKeyFingerprint: pubFp,
900
- version: 1,
901
- });
902
- } catch (_e) { /* best effort */ }
997
+ // Flush the anchored rows to durable db.enc BEFORE advancing the durable
998
+ // tip sidecar. Otherwise a crash between this checkpoint and the next
999
+ // encrypt leaves the tip referencing a counter not yet on durable disk;
1000
+ // on reboot the rollback detector reads the tip ahead of the restored
1001
+ // db.enc and FALSELY refuses boot as a rollback/deletion, even though the
1002
+ // chain is intact and only unflushed rows were lost in a normal crash.
1003
+ // Rows-before-tip is the correct durability ordering. flushToDisk is a
1004
+ // no-op outside encrypted-at-rest mode (the live file is already durable).
1005
+ // The tip advances ONLY if the flush succeeded, so the durable tip can
1006
+ // never get ahead of the durable rows.
1007
+ var rowsFlushed = false;
1008
+ try { db().flushToDisk(); rowsFlushed = true; }
1009
+ catch (_e) { /* flush failed - do not advance the tip ahead of the rows */ }
1010
+ if (rowsFlushed) {
1011
+ try {
1012
+ db()._writeAuditTip({
1013
+ atMonotonicCounter: counter,
1014
+ atRowHash: tip.rowHash,
1015
+ anchoredAt: createdAt,
1016
+ checkpointId: ckptId,
1017
+ publicKeyFingerprint: pubFp,
1018
+ version: 1,
1019
+ });
1020
+ } catch (_e) { /* best effort */ }
1021
+ }
903
1022
  }
904
1023
 
905
1024
  return {
@@ -947,28 +1066,28 @@ async function verifyCheckpoints() {
947
1066
 
948
1067
  if (rows.length === 0) return { ok: true, checkpointsVerified: 0 };
949
1068
 
950
- var currentFp = auditSign.getPublicKeyFingerprint();
951
- var currentPub = auditSign.getPublicKey();
952
-
953
1069
  for (var i = 0; i < rows.length; i++) {
954
1070
  var c = rows[i];
955
- // Public key check: only the current key is accepted there is no
956
- // key-history table, so any rotation requires re-signing existing
957
- // checkpoints. A fingerprint mismatch fails verification.
958
- if (c.publicKeyFingerprint !== currentFp) {
1071
+ // Resolve the public key the checkpoint was signed under: the current
1072
+ // key, or a rotated-out key from the unsealed public-key history. A
1073
+ // rotation archives the prior public key, so a checkpoint anchored
1074
+ // before the rotation still verifies (no re-signing required). A
1075
+ // fingerprint with no recorded key is the genuine break (key rotated
1076
+ // away with no history, or a forged fingerprint).
1077
+ var pub = auditSign.getPublicKeyByFingerprint(c.publicKeyFingerprint);
1078
+ if (!pub) {
959
1079
  return {
960
1080
  ok: false,
961
1081
  checkpointsVerified: i,
962
1082
  breakAt: i,
963
1083
  checkpointId: c._id,
964
- reason: "public key fingerprint mismatch (key rotated without history?)",
965
- expected: currentFp,
1084
+ reason: "no audit-signing key on record for this checkpoint's fingerprint (key rotated without history?)",
966
1085
  actual: c.publicKeyFingerprint,
967
1086
  };
968
1087
  }
969
1088
  var payload = _checkpointPayload(Number(c.atMonotonicCounter), c.atRowHash, Number(c.createdAt));
970
1089
  var sigBuf = Buffer.isBuffer(c.signature) ? c.signature : Buffer.from(c.signature);
971
- if (!auditSign.verify(payload, sigBuf, currentPub)) {
1090
+ if (!auditSign.verify(payload, sigBuf, pub)) {
972
1091
  return {
973
1092
  ok: false,
974
1093
  checkpointsVerified: i,
@@ -1515,10 +1634,15 @@ function bindActor(actorId, opts) {
1515
1634
  function generateActorBindingTriggerSql(opts) {
1516
1635
  opts = opts || {};
1517
1636
  var columnRaw = opts.column || "actorUserId";
1518
- var tableNameRaw = opts.tableName || "_blamejs_audit_log";
1637
+ // Default resolves through frameworkSchema.tableName so the configurable
1638
+ // framework-table prefix flows into the operator-applied trigger DDL.
1639
+ var tableNameRaw = opts.tableName || frameworkSchema.tableName("audit_log");
1519
1640
  var allowRoles = Array.isArray(opts.allowRoles) ? opts.allowRoles : [];
1520
- var fnNameRaw = "_blamejs_audit_actor_binding_check";
1521
- var trigNameRaw = "_blamejs_audit_actor_binding_trig";
1641
+ // Trigger function + trigger object NAMES (not framework tables — they have
1642
+ // no LOCAL_TO_EXTERNAL mapping and carry no prefix). assertSegregation
1643
+ // looks them up under these exact names.
1644
+ var fnNameRaw = "_blamejs_audit_actor_binding_check"; // allow:hand-rolled-sql
1645
+ var trigNameRaw = "_blamejs_audit_actor_binding_trig"; // allow:hand-rolled-sql
1522
1646
  // Quote-and-validate every identifier through safeSql.quoteIdentifier
1523
1647
  // so operator-supplied opts.column / opts.tableName / opts.roleMappingFn
1524
1648
  // can't reach raw concatenation. PostgreSQL + SQLite both use the
@@ -1537,8 +1661,14 @@ function generateActorBindingTriggerSql(opts) {
1537
1661
  var roleMatch = qRoleMapFn
1538
1662
  ? " IF " + qRoleMapFn + "(NEW." + qColumn + ") IS DISTINCT FROM current_user THEN\n"
1539
1663
  : " IF NEW." + qColumn + " IS DISTINCT FROM current_user THEN\n";
1664
+ // Operator-applied plpgsql trigger DDL — a CREATE FUNCTION body + RAISE
1665
+ // EXCEPTION + CREATE/DROP TRIGGER, none of which b.sql's verb builders
1666
+ // model. Every identifier is quoted through safeSql.quoteIdentifier above;
1667
+ // the table name resolves via frameworkSchema.tableName, so the prefix is
1668
+ // honored. allow:hand-rolled-sql — this is migration-script generation,
1669
+ // not a framework-state DML path.
1540
1670
  var up =
1541
- "CREATE OR REPLACE FUNCTION " + qFn + "() RETURNS trigger AS $$\n" +
1671
+ "CREATE OR REPLACE FUNCTION " + qFn + "() RETURNS trigger AS $$\n" + // allow:hand-rolled-sql
1542
1672
  "BEGIN\n" +
1543
1673
  allowList +
1544
1674
  roleMatch +
@@ -1548,12 +1678,12 @@ function generateActorBindingTriggerSql(opts) {
1548
1678
  " RETURN NEW;\n" +
1549
1679
  "END;\n" +
1550
1680
  "$$ LANGUAGE plpgsql;\n" +
1551
- "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" +
1552
- "CREATE TRIGGER " + qTrig + "\n" +
1681
+ "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" + // allow:hand-rolled-sql
1682
+ "CREATE TRIGGER " + qTrig + "\n" + // allow:hand-rolled-sql
1553
1683
  " BEFORE INSERT ON " + qTable + "\n" +
1554
1684
  " FOR EACH ROW EXECUTE FUNCTION " + qFn + "();\n";
1555
1685
  var down =
1556
- "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" +
1686
+ "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" + // allow:hand-rolled-sql
1557
1687
  "DROP FUNCTION IF EXISTS " + qFn + "();\n";
1558
1688
  return { up: up, down: down, functionName: fnNameRaw, triggerName: trigNameRaw };
1559
1689
  }
@@ -1592,14 +1722,20 @@ async function assertSegregation(opts) {
1592
1722
  throw new AuditSegregationError("audit/segregation-no-db",
1593
1723
  "audit.assertSegregation: opts.db with a query() method is required");
1594
1724
  }
1595
- var fnName = opts.functionName || "_blamejs_audit_actor_binding_check";
1596
- var trigName = opts.triggerName || "_blamejs_audit_actor_binding_trig";
1725
+ // Trigger / function object NAMES (not framework tables — they have no
1726
+ // LOCAL_TO_EXTERNAL mapping and carry no prefix). They must match the
1727
+ // names generateActorBindingTriggerSql emits.
1728
+ var fnName = opts.functionName || "_blamejs_audit_actor_binding_check"; // allow:hand-rolled-sql
1729
+ var trigName = opts.triggerName || "_blamejs_audit_actor_binding_trig"; // allow:hand-rolled-sql
1730
+ // Operator-DB system-catalog introspection (Postgres pg_proc / pg_trigger,
1731
+ // $N-native, against the operator-supplied db.query) — not a framework
1732
+ // table, so b.sql's verb builders don't apply.
1597
1733
  var fnRes = await db.query(
1598
- "SELECT 1 FROM pg_proc WHERE proname = $1 LIMIT 1", [fnName]
1734
+ "SELECT 1 FROM pg_proc WHERE proname = $1 LIMIT 1", [fnName] // allow:hand-rolled-sql
1599
1735
  );
1600
1736
  var fnPresent = !!(fnRes && fnRes.rows && fnRes.rows.length > 0);
1601
1737
  var trigRes = await db.query(
1602
- "SELECT 1 FROM pg_trigger WHERE tgname = $1 LIMIT 1", [trigName]
1738
+ "SELECT 1 FROM pg_trigger WHERE tgname = $1 LIMIT 1", [trigName] // allow:hand-rolled-sql
1603
1739
  );
1604
1740
  var trigPresent = !!(trigRes && trigRes.rows && trigRes.rows.length > 0);
1605
1741
  var missing = [];