@blamejs/blamejs-shop 0.4.31 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/vendor/MANIFEST.json +392 -278
  4. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  5. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  6. package/lib/vendor/blamejs/.gitignore +6 -0
  7. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  8. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  9. package/lib/vendor/blamejs/README.md +8 -6
  10. package/lib/vendor/blamejs/SECURITY.md +19 -3
  11. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  12. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  13. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  14. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  15. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  16. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  17. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  18. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  19. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  20. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  23. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  24. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  25. package/lib/vendor/blamejs/index.js +4 -0
  26. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  27. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  28. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  29. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  30. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  31. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  32. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  33. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  34. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  35. package/lib/vendor/blamejs/lib/audit.js +259 -123
  36. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  37. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  38. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  39. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  40. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  41. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  42. package/lib/vendor/blamejs/lib/cache.js +174 -105
  43. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  44. package/lib/vendor/blamejs/lib/cli.js +19 -14
  45. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  46. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  47. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  48. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  49. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  50. package/lib/vendor/blamejs/lib/consent.js +82 -29
  51. package/lib/vendor/blamejs/lib/constants.js +27 -11
  52. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  53. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  54. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  55. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  56. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  57. package/lib/vendor/blamejs/lib/db.js +249 -99
  58. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  59. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  60. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  61. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  62. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  63. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  64. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  65. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  66. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  67. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  68. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  69. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  70. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  71. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  72. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  73. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  74. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  75. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  76. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  77. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  78. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  79. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  80. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  81. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  82. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  83. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  84. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  85. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  86. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  87. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  88. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  89. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  90. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  91. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  92. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  93. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  95. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  96. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  97. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  98. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  99. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  100. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  101. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  102. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  103. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  104. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  107. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  109. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  110. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  111. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  112. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  113. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  114. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  115. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  116. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  117. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  118. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  119. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  120. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  121. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  122. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  123. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  125. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  132. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  133. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  135. package/lib/vendor/blamejs/lib/mail.js +8 -4
  136. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  137. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  138. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  139. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  141. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  142. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  143. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  144. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  145. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  146. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  147. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  148. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  149. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  150. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  151. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  152. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  153. package/lib/vendor/blamejs/lib/observability.js +124 -0
  154. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  155. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  156. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  157. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  158. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  159. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  160. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  161. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  162. package/lib/vendor/blamejs/lib/queue.js +7 -0
  163. package/lib/vendor/blamejs/lib/redact.js +68 -11
  164. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  165. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  166. package/lib/vendor/blamejs/lib/retention.js +101 -40
  167. package/lib/vendor/blamejs/lib/router.js +212 -5
  168. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  169. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  170. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  171. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  172. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  173. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  174. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  175. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  176. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  177. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  178. package/lib/vendor/blamejs/lib/session.js +175 -77
  179. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  180. package/lib/vendor/blamejs/lib/sse.js +26 -0
  181. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  182. package/lib/vendor/blamejs/lib/static.js +177 -34
  183. package/lib/vendor/blamejs/lib/subject.js +96 -49
  184. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  185. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  186. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  187. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  188. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  189. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  190. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  191. package/lib/vendor/blamejs/package.json +2 -2
  192. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  193. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  194. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  200. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  201. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  202. package/lib/vendor/blamejs/scripts/release.js +398 -38
  203. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  204. package/lib/vendor/blamejs/test/10-state.js +140 -14
  205. package/lib/vendor/blamejs/test/20-db.js +65 -2
  206. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  207. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  208. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  209. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  210. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  211. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  212. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  213. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  214. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  215. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  220. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  221. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  222. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  223. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  224. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  225. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  226. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  227. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  228. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  229. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  230. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  231. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  232. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  233. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  234. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  235. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  236. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  237. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  238. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  248. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  258. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  270. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  271. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  272. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  273. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  277. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  278. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  291. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  302. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  308. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  310. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  311. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  312. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  313. package/lib/vendor/blamejs/test/smoke.js +79 -21
  314. package/package.json +1 -1
  315. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  316. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  317. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  318. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  319. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  320. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  321. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  322. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  323. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  324. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  325. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  326. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  327. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  328. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  329. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  330. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  331. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  332. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  334. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  335. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  336. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -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
+ }