@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
@@ -255,6 +255,221 @@ function testV0882NewPostures() {
255
255
  euP.indexOf("dsa") !== -1 && euP.indexOf("dga") !== -1 && euP.indexOf("eu-cer") !== -1);
256
256
  }
257
257
 
258
+ // ---- Seal-envelope floor (POSTURE_DEFAULTS data + registerTable gate) ----
259
+
260
+ function testSealEnvelopeFloorData() {
261
+ check("postureDefault(hipaa, sealEnvelopeFloor) === 'aad'",
262
+ b.compliance.postureDefault("hipaa", "sealEnvelopeFloor") === "aad");
263
+ check("postureDefault(pci-dss, sealEnvelopeFloor) === 'aad'",
264
+ b.compliance.postureDefault("pci-dss", "sealEnvelopeFloor") === "aad");
265
+ // Absent on non-regulated / unfloored postures → null (back-compat).
266
+ check("postureDefault(gdpr, sealEnvelopeFloor) === null",
267
+ b.compliance.postureDefault("gdpr", "sealEnvelopeFloor") === null);
268
+ check("postureDefault(soc2, sealEnvelopeFloor) === null",
269
+ b.compliance.postureDefault("soc2", "sealEnvelopeFloor") === null);
270
+ check("postureDefault(dora, sealEnvelopeFloor) === null",
271
+ b.compliance.postureDefault("dora", "sealEnvelopeFloor") === null);
272
+ }
273
+
274
+ function testRegisterTableFloorBackCompatUnpinned() {
275
+ // No posture pinned: a plain sealed table registers exactly as before.
276
+ _resetState();
277
+ b.cryptoField.clearForTest();
278
+ var threw = null;
279
+ try {
280
+ b.cryptoField.registerTable("ct_floor_unpinned", { sealedFields: ["x"] });
281
+ } catch (e) { threw = e; }
282
+ check("plain sealed table registers under no posture (back-compat)",
283
+ threw === null);
284
+ b.cryptoField.clearForTest();
285
+ }
286
+
287
+ function testRegisterTableFloorThrowsUnderHipaa() {
288
+ _resetState();
289
+ b.cryptoField.clearForTest();
290
+ b.compliance.set("hipaa");
291
+ var threw = null;
292
+ try {
293
+ b.cryptoField.registerTable("ct_floor_hipaa_plain", { sealedFields: ["ssn"] });
294
+ } catch (e) { threw = e; }
295
+ check("plain sealed table under hipaa throws seal-envelope-below-floor",
296
+ threw && threw.code === "crypto-field/seal-envelope-below-floor");
297
+ b.cryptoField.clearForTest();
298
+ _resetState();
299
+ }
300
+
301
+ function testRegisterTableFloorAadSatisfiesHipaa() {
302
+ _resetState();
303
+ b.cryptoField.clearForTest();
304
+ b.compliance.set("hipaa");
305
+ var threw = null;
306
+ try {
307
+ b.cryptoField.registerTable("ct_floor_hipaa_aad",
308
+ { sealedFields: ["ssn"], aad: true, rowIdField: "id" });
309
+ } catch (e) { threw = e; }
310
+ check("aad-bound sealed table satisfies hipaa floor (no throw)",
311
+ threw === null);
312
+ b.cryptoField.clearForTest();
313
+ _resetState();
314
+ }
315
+
316
+ function testRegisterTableFloorNoSealedFieldsPasses() {
317
+ _resetState();
318
+ b.cryptoField.clearForTest();
319
+ b.compliance.set("pci-dss");
320
+ var threw = null;
321
+ try {
322
+ // No sealed columns → no envelope to gate.
323
+ b.cryptoField.registerTable("ct_floor_pci_nosealed", { sealedFields: [] });
324
+ } catch (e) { threw = e; }
325
+ check("table with no sealed fields passes under pci-dss floor",
326
+ threw === null);
327
+ b.cryptoField.clearForTest();
328
+ _resetState();
329
+ }
330
+
331
+ function testRegisterTableFloorPerRowKeySatisfies() {
332
+ _resetState();
333
+ b.cryptoField.clearForTest();
334
+ b.compliance.set("hipaa");
335
+ // declarePerRowKey before registerTable: the table's declared envelope
336
+ // is per-row-key, which is ABOVE the aad floor.
337
+ b.cryptoField.declarePerRowKey("ct_floor_hipaa_prk", { keySize: 32 });
338
+ var threw = null;
339
+ try {
340
+ b.cryptoField.registerTable("ct_floor_hipaa_prk", { sealedFields: ["ssn"] });
341
+ } catch (e) { threw = e; }
342
+ check("per-row-key table satisfies hipaa floor (no throw)",
343
+ threw === null);
344
+ b.cryptoField.clearForTest();
345
+ _resetState();
346
+ }
347
+
348
+ function testRegisterTableFloorUnflooredPosturePasses() {
349
+ // gdpr is regulated but declares no sealEnvelopeFloor → plain passes.
350
+ _resetState();
351
+ b.cryptoField.clearForTest();
352
+ b.compliance.set("gdpr");
353
+ var threw = null;
354
+ try {
355
+ b.cryptoField.registerTable("ct_floor_gdpr_plain", { sealedFields: ["email"] });
356
+ } catch (e) { threw = e; }
357
+ check("plain sealed table under gdpr (no floor) passes (back-compat)",
358
+ threw === null);
359
+ b.cryptoField.clearForTest();
360
+ _resetState();
361
+ }
362
+
363
+ // ---- Region-tag normalization + compatibility helpers (additive) ----
364
+
365
+ function testNormalizeRegionTag() {
366
+ check("normalizeRegionTag('EU') === normalizeRegionTag('eu')",
367
+ b.compliance.normalizeRegionTag("EU") === b.compliance.normalizeRegionTag("eu"));
368
+ check("normalizeRegionTag(' eu ') trims + lowercases",
369
+ b.compliance.normalizeRegionTag(" eu ") === "eu");
370
+ check("normalizeRegionTag('global') folds to 'unrestricted'",
371
+ b.compliance.normalizeRegionTag("global") === "unrestricted");
372
+ check("normalizeRegionTag('unrestricted') === 'unrestricted'",
373
+ b.compliance.normalizeRegionTag("unrestricted") === "unrestricted");
374
+ check("normalizeRegionTag(null) === null",
375
+ b.compliance.normalizeRegionTag(null) === null);
376
+ check("normalizeRegionTag('') === null",
377
+ b.compliance.normalizeRegionTag("") === null);
378
+ }
379
+
380
+ function testIsRegionCompatible() {
381
+ check("isRegionCompatible('EU','eu') === true (case-insensitive)",
382
+ b.compliance.isRegionCompatible("EU", "eu") === true);
383
+ check("isRegionCompatible('eu','global') === true (wildcard)",
384
+ b.compliance.isRegionCompatible("eu", "global") === true);
385
+ check("isRegionCompatible('unrestricted','us') === true (wildcard)",
386
+ b.compliance.isRegionCompatible("unrestricted", "us") === true);
387
+ check("isRegionCompatible('eu','us') === false (distinct regions)",
388
+ b.compliance.isRegionCompatible("eu", "us") === false);
389
+ check("isRegionCompatible('EU',null) === true (no constraint)",
390
+ b.compliance.isRegionCompatible("EU", null) === true);
391
+ }
392
+
393
+ // ---- Bug C1: gate-contract unmapped-posture warning ----
394
+
395
+ function _gcCfg() {
396
+ return {
397
+ profiles: { strict: { a: 1 } },
398
+ compliancePostures: { hipaa: { piiPolicy: "redact" }, "pci-dss": { piiPolicy: "refuse" } },
399
+ defaults: { piiPolicy: "serve" },
400
+ errCodePrefix: "ct_c1",
401
+ };
402
+ }
403
+
404
+ function testGateContractUnmappedPostureWarns() {
405
+ _resetState();
406
+ b.gateContract._resetForTest();
407
+ // fedramp-rev5-moderate is a real posture with no overlay in _gcCfg.
408
+ b.compliance.set("fedramp-rev5-moderate");
409
+ var captured = [];
410
+ var origAudit = b.audit.safeEmit;
411
+ b.audit.safeEmit = function (e) { captured.push(e); };
412
+ var resolved;
413
+ try {
414
+ resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
415
+ // Second call same posture+guard must NOT re-warn (dedupe).
416
+ b.gateContract.resolveProfileAndPosture({}, _gcCfg());
417
+ } finally {
418
+ b.audit.safeEmit = origAudit;
419
+ }
420
+ var warns = captured.filter(function (e) {
421
+ return e.action === "gateContract.posture.unmapped";
422
+ });
423
+ check("unmapped global posture emits exactly one warning (deduped)",
424
+ warns.length === 1 && warns[0].metadata.posture === "fedramp-rev5-moderate");
425
+ check("unmapped posture keeps the safe (unposture-d) default",
426
+ resolved.piiPolicy === "serve");
427
+ b.gateContract._resetForTest();
428
+ _resetState();
429
+ }
430
+
431
+ function testGateContractMappedPostureNoWarn() {
432
+ _resetState();
433
+ b.gateContract._resetForTest();
434
+ b.compliance.set("hipaa");
435
+ var captured = [];
436
+ var origAudit = b.audit.safeEmit;
437
+ b.audit.safeEmit = function (e) { captured.push(e); };
438
+ var resolved;
439
+ try {
440
+ resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
441
+ } finally {
442
+ b.audit.safeEmit = origAudit;
443
+ }
444
+ var warns = captured.filter(function (e) {
445
+ return e.action === "gateContract.posture.unmapped";
446
+ });
447
+ check("mapped global posture does not warn", warns.length === 0);
448
+ check("mapped global posture applies overlay", resolved.piiPolicy === "redact");
449
+ b.gateContract._resetForTest();
450
+ _resetState();
451
+ }
452
+
453
+ function testGateContractUnpinnedNoWarn() {
454
+ _resetState();
455
+ b.gateContract._resetForTest();
456
+ var captured = [];
457
+ var origAudit = b.audit.safeEmit;
458
+ b.audit.safeEmit = function (e) { captured.push(e); };
459
+ var resolved;
460
+ try {
461
+ resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
462
+ } finally {
463
+ b.audit.safeEmit = origAudit;
464
+ }
465
+ var warns = captured.filter(function (e) {
466
+ return e.action === "gateContract.posture.unmapped";
467
+ });
468
+ check("unpinned deployment does not warn", warns.length === 0);
469
+ check("unpinned deployment keeps default", resolved.piiPolicy === "serve");
470
+ b.gateContract._resetForTest();
471
+ }
472
+
258
473
  async function run() {
259
474
  testSurface();
260
475
  testSetThenCurrent();
@@ -266,7 +481,21 @@ async function run() {
266
481
  testV0870NewPostures();
267
482
  testV0881NewPostures();
268
483
  testV0882NewPostures();
484
+ testSealEnvelopeFloorData();
485
+ testRegisterTableFloorBackCompatUnpinned();
486
+ testRegisterTableFloorThrowsUnderHipaa();
487
+ testRegisterTableFloorAadSatisfiesHipaa();
488
+ testRegisterTableFloorNoSealedFieldsPasses();
489
+ testRegisterTableFloorPerRowKeySatisfies();
490
+ testRegisterTableFloorUnflooredPosturePasses();
491
+ testNormalizeRegionTag();
492
+ testIsRegionCompatible();
493
+ testGateContractUnmappedPostureWarns();
494
+ testGateContractMappedPostureNoWarn();
495
+ testGateContractUnpinnedNoWarn();
269
496
  // Reset at end so other tests don't see leaked posture.
497
+ b.cryptoField.clearForTest();
498
+ b.gateContract._resetForTest();
270
499
  _resetState();
271
500
  }
272
501
 
@@ -4,10 +4,12 @@
4
4
  *
5
5
  * Equality-lookup ("derived") hashes for sealed columns can be computed
6
6
  * two ways:
7
- * - salted-sha3 (default) — SHA3-512 over a per-deployment salt.
8
- * - hmac-shake256 (opt-in) — keyed MAC off vault.getDerivedHashMacKey,
9
- * so the hash is unforgeable without the
10
- * per-deployment MAC key.
7
+ * - hmac-shake256 (default, v0.15.0) — keyed MAC off
8
+ * vault.getDerivedHashMacKey, so an attacker
9
+ * who recovers the per-deployment salt alone
10
+ * cannot correlate low-entropy plaintexts.
11
+ * - salted-sha3 (opt-out) — SHA3-512 over a per-deployment salt;
12
+ * byte-compatible with the legacy index.
11
13
  *
12
14
  * The mode is chosen per-table (derivedHashMode) or per-column
13
15
  * (spec.mode). The decision lives in _computeDerivedHash; call sites
@@ -25,17 +27,31 @@ async function run() {
25
27
  var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-dh-"));
26
28
  await b.vault.init({ mode: "plaintext", dataDir: dir });
27
29
 
28
- // ---- salted-sha3 is the default: SHA3-512128 hex chars ----
30
+ // ---- DEFAULT (v0.15.0) is the keyed MAC: hmac-shake25664 hex chars.
31
+ // A table that declares no derivedHashMode gets the keyed digest so an
32
+ // attacker who recovers the salt alone can't correlate plaintexts. ----
33
+ b.cryptoField.registerTable("cf_dh_default", {
34
+ sealedFields: ["email"],
35
+ derivedHashes: { emailHash: { from: "email" } },
36
+ });
37
+ var defaultH = b.cryptoField.lookupHash("cf_dh_default", "email", "a@b.com").value;
38
+ check("derivedHashMode default is keyed hmac-shake256 (64 hex)", defaultH.length === 64);
39
+ check("default keyed hash is deterministic",
40
+ defaultH === b.cryptoField.lookupHash("cf_dh_default", "email", "a@b.com").value);
41
+
42
+ // ---- documented opt-out: derivedHashMode:'salted-sha3' restores the
43
+ // deterministic-per-deployment SHA3-512 digest → 128 hex chars. ----
29
44
  b.cryptoField.registerTable("cf_dh_t1", {
30
45
  sealedFields: ["email"],
31
46
  derivedHashes: { emailHash: { from: "email" } },
47
+ derivedHashMode: "salted-sha3",
32
48
  });
33
49
  var saltedH = b.cryptoField.lookupHash("cf_dh_t1", "email", "a@b.com").value;
34
- check("salted-sha3 default is 128 hex (SHA3-512)", saltedH.length === 128);
50
+ check("salted-sha3 opt-out is 128 hex (SHA3-512)", saltedH.length === 128);
35
51
  check("salted-sha3 is deterministic",
36
52
  saltedH === b.cryptoField.lookupHash("cf_dh_t1", "email", "a@b.com").value);
37
53
 
38
- // ---- hmac-shake256 table mode: SHAKE256/32 → 64 hex chars ----
54
+ // ---- hmac-shake256 table mode (explicit): SHAKE256/32 → 64 hex chars ----
39
55
  b.cryptoField.registerTable("cf_dh_t2", {
40
56
  sealedFields: ["email"],
41
57
  derivedHashes: { emailHash: { from: "email" } },
@@ -46,6 +62,7 @@ async function run() {
46
62
  check("hmac-shake256 is deterministic",
47
63
  keyedH === b.cryptoField.lookupHash("cf_dh_t2", "email", "a@b.com").value);
48
64
  check("keyed hash differs from salted hash", keyedH !== saltedH);
65
+ check("explicit keyed mode matches the new default", keyedH === defaultH);
49
66
 
50
67
  // ---- per-column mode override on a salted-default table ----
51
68
  b.cryptoField.registerTable("cf_dh_t3", {
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * b.cryptoField derived-hash dual-read + upgrade-on-read auto-migrate.
4
+ *
5
+ * The derived-hash default flipped from salted-sha3 (128 hex) to the keyed
6
+ * MAC hmac-shake256 (64 hex) in v0.15.0. The mode is resolved at
7
+ * registerTable from the current default and is NOT persisted, so on upgrade
8
+ * a row written under the OLD default still carries the salted-sha3 digest in
9
+ * its lookup column while new lookups compute the keyed-MAC digest — a silent
10
+ * index miss (find-by-email / destroyAllForUser would skip the legacy row).
11
+ *
12
+ * This proves the non-breaking landing:
13
+ * 1. lookupHashCandidates returns BOTH the keyed-MAC and the legacy
14
+ * salted-sha3 digest, so a match-EITHER query finds the legacy row.
15
+ * 2. unsealRow upgrade-on-read re-hashes a row whose derived-hash column
16
+ * holds the legacy salted digest to the keyed-MAC form (in the returned
17
+ * row AND, with a writable db handle, durably on disk), so the candidate
18
+ * set collapses back to a single value over time.
19
+ */
20
+
21
+ var helpers = require("../helpers");
22
+ var b = helpers.b;
23
+ var check = helpers.check;
24
+ var fs = require("fs");
25
+ var os = require("os");
26
+ var path = require("path");
27
+ var { setupTestDb, teardownTestDb } = require("../helpers/db");
28
+
29
+ async function run() {
30
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-dual-"));
31
+ try {
32
+ // The default users fixture: sealedFields ["email","name"],
33
+ // derivedHashes { emailHash: { from: "email", normalize: lowercase } },
34
+ // keyed-MAC default mode.
35
+ await setupTestDb(dir);
36
+
37
+ var email = "Alice@Example.com"; // mixed-case → normalize lowercases it
38
+
39
+ // ---- 1. dual-read: lookupHashCandidates names both digests ----
40
+ var keyed = b.cryptoField.lookupHash("users", "email", email);
41
+ check("active lookup hash is the keyed MAC (64 hex)",
42
+ keyed && keyed.value.length === 64);
43
+ check("lookupHash surfaces the legacy salted digest as legacyValue (128 hex)",
44
+ typeof keyed.legacyValue === "string" && keyed.legacyValue.length === 128);
45
+
46
+ // The legacy digest the lib computes is the byte-form a pre-v0.15.0 row
47
+ // carries; treat it as ground truth for the forged legacy row below.
48
+ var legacyHash = keyed.legacyValue;
49
+ check("legacy digest is the salted-sha3 SHA3-512 width (128 hex), distinct from the keyed MAC",
50
+ legacyHash.length === 128 && legacyHash !== keyed.value);
51
+
52
+ var cands = b.cryptoField.lookupHashCandidates("users", "email", email);
53
+ check("lookupHashCandidates returns the derived field name",
54
+ cands && cands.field === "emailHash");
55
+ check("candidates list carries BOTH the keyed and legacy digests (match-either)",
56
+ cands.values.length === 2 &&
57
+ cands.values.indexOf(keyed.value) !== -1 &&
58
+ cands.values.indexOf(legacyHash) !== -1);
59
+
60
+ // ---- 2. forge a LEGACY-indexed row on disk ----
61
+ // Seal the email under the live envelope but OVERWRITE the derived-hash
62
+ // column with the legacy salted digest, mimicking a row written before
63
+ // the default flipped. Insert via the raw handle so the framework's
64
+ // write boundary doesn't recompute emailHash to the keyed form.
65
+ var sealed = b.cryptoField.sealRow("users", { _id: "u-legacy", email: email, name: "Alice" });
66
+ sealed.emailHash = legacyHash; // pin the LEGACY digest, not the keyed one
67
+ b.db.prepare(
68
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
69
+ ).run(sealed._id, sealed.email, sealed.emailHash, sealed.name);
70
+
71
+ var onDiskBefore = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy");
72
+ check("forged row stores the legacy salted-sha3 emailHash on disk",
73
+ onDiskBefore.h === legacyHash && onDiskBefore.h.length === 128);
74
+
75
+ // A match-either query (the candidate list) FINDS the legacy row even
76
+ // though the keyed-only lookup would have missed it.
77
+ var foundLegacy = b.db.prepare(
78
+ 'SELECT _id FROM "users" WHERE "emailHash" = ?'
79
+ ).get(legacyHash);
80
+ check("legacy-indexed row is found via the legacy candidate hash",
81
+ foundLegacy && foundLegacy._id === "u-legacy");
82
+ var missedByKeyed = b.db.prepare(
83
+ 'SELECT _id FROM "users" WHERE "emailHash" = ?'
84
+ ).get(keyed.value);
85
+ check("keyed-only lookup MISSES the legacy row (why dual-read is needed)",
86
+ missedByKeyed === undefined || missedByKeyed === null);
87
+
88
+ // ---- 3. upgrade-on-read: unsealRow re-hashes to the keyed MAC ----
89
+ var rawRow = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy");
90
+ // Pass the writable local db handle so the durable rewrite fires.
91
+ var unsealed = b.cryptoField.unsealRow("users", rawRow, "actor-1", b.db);
92
+ check("unsealRow decrypted the sealed email back to plaintext",
93
+ unsealed.email === email);
94
+ check("upgrade-on-read: returned row's emailHash is now the keyed MAC",
95
+ unsealed.emailHash === keyed.value && unsealed.emailHash.length === 64);
96
+
97
+ var onDiskAfter = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy");
98
+ check("upgrade-on-read: the row's emailHash was durably re-written to the keyed MAC",
99
+ onDiskAfter.h === keyed.value && onDiskAfter.h.length === 64);
100
+
101
+ // After the upgrade, the keyed-only lookup now finds the row directly.
102
+ var foundKeyed = b.db.prepare(
103
+ 'SELECT _id FROM "users" WHERE "emailHash" = ?'
104
+ ).get(keyed.value);
105
+ check("post-migrate: keyed-only lookup now finds the upgraded row",
106
+ foundKeyed && foundKeyed._id === "u-legacy");
107
+
108
+ // ---- 4. idempotence: a row already keyed is left untouched ----
109
+ var rawRow2 = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy");
110
+ var unsealed2 = b.cryptoField.unsealRow("users", rawRow2, "actor-1", b.db);
111
+ check("re-reading an already-keyed row leaves its emailHash unchanged",
112
+ unsealed2.emailHash === keyed.value);
113
+
114
+ // ---- 5. no-handle read resolves the framework db for the rewrite ----
115
+ // Re-forge a legacy row, unseal WITHOUT an explicit dbHandle: unsealRow
116
+ // resolves the framework's local db itself (the same fallback the K_row
117
+ // read path uses), so the returned row carries the keyed hash AND the
118
+ // durable rewrite still lands — keyed reads must work on every path.
119
+ var sealed3 = b.cryptoField.sealRow("users", { _id: "u-legacy-2", email: email, name: "Bob" });
120
+ sealed3.emailHash = legacyHash;
121
+ b.db.prepare(
122
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
123
+ ).run(sealed3._id, sealed3.email, sealed3.emailHash, sealed3.name);
124
+ var raw3 = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy-2");
125
+ var unsealedNoHandle = b.cryptoField.unsealRow("users", raw3); // no explicit dbHandle
126
+ check("no-handle unseal surfaces the keyed hash on the returned row",
127
+ unsealedNoHandle.emailHash === keyed.value);
128
+ var disk3 = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy-2");
129
+ check("no-handle unseal still durably upgrades via the resolved framework db",
130
+ disk3.h === keyed.value);
131
+
132
+ // ---- 6. THE REAL CONSUMER PATH: b.db.from().where on a sealed field ----
133
+ // Operators — and the framework's own api-key / session / audit / mail
134
+ // stores — find a row by a sealed field through the equality rewrite,
135
+ // which MUST dual-read across the keyed-MAC flip or it silently drops
136
+ // un-migrated rows (the keyed-only lookup misses the legacy digest).
137
+ // Forge a FRESH legacy row (not yet upgraded) and find it via the real
138
+ // query path — this is the path the primitive-only test above never
139
+ // exercised.
140
+ var sealed6 = b.cryptoField.sealRow("users", { _id: "u-legacy-q", email: email, name: "Dave" });
141
+ sealed6.emailHash = legacyHash;
142
+ b.db.prepare(
143
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
144
+ ).run(sealed6._id, sealed6.email, sealed6.emailHash, sealed6.name);
145
+ var viaQuery = await b.db.from("users").where("email", email).all();
146
+ check("real consumer path (b.db.from().where on a sealed field) finds the un-migrated legacy row",
147
+ Array.isArray(viaQuery) && viaQuery.some(function (r) { return r._id === "u-legacy-q"; }));
148
+
149
+ // b.db.hashCandidatesFor — the db-level dual-read helper the framework's
150
+ // bespoke stores (consent/subject) compose for whereIn lookups.
151
+ var dbCands = b.db.hashCandidatesFor("users", "email", email);
152
+ check("b.db.hashCandidatesFor returns both the keyed and legacy digests",
153
+ dbCands && dbCands.field === "emailHash" && dbCands.values.length === 2);
154
+
155
+ console.log("OK — crypto-field dual-read + auto-migrate tests");
156
+ } finally {
157
+ await teardownTestDb(dir);
158
+ }
159
+ }
160
+
161
+ module.exports = { run: run };
162
+ if (require.main === module) {
163
+ run().then(function () { process.exit(0); })
164
+ .catch(function (err) { process.exitCode = 1; throw err; });
165
+ }