@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
@@ -0,0 +1,638 @@
1
+ "use strict";
2
+ /**
3
+ * external-db-residency — per-row residency write gate
4
+ * (b.externalDb.query / transaction / read.query).
5
+ *
6
+ * Drives the cross-border DML write gate end-to-end against a fake
7
+ * backend so the wire (the backend's query hook) is observed for every
8
+ * enforcement-matrix cell:
9
+ *
10
+ * - unregulated posture + residency-tagged backend → DML passes
11
+ * - regulated posture (gdpr) + untagged backend → DML passes
12
+ * - gdpr + tagged backend + no rowResidencyTag → RESIDENCY_GATE_REQUIRED (wire NOT reached)
13
+ * - gdpr + tagged backend + matching tag → passes (statement reaches the wire)
14
+ * - gdpr + tagged backend + "global" → passes
15
+ * - gdpr + tagged backend + mismatched tag → RESIDENCY_TAG_MISMATCH
16
+ * - empty-string rowResidencyTag → INVALID_OPT
17
+ * - SELECT under gdpr to a tagged backend → passes (non-DML not gated)
18
+ * - transaction: tx-level + per-call override tags → mismatch rolls back (no COMMIT)
19
+ * - read-replica residency incompatibility → REPLICA_RESIDENCY_INCOMPATIBLE / allowCrossBorder bypass
20
+ *
21
+ * The gate reads the active posture from b.compliance.current(), so each
22
+ * regulated-posture block flips the posture via b.compliance.set("gdpr")
23
+ * and ALWAYS restores it in a finally so parallel smoke files aren't
24
+ * poisoned.
25
+ */
26
+
27
+ var helpers = require("../helpers");
28
+ var b = helpers.b;
29
+ var check = helpers.check;
30
+
31
+ // Tracking driver: records every SQL statement the backend's query hook
32
+ // sees so tests assert whether the wire was reached. Returns rowCount 1
33
+ // for DML, an empty result for SELECT, and a no-op for BEGIN / COMMIT /
34
+ // ROLLBACK so the transaction machinery completes.
35
+ function _trackingDriver(label) {
36
+ var seen = [];
37
+ return {
38
+ label: label,
39
+ seen: seen,
40
+ connect: async function () { return { id: label + "-client" }; },
41
+ query: async function (_client, sql, _params) {
42
+ seen.push(sql);
43
+ if (/^(BEGIN|COMMIT|ROLLBACK)\b/i.test(sql)) return { rows: [], rowCount: 0 };
44
+ if (/^SELECT\b/i.test(sql)) return { rows: [{ from: label }], rowCount: 1 };
45
+ return { rows: [], rowCount: 1 };
46
+ },
47
+ close: async function () { /* no-op */ },
48
+ ping: async function () { return true; },
49
+ };
50
+ }
51
+
52
+ function _saw(driver, re) {
53
+ return driver.seen.some(function (s) { return re.test(s); });
54
+ }
55
+
56
+ // Run fn() under the gdpr posture, restoring whatever posture (or none)
57
+ // was pinned before. clear()+set() because compliance.set refuses a
58
+ // runtime switch when a different posture is already pinned.
59
+ async function _underGdpr(fn) {
60
+ var prior = b.compliance.current();
61
+ b.compliance.clear();
62
+ b.compliance.set("gdpr");
63
+ try {
64
+ await fn();
65
+ } finally {
66
+ b.compliance.clear();
67
+ if (prior) b.compliance.set(prior);
68
+ }
69
+ }
70
+
71
+ async function _expectThrow(label, fn, expectedCode) {
72
+ var threw = null;
73
+ try { await fn(); } catch (e) { threw = e; }
74
+ check(label + ": threw " + expectedCode,
75
+ threw !== null && threw.code === expectedCode);
76
+ }
77
+
78
+ async function run() {
79
+ // Confirm the cross-border vocabulary is shared between the gate and
80
+ // the compliance catalog (the gate consults isCrossBorderRegulated).
81
+ check("compliance.isCrossBorderRegulated('gdpr') === true",
82
+ b.compliance.isCrossBorderRegulated("gdpr") === true);
83
+ check("compliance.isCrossBorderRegulated('soc2') === false",
84
+ b.compliance.isCrossBorderRegulated("soc2") === false);
85
+ check("CROSS_BORDER_REGULATED_POSTURES includes gdpr",
86
+ Array.isArray(b.compliance.CROSS_BORDER_REGULATED_POSTURES) &&
87
+ b.compliance.CROSS_BORDER_REGULATED_POSTURES.indexOf("gdpr") !== -1);
88
+
89
+ // ---- unregulated posture + residency-tagged backend → DML passes ----
90
+ // No posture pinned (default) → the gate does not engage even for a
91
+ // residency-tagged backend, even without a rowResidencyTag.
92
+ b.externalDb._resetForTest();
93
+ var euDriver = _trackingDriver("eu");
94
+ b.externalDb.init({
95
+ backends: {
96
+ main: {
97
+ connect: euDriver.connect, query: euDriver.query,
98
+ close: euDriver.close, residencyTag: "eu",
99
+ },
100
+ },
101
+ });
102
+ await b.externalDb.query("INSERT INTO orders (id) VALUES ($1)", ["o-1"]);
103
+ check("unregulated + tagged backend + untagged DML → passes, wire reached",
104
+ _saw(euDriver, /INSERT INTO orders/));
105
+
106
+ // ---- gdpr + UNTAGGED (unrestricted) backend + untagged DML → passes ----
107
+ b.externalDb._resetForTest();
108
+ var freeDriver = _trackingDriver("free");
109
+ b.externalDb.init({
110
+ backends: {
111
+ main: {
112
+ connect: freeDriver.connect, query: freeDriver.query,
113
+ close: freeDriver.close, // no residencyTag → "unrestricted"
114
+ },
115
+ },
116
+ });
117
+ await _underGdpr(async function () {
118
+ await b.externalDb.query("UPDATE orders SET total = $1 WHERE id = $2", [10, "o-1"]);
119
+ });
120
+ check("gdpr + unrestricted backend + untagged DML → passes, wire reached",
121
+ _saw(freeDriver, /UPDATE orders/));
122
+
123
+ // ---- gdpr + eu backend + DML without tag → RESIDENCY_GATE_REQUIRED ----
124
+ b.externalDb._resetForTest();
125
+ var gateDriver = _trackingDriver("gate");
126
+ b.externalDb.init({
127
+ backends: {
128
+ main: {
129
+ connect: gateDriver.connect, query: gateDriver.query,
130
+ close: gateDriver.close, residencyTag: "eu",
131
+ },
132
+ },
133
+ });
134
+ await _underGdpr(async function () {
135
+ await _expectThrow("gdpr + eu backend + untagged DML",
136
+ function () {
137
+ return b.externalDb.query("DELETE FROM orders WHERE id = $1", ["o-1"]);
138
+ },
139
+ "RESIDENCY_GATE_REQUIRED");
140
+ });
141
+ check("RESIDENCY_GATE_REQUIRED → wire NOT reached",
142
+ gateDriver.seen.every(function (s) { return !/DELETE FROM orders/.test(s); }));
143
+
144
+ // ---- gdpr + eu backend + matching tag "eu" → passes (success path) ----
145
+ b.externalDb._resetForTest();
146
+ var okDriver = _trackingDriver("ok");
147
+ b.externalDb.init({
148
+ backends: {
149
+ main: {
150
+ connect: okDriver.connect, query: okDriver.query,
151
+ close: okDriver.close, residencyTag: "eu",
152
+ },
153
+ },
154
+ });
155
+ await _underGdpr(async function () {
156
+ var res = await b.externalDb.query(
157
+ "INSERT INTO orders (id, region) VALUES ($1, $2)",
158
+ ["o-2", "eu"],
159
+ { rowResidencyTag: "eu" });
160
+ check("gdpr + eu backend + tag 'eu' → rowCount returned",
161
+ res && res.rowCount === 1);
162
+ });
163
+ check("gdpr + eu backend + tag 'eu' → wire reached with the statement",
164
+ _saw(okDriver, /INSERT INTO orders \(id, region\)/));
165
+
166
+ // ---- gdpr + eu backend + tag "global" → passes ----
167
+ b.externalDb._resetForTest();
168
+ var globalDriver = _trackingDriver("global");
169
+ b.externalDb.init({
170
+ backends: {
171
+ main: {
172
+ connect: globalDriver.connect, query: globalDriver.query,
173
+ close: globalDriver.close, residencyTag: "eu",
174
+ },
175
+ },
176
+ });
177
+ await _underGdpr(async function () {
178
+ await b.externalDb.query(
179
+ "UPDATE orders SET total = $1 WHERE id = $2",
180
+ [99, "o-2"],
181
+ { rowResidencyTag: "global" });
182
+ });
183
+ check("gdpr + eu backend + tag 'global' → passes, wire reached",
184
+ _saw(globalDriver, /UPDATE orders/));
185
+
186
+ // ---- gdpr + eu backend + tag "us" → RESIDENCY_TAG_MISMATCH ----
187
+ b.externalDb._resetForTest();
188
+ var mismatchDriver = _trackingDriver("mismatch");
189
+ b.externalDb.init({
190
+ backends: {
191
+ main: {
192
+ connect: mismatchDriver.connect, query: mismatchDriver.query,
193
+ close: mismatchDriver.close, residencyTag: "eu",
194
+ },
195
+ },
196
+ });
197
+ await _underGdpr(async function () {
198
+ await _expectThrow("gdpr + eu backend + tag 'us'",
199
+ function () {
200
+ return b.externalDb.query(
201
+ "INSERT INTO orders (id) VALUES ($1)",
202
+ ["o-3"],
203
+ { rowResidencyTag: "us" });
204
+ },
205
+ "RESIDENCY_TAG_MISMATCH");
206
+ });
207
+ check("RESIDENCY_TAG_MISMATCH → wire NOT reached",
208
+ mismatchDriver.seen.every(function (s) { return !/INSERT INTO orders/.test(s); }));
209
+
210
+ // ---- rowResidencyTag present but empty string → INVALID_OPT ----
211
+ // The empty-string guard runs ahead of posture / DML classification —
212
+ // exercise it on a DML statement under gdpr so the path is realistic.
213
+ b.externalDb._resetForTest();
214
+ var emptyDriver = _trackingDriver("empty");
215
+ b.externalDb.init({
216
+ backends: {
217
+ main: {
218
+ connect: emptyDriver.connect, query: emptyDriver.query,
219
+ close: emptyDriver.close, residencyTag: "eu",
220
+ },
221
+ },
222
+ });
223
+ await _underGdpr(async function () {
224
+ await _expectThrow("empty-string rowResidencyTag",
225
+ function () {
226
+ return b.externalDb.query(
227
+ "INSERT INTO orders (id) VALUES ($1)",
228
+ ["o-4"],
229
+ { rowResidencyTag: "" });
230
+ },
231
+ "INVALID_OPT");
232
+ });
233
+ check("INVALID_OPT → wire NOT reached",
234
+ emptyDriver.seen.every(function (s) { return !/INSERT INTO orders/.test(s); }));
235
+
236
+ // ---- multi-statement SQL hiding a trailing DML behind a SELECT
237
+ // prefix → MULTI_STATEMENT_REFUSED (the classifier reads only the
238
+ // leading keyword; a trailing INSERT must not slip the gate) ----
239
+ b.externalDb._resetForTest();
240
+ var multiDriver = _trackingDriver("multi");
241
+ b.externalDb.init({
242
+ backends: {
243
+ main: {
244
+ connect: multiDriver.connect, query: multiDriver.query,
245
+ close: multiDriver.close, residencyTag: "eu",
246
+ },
247
+ },
248
+ });
249
+ await _underGdpr(async function () {
250
+ await _expectThrow("multi-statement SELECT;INSERT refused",
251
+ function () {
252
+ return b.externalDb.query(
253
+ "SELECT 1; INSERT INTO orders (id) VALUES ('o-x')", []);
254
+ },
255
+ "MULTI_STATEMENT_REFUSED");
256
+ });
257
+ check("MULTI_STATEMENT_REFUSED → wire NOT reached",
258
+ multiDriver.seen.every(function (s) { return !/INSERT INTO orders/.test(s); }));
259
+ // A single statement ending in a bare ; (no trailing statement) is fine.
260
+ await _underGdpr(async function () {
261
+ var ok = await b.externalDb.query(
262
+ "INSERT INTO orders (id) VALUES ('o-semi');", [], { rowResidencyTag: "eu" });
263
+ check("single statement with trailing ; passes", ok && ok.rowCount === 1);
264
+ });
265
+
266
+ // ---- transaction-level rowResidencyTag shape validated at entry ----
267
+ b.externalDb._resetForTest();
268
+ var txShapeDriver = _trackingDriver("txshape");
269
+ b.externalDb.init({
270
+ backends: {
271
+ main: {
272
+ connect: txShapeDriver.connect, query: txShapeDriver.query,
273
+ close: txShapeDriver.close, residencyTag: "eu",
274
+ },
275
+ },
276
+ });
277
+ await _underGdpr(async function () {
278
+ await _expectThrow("empty tx-level rowResidencyTag refused at entry",
279
+ function () {
280
+ return b.externalDb.transaction(async function (tx) {
281
+ await tx.query("INSERT INTO orders (id) VALUES ('o-tx')", []);
282
+ }, { rowResidencyTag: "" });
283
+ },
284
+ "INVALID_OPT");
285
+ });
286
+ check("tx-level shape refusal → no BEGIN reached the backend",
287
+ txShapeDriver.seen.every(function (s) { return !/BEGIN/i.test(s); }));
288
+
289
+ // ---- SELECT under gdpr to an eu backend without tag → passes (non-DML) ----
290
+ b.externalDb._resetForTest();
291
+ var selectDriver = _trackingDriver("select");
292
+ b.externalDb.init({
293
+ backends: {
294
+ main: {
295
+ connect: selectDriver.connect, query: selectDriver.query,
296
+ close: selectDriver.close, residencyTag: "eu",
297
+ },
298
+ },
299
+ });
300
+ await _underGdpr(async function () {
301
+ var res = await b.externalDb.query("SELECT id FROM orders WHERE id = $1", ["o-1"]);
302
+ check("gdpr + eu backend + untagged SELECT → passes, rows returned",
303
+ res && res.rowCount === 1);
304
+ });
305
+ check("gdpr + eu backend + untagged SELECT → wire reached (non-DML not gated)",
306
+ _saw(selectDriver, /SELECT id FROM orders/));
307
+
308
+ // ---- transaction: tx-level tag applies to tx.query; mismatch rolls back ----
309
+ // A transaction-level rowResidencyTag of "us" against an "eu" backend
310
+ // makes the first DML tx.query throw RESIDENCY_TAG_MISMATCH, which
311
+ // rolls the transaction back: the fake backend sees BEGIN then
312
+ // ROLLBACK, and NEVER COMMIT or the gated INSERT.
313
+ b.externalDb._resetForTest();
314
+ var txMismatchDriver = _trackingDriver("txmismatch");
315
+ b.externalDb.init({
316
+ backends: {
317
+ main: {
318
+ connect: txMismatchDriver.connect, query: txMismatchDriver.query,
319
+ close: txMismatchDriver.close, residencyTag: "eu",
320
+ },
321
+ },
322
+ });
323
+ await _underGdpr(async function () {
324
+ await _expectThrow("transaction: tx-level tag 'us' on eu backend",
325
+ function () {
326
+ return b.externalDb.transaction(async function (tx) {
327
+ await tx.query("INSERT INTO orders (id) VALUES ($1)", ["tx-1"]);
328
+ }, { rowResidencyTag: "us" });
329
+ },
330
+ "RESIDENCY_TAG_MISMATCH");
331
+ });
332
+ check("transaction mismatch: BEGIN issued",
333
+ _saw(txMismatchDriver, /^BEGIN\b/));
334
+ check("transaction mismatch: ROLLBACK issued (transaction rolled back)",
335
+ _saw(txMismatchDriver, /^ROLLBACK\b/));
336
+ check("transaction mismatch: COMMIT NOT reached",
337
+ txMismatchDriver.seen.every(function (s) { return !/^COMMIT\b/i.test(s); }));
338
+ check("transaction mismatch: gated INSERT NOT reached",
339
+ txMismatchDriver.seen.every(function (s) { return !/INSERT INTO orders/.test(s); }));
340
+
341
+ // tx-level matching tag "eu" → the statement commits end-to-end.
342
+ b.externalDb._resetForTest();
343
+ var txOkDriver = _trackingDriver("txok");
344
+ b.externalDb.init({
345
+ backends: {
346
+ main: {
347
+ connect: txOkDriver.connect, query: txOkDriver.query,
348
+ close: txOkDriver.close, residencyTag: "eu",
349
+ },
350
+ },
351
+ });
352
+ await _underGdpr(async function () {
353
+ await b.externalDb.transaction(async function (tx) {
354
+ await tx.query("INSERT INTO orders (id) VALUES ($1)", ["tx-2"]);
355
+ }, { rowResidencyTag: "eu" });
356
+ });
357
+ check("transaction tx-level tag 'eu': INSERT reached the wire",
358
+ _saw(txOkDriver, /INSERT INTO orders/));
359
+ check("transaction tx-level tag 'eu': COMMIT reached",
360
+ _saw(txOkDriver, /^COMMIT\b/));
361
+
362
+ // per-call third-arg override wins over the transaction-level tag.
363
+ // Transaction-level tag "us" (would refuse), but the per-call override
364
+ // "eu" replaces it for that statement → the DML reaches the wire and
365
+ // the transaction commits.
366
+ b.externalDb._resetForTest();
367
+ var txOverrideDriver = _trackingDriver("txoverride");
368
+ b.externalDb.init({
369
+ backends: {
370
+ main: {
371
+ connect: txOverrideDriver.connect, query: txOverrideDriver.query,
372
+ close: txOverrideDriver.close, residencyTag: "eu",
373
+ },
374
+ },
375
+ });
376
+ await _underGdpr(async function () {
377
+ await b.externalDb.transaction(async function (tx) {
378
+ await tx.query(
379
+ "INSERT INTO orders (id) VALUES ($1)",
380
+ ["tx-3"],
381
+ { rowResidencyTag: "eu" });
382
+ }, { rowResidencyTag: "us" });
383
+ });
384
+ check("transaction per-call override 'eu' beats tx-level 'us': INSERT reached",
385
+ _saw(txOverrideDriver, /INSERT INTO orders/));
386
+ check("transaction per-call override 'eu' beats tx-level 'us': COMMIT reached",
387
+ _saw(txOverrideDriver, /^COMMIT\b/));
388
+
389
+ // and the converse: per-call override "us" beats a benign tx-level
390
+ // "eu" → that statement is refused and the transaction rolls back.
391
+ b.externalDb._resetForTest();
392
+ var txOverrideBadDriver = _trackingDriver("txoverridebad");
393
+ b.externalDb.init({
394
+ backends: {
395
+ main: {
396
+ connect: txOverrideBadDriver.connect, query: txOverrideBadDriver.query,
397
+ close: txOverrideBadDriver.close, residencyTag: "eu",
398
+ },
399
+ },
400
+ });
401
+ await _underGdpr(async function () {
402
+ await _expectThrow("transaction per-call override 'us' beats tx-level 'eu'",
403
+ function () {
404
+ return b.externalDb.transaction(async function (tx) {
405
+ await tx.query(
406
+ "INSERT INTO orders (id) VALUES ($1)",
407
+ ["tx-4"],
408
+ { rowResidencyTag: "us" });
409
+ }, { rowResidencyTag: "eu" });
410
+ },
411
+ "RESIDENCY_TAG_MISMATCH");
412
+ });
413
+ check("transaction per-call override 'us': COMMIT NOT reached",
414
+ txOverrideBadDriver.seen.every(function (s) { return !/^COMMIT\b/i.test(s); }));
415
+
416
+ // ---- read-replica residency incompatibility ----
417
+ // The replica-vs-row read gate is distinct from the replica-vs-primary
418
+ // CONFIG gate. To reach the read gate with allowCrossBorder:false (init
419
+ // would refuse a cross-border replica otherwise), the primary is left
420
+ // "unrestricted" — so the "us" replica is config-compatible with the
421
+ // primary and init accepts it — and the incompatibility is between the
422
+ // ROW's tag ("eu") and the replica's tag ("us") at read time.
423
+ b.externalDb._resetForTest();
424
+ var roPrimary = _trackingDriver("ro-primary");
425
+ var usReplica = _trackingDriver("us-replica");
426
+ b.externalDb.init({
427
+ backends: {
428
+ main: {
429
+ connect: roPrimary.connect, query: roPrimary.query,
430
+ close: roPrimary.close, // primary unrestricted
431
+ replicas: [
432
+ {
433
+ connect: usReplica.connect, query: usReplica.query,
434
+ close: usReplica.close, residencyTag: "us",
435
+ allowCrossBorder: false,
436
+ },
437
+ ],
438
+ replicaFallbackToPrimary: false,
439
+ },
440
+ },
441
+ });
442
+ await _underGdpr(async function () {
443
+ await _expectThrow("read to us replica for eu row, allowCrossBorder false",
444
+ function () {
445
+ return b.externalDb.read.query(
446
+ "SELECT id FROM orders WHERE id = $1",
447
+ ["o-1"],
448
+ { rowResidencyTag: "eu" });
449
+ },
450
+ "REPLICA_RESIDENCY_INCOMPATIBLE");
451
+ });
452
+ check("REPLICA_RESIDENCY_INCOMPATIBLE → replica wire NOT reached",
453
+ usReplica.seen.every(function (s) { return !/SELECT id FROM orders/.test(s); }));
454
+
455
+ // allowCrossBorder true on the replica → the cross-border read is
456
+ // permitted (audited) and reaches the replica wire.
457
+ b.externalDb._resetForTest();
458
+ var roPrimary2 = _trackingDriver("ro-primary2");
459
+ var usReplicaOk = _trackingDriver("us-replica-ok");
460
+ b.externalDb.init({
461
+ backends: {
462
+ main: {
463
+ connect: roPrimary2.connect, query: roPrimary2.query,
464
+ close: roPrimary2.close, // primary unrestricted
465
+ replicas: [
466
+ {
467
+ connect: usReplicaOk.connect, query: usReplicaOk.query,
468
+ close: usReplicaOk.close, residencyTag: "us",
469
+ allowCrossBorder: true,
470
+ },
471
+ ],
472
+ replicaFallbackToPrimary: false,
473
+ },
474
+ },
475
+ });
476
+ await _underGdpr(async function () {
477
+ var res = await b.externalDb.read.query(
478
+ "SELECT id FROM orders WHERE id = $1",
479
+ ["o-1"],
480
+ { rowResidencyTag: "eu" });
481
+ check("read to us replica for eu row, allowCrossBorder true → rows returned",
482
+ res && res.rowCount === 1);
483
+ });
484
+ check("allowCrossBorder replica read → replica wire reached",
485
+ _saw(usReplicaOk, /SELECT id FROM orders/));
486
+
487
+ // ---- read-replica residency gate: OMITTED rowResidencyTag must fail closed ----
488
+ // Symmetric with the WRITE gate's RESIDENCY_GATE_REQUIRED. When the read gate
489
+ // is skipped on an absent tag, a SELECT of EU-resident rows silently lands on
490
+ // a tagged US replica — the fan-out drops the per-row residency tag. Under a
491
+ // cross-border-regulated posture a tagged, non-cross-border replica must
492
+ // refuse the untagged read rather than route it.
493
+ b.externalDb._resetForTest();
494
+ var roPrimaryOmit = _trackingDriver("ro-primary-omit");
495
+ var usReplicaOmit = _trackingDriver("us-replica-omit");
496
+ b.externalDb.init({
497
+ backends: {
498
+ main: {
499
+ connect: roPrimaryOmit.connect, query: roPrimaryOmit.query,
500
+ close: roPrimaryOmit.close, // primary unrestricted
501
+ replicas: [
502
+ {
503
+ connect: usReplicaOmit.connect, query: usReplicaOmit.query,
504
+ close: usReplicaOmit.close, residencyTag: "us",
505
+ allowCrossBorder: false,
506
+ },
507
+ ],
508
+ replicaFallbackToPrimary: false,
509
+ },
510
+ },
511
+ });
512
+ await _underGdpr(async function () {
513
+ // No rowResidencyTag opt → the read gate must NOT silently pass.
514
+ await _expectThrow("read with OMITTED rowResidencyTag under gdpr to a tagged replica fails closed",
515
+ function () {
516
+ return b.externalDb.read.query("SELECT id FROM orders WHERE id = $1", ["o-1"]);
517
+ },
518
+ "REPLICA_RESIDENCY_TAG_REQUIRED");
519
+ });
520
+ check("omitted-tag read → us-replica wire NOT reached (gate fail-closed)",
521
+ usReplicaOmit.seen.every(function (s) { return !/SELECT id FROM orders/.test(s); }));
522
+
523
+ // ---- write verbs that wear a harmless leading keyword must still be
524
+ // gated: WITH (CTE) / COPY FROM / EXPLAIN ANALYZE / CALL / EXECUTE /
525
+ // REPLACE / DO all PLACE rows, so under gdpr + eu backend they require
526
+ // a tag; recognized pure reads (WITH...SELECT, COPY...TO, plain
527
+ // EXPLAIN) pass untagged. One driver, exercised across the matrix. ----
528
+ function _classDriver() { return _trackingDriver("class"); }
529
+
530
+ async function _refusedUntagged(label, sql, code) {
531
+ b.externalDb._resetForTest();
532
+ var d = _classDriver();
533
+ b.externalDb.init({ backends: { main: {
534
+ connect: d.connect, query: d.query, close: d.close, residencyTag: "eu",
535
+ } } });
536
+ await _underGdpr(async function () {
537
+ await _expectThrow(label, function () { return b.externalDb.query(sql, []); }, code);
538
+ });
539
+ check(label + " → wire NOT reached", d.seen.length === 0);
540
+ }
541
+
542
+ async function _passesUntagged(label, sql) {
543
+ b.externalDb._resetForTest();
544
+ var d = _classDriver();
545
+ b.externalDb.init({ backends: { main: {
546
+ connect: d.connect, query: d.query, close: d.close, residencyTag: "eu",
547
+ } } });
548
+ var err = null;
549
+ await _underGdpr(async function () {
550
+ try { await b.externalDb.query(sql, []); } catch (e) { err = e; }
551
+ });
552
+ check(label + " → passes untagged, wire reached", err === null && d.seen.length === 1);
553
+ }
554
+
555
+ async function _passesWithTag(label, sql) {
556
+ b.externalDb._resetForTest();
557
+ var d = _classDriver();
558
+ b.externalDb.init({ backends: { main: {
559
+ connect: d.connect, query: d.query, close: d.close, residencyTag: "eu",
560
+ } } });
561
+ var err = null;
562
+ await _underGdpr(async function () {
563
+ try { await b.externalDb.query(sql, [], { rowResidencyTag: "eu" }); }
564
+ catch (e) { err = e; }
565
+ });
566
+ check(label + " → matching tag reaches the wire", err === null && d.seen.length === 1);
567
+ }
568
+
569
+ // CTE-wrapped DML (the Codex P1 shape) — write, gated.
570
+ await _refusedUntagged("WITH ... INSERT (CTE write) untagged",
571
+ "WITH src AS (SELECT 1 AS id) INSERT INTO eu_users (id) SELECT id FROM src",
572
+ "RESIDENCY_GATE_REQUIRED");
573
+ await _passesWithTag("WITH ... INSERT (CTE write) tag 'eu'",
574
+ "WITH src AS (SELECT 1 AS id) INSERT INTO eu_users (id) SELECT id FROM src");
575
+ // CTE-wrapped SELECT — read, passes untagged.
576
+ await _passesUntagged("WITH ... SELECT (CTE read) untagged",
577
+ "WITH src AS (SELECT 1 AS id) SELECT id FROM src");
578
+ // RECURSIVE + MATERIALIZED keywords before the main verb don't confuse it.
579
+ await _refusedUntagged("WITH RECURSIVE ... UPDATE untagged",
580
+ "WITH RECURSIVE t AS (SELECT 1) UPDATE eu_users SET id = 2 WHERE id IN (SELECT * FROM t)",
581
+ "RESIDENCY_GATE_REQUIRED");
582
+
583
+ // COPY ... FROM loads rows (write); COPY ... TO exports (read).
584
+ await _refusedUntagged("COPY ... FROM (bulk load) untagged",
585
+ "COPY eu_users (id) FROM STDIN", "RESIDENCY_GATE_REQUIRED");
586
+ await _passesUntagged("COPY (query) TO (export) untagged",
587
+ "COPY (SELECT id FROM eu_users) TO STDOUT");
588
+
589
+ // EXPLAIN ANALYZE EXECUTES the wrapped statement; plain EXPLAIN does not.
590
+ await _refusedUntagged("EXPLAIN ANALYZE INSERT untagged",
591
+ "EXPLAIN ANALYZE INSERT INTO eu_users (id) VALUES (1)", "RESIDENCY_GATE_REQUIRED");
592
+ await _refusedUntagged("EXPLAIN (ANALYZE, FORMAT JSON) UPDATE untagged",
593
+ "EXPLAIN (ANALYZE, FORMAT JSON) UPDATE eu_users SET id = 2 WHERE id = 1",
594
+ "RESIDENCY_GATE_REQUIRED");
595
+ await _passesUntagged("EXPLAIN (plan-only) INSERT untagged",
596
+ "EXPLAIN INSERT INTO eu_users (id) VALUES (1)");
597
+ await _passesUntagged("EXPLAIN SELECT untagged",
598
+ "EXPLAIN SELECT id FROM eu_users");
599
+
600
+ // Opaque-write verbs — CALL / EXECUTE / DO — gated (fail-closed).
601
+ await _refusedUntagged("CALL stored-proc untagged",
602
+ "CALL load_eu_rows('x')", "RESIDENCY_GATE_REQUIRED");
603
+ await _refusedUntagged("EXECUTE prepared untagged",
604
+ "EXECUTE ins_eu (1)", "RESIDENCY_GATE_REQUIRED");
605
+ await _refusedUntagged("DO anonymous block untagged",
606
+ "DO $$ BEGIN INSERT INTO eu_users (id) VALUES (1); END $$",
607
+ "RESIDENCY_GATE_REQUIRED");
608
+
609
+ // REPLACE INTO (mysql/sqlite) — delete-then-insert, a write.
610
+ await _refusedUntagged("REPLACE INTO untagged",
611
+ "REPLACE INTO eu_users (id) VALUES (1)", "RESIDENCY_GATE_REQUIRED");
612
+
613
+ // A `;` inside a dollar-quoted body is DATA, not a statement
614
+ // separator — must not false-positive as MULTI_STATEMENT_REFUSED; the
615
+ // DO block is gated as the ROUTINE write it is.
616
+ await _refusedUntagged("DO body with inner ; is one statement",
617
+ "DO $$ BEGIN INSERT INTO eu_users (id) VALUES (1); INSERT INTO eu_users (id) VALUES (2); END $$",
618
+ "RESIDENCY_GATE_REQUIRED");
619
+
620
+ // An unresolvable WITH (no main verb past the CTE list) fails closed.
621
+ await _refusedUntagged("unresolvable WITH refused",
622
+ "WITH x AS (SELECT 1)", "STATEMENT_UNRESOLVED_REFUSED");
623
+
624
+ // ---- Final clean ----
625
+ b.externalDb._resetForTest();
626
+ b.compliance.clear();
627
+ }
628
+
629
+ module.exports = { run: run };
630
+
631
+ if (require.main === module) {
632
+ run().then(function () {
633
+ process.stdout.write("OK — external-db-residency: " + helpers.getChecks() + " checks passed\n");
634
+ }, function (e) {
635
+ process.stderr.write("FAIL: " + (e && e.stack || e) + "\n");
636
+ process.exit(1);
637
+ });
638
+ }
@@ -345,6 +345,25 @@ async function _runIdentifierGuard(g) {
345
345
  !b.guardAll.list().some(function (entry) { return entry.name === g.NAME; }));
346
346
  }
347
347
 
348
+ async function _runSqlGuard(g) {
349
+ var fx = g.INTEGRATION_FIXTURES;
350
+ var gate = g.gate({ profile: "strict" });
351
+
352
+ // Benign parameterized fragment → serve.
353
+ var rvBenign = await gate.check({ sql: fx.benignSql });
354
+ check("[" + g.NAME + "] direct gate: benign SQL → serve",
355
+ rvBenign.ok === true && rvBenign.action === "serve");
356
+
357
+ // Hostile fragment (stacked statement) → not serve.
358
+ var rvHostile = await gate.check({ sql: fx.hostileSql });
359
+ check("[" + g.NAME + "] direct gate: hostile SQL → not serve",
360
+ rvHostile.action !== "serve");
361
+
362
+ // Standalone primitive — not in guardAll's content-type dispatch.
363
+ check("[" + g.NAME + "] NOT registered in guardAll content-type dispatch",
364
+ !b.guardAll.list().some(function (entry) { return entry.name === g.NAME; }));
365
+ }
366
+
348
367
  async function _runAuthBundleGuard(g) {
349
368
  var fx = g.INTEGRATION_FIXTURES;
350
369
  var gate = g.gate({ profile: "strict" });
@@ -431,6 +450,8 @@ async function testGuardHostIntegrationAdaptive() {
431
450
  await _runFilenameGuard(g);
432
451
  } else if (g.KIND === "identifier") {
433
452
  await _runIdentifierGuard(g);
453
+ } else if (g.KIND === "sql") {
454
+ await _runSqlGuard(g);
434
455
  } else if (g.KIND === "oauth-flow") {
435
456
  await _runOauthFlowGuard(g);
436
457
  } else if (g.KIND === "graphql-request") {