@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,576 @@
1
+ "use strict";
2
+ /**
3
+ * Live b.externalDb test against the docker Postgres container — the
4
+ * framework's advertised PRIMARY external-db dialect. The "driver" is a
5
+ * docker-exec psql shim: connect() spawns a persistent
6
+ *
7
+ * docker exec -i blamejs-test-postgres psql -U blamejs -d blamejs_test ...
8
+ *
9
+ * subprocess (SQL fed over stdin, never as argv — no shell parsing of
10
+ * SQL), and query() writes one statement plus an `\echo <sentinel>`
11
+ * marker, reading the merged stdout/stderr block up to that sentinel.
12
+ * A persistent session per client is what lets the framework's
13
+ * transaction() keep BEGIN / SET LOCAL gucs / body / COMMIT on one
14
+ * backend session — the precondition for the RLS + sessionGucs proof.
15
+ *
16
+ * It removes any npm pg-driver dep AND exercises the framework's
17
+ * Postgres SQL, the read/write classifier, the per-row residency write
18
+ * gate, and ROW LEVEL SECURITY against a real server:
19
+ *
20
+ * 1. CRUD round-trip (CREATE / INSERT $1 / SELECT / UPDATE / DELETE)
21
+ * with real returned rows + affectedRows.
22
+ * 2. The read/write classifier on real SQL (SELECT read; INSERT /
23
+ * UPDATE / DELETE + a WITH-wrapped INSERT write) drives the gate.
24
+ * 3. The residency write gate WIRED on the real query path: a cross-
25
+ * border write is refused (and never persists), an in-region write
26
+ * succeeds (and persists).
27
+ * 4. declareRowPolicy RLS actually blocks a row for an unauthorized
28
+ * tenant while the authorized tenant sees its row, via the
29
+ * framework's transaction({ sessionGucs }) SET LOCAL plumbing and a
30
+ * restricted non-owner role.
31
+ */
32
+
33
+ var spawn = require("node:child_process").spawn;
34
+ var execFileSync = require("node:child_process").execFileSync;
35
+ var helpers = require("../helpers");
36
+ var check = helpers.check;
37
+ var services = require("../helpers/services");
38
+ var b = require("../../");
39
+
40
+ var CONTAINER = "blamejs-test-postgres";
41
+ // The field separator is set over stdin via `\pset fieldsep '\t'` (psql
42
+ // interprets the backslash escape into a real TAB there); passing -F'\t'
43
+ // in argv through `sh -c` would deliver the literal two chars backslash-t
44
+ // instead. -A = unaligned WITH a header row (column names recoverable).
45
+ // NOTE: no -q — quiet mode suppresses the per-statement command tags
46
+ // ("INSERT 0 1" / "UPDATE 2" / "DELETE 1") that carry affectedRows; the
47
+ // one-time prelude confirmation lines are drained before the first query.
48
+ // null sentinel distinguishes a real NULL from an empty string.
49
+ var PSQL_ARGS = "psql -U blamejs -d blamejs_test -A " +
50
+ "-v ON_ERROR_STOP=0 -P null=__BJNULL__ 2>&1";
51
+ var NULL_SENTINEL = "__BJNULL__";
52
+
53
+ // ---- one-shot psql (setup / teardown / out-of-band assertions) ----
54
+ // Shell-free SQL: the statement travels on stdin, never in argv.
55
+ function _psql(sql) {
56
+ var prelude = "\\pset fieldsep '\\t'\n";
57
+ var out = execFileSync(
58
+ "docker",
59
+ ["exec", "-i", CONTAINER, "sh", "-c",
60
+ "psql -U blamejs -d blamejs_test -qtA -P null=__BJNULL__ 2>&1"],
61
+ { input: prelude + sql + "\n", stdio: ["pipe", "pipe", "pipe"] }
62
+ ).toString("utf8");
63
+ if (/^ERROR:/m.test(out)) {
64
+ throw new Error("psql setup failed for [" + sql + "]:\n" + out);
65
+ }
66
+ return out;
67
+ }
68
+
69
+ // ---- persistent-session docker-exec psql driver ----
70
+ //
71
+ // Each client is a long-lived psql subprocess. Statements are written to
72
+ // its stdin terminated by an `\echo <sentinel>`; the driver reads merged
73
+ // stdout until the sentinel line, then parses the block. SQLSTATE-coded
74
+ // ERROR lines (psql VERBOSITY verbose) throw an Error carrying `.code`,
75
+ // so the framework's auth-failure / deadlock / RLS-denial handling sees a
76
+ // real Postgres SQLSTATE.
77
+ function _makeDockerPgDriver() {
78
+ return {
79
+ connect: function () {
80
+ return new Promise(function (resolve, reject) {
81
+ var child = spawn(
82
+ "docker",
83
+ ["exec", "-i", CONTAINER, "sh", "-c",
84
+ PSQL_ARGS + " ; echo __BLAMEJS_PSQL_EXIT__"],
85
+ { stdio: ["pipe", "pipe", "pipe"] }
86
+ );
87
+ var client = {
88
+ child: child,
89
+ buf: "",
90
+ pending: null, // { sentinel, resolve, reject }
91
+ closed: false,
92
+ exitErr: null,
93
+ };
94
+ child.on("error", function (e) {
95
+ client.exitErr = e;
96
+ if (client.pending) { var p = client.pending; client.pending = null; p.reject(e); }
97
+ });
98
+ child.on("close", function () {
99
+ client.closed = true;
100
+ if (client.pending) {
101
+ var p = client.pending; client.pending = null;
102
+ p.reject(new Error("psql session closed mid-statement"));
103
+ }
104
+ });
105
+ child.stdout.on("data", function (chunk) {
106
+ client.buf += chunk.toString("utf8");
107
+ _drain(client);
108
+ });
109
+ // Prime the session: tab field separator (a real TAB via psql's
110
+ // backslash interpretation), unaligned WITH headers (so column
111
+ // names are recoverable), no footer, verbose errors (SQLSTATE).
112
+ // Without -q these \pset lines each print a confirmation; drain
113
+ // everything up to a priming sentinel so the first real query's
114
+ // block starts clean.
115
+ var primeSentinel = "__BJ_PRIME__";
116
+ client.pending = {
117
+ sentinel: primeSentinel,
118
+ resolve: function () { resolve(client); },
119
+ reject: reject,
120
+ };
121
+ client.child.stdin.write(
122
+ "\\pset fieldsep '\\t'\n\\pset footer off\n\\set VERBOSITY verbose\n" +
123
+ "\\echo " + primeSentinel + "\n");
124
+ });
125
+ },
126
+
127
+ query: function (client, sql, params) {
128
+ params = params || [];
129
+ var bound = _bindParams(sql, params);
130
+ var sentinel = "__BJ_EOR_" + (++_seq) + "__";
131
+ return new Promise(function (resolve, reject) {
132
+ if (client.closed) { reject(new Error("psql session is closed")); return; }
133
+ client.pending = { sentinel: sentinel, resolve: resolve, reject: reject };
134
+ // Terminate the statement, then echo the sentinel. A trailing ';'
135
+ // on an already-';'-terminated statement is a harmless empty
136
+ // statement to Postgres.
137
+ client.child.stdin.write(bound + "\n;\n\\echo " + sentinel + "\n");
138
+ });
139
+ },
140
+
141
+ close: function (client) {
142
+ return new Promise(function (resolve) {
143
+ if (client.closed) { resolve(); return; }
144
+ try { client.child.stdin.end("\\q\n"); } catch (_e) { /* best effort */ }
145
+ var done = false;
146
+ client.child.on("close", function () { if (!done) { done = true; resolve(); } });
147
+ setTimeout(function () {
148
+ if (done) return;
149
+ done = true;
150
+ try { client.child.kill("SIGKILL"); } catch (_e) {}
151
+ resolve();
152
+ }, 2000);
153
+ });
154
+ },
155
+
156
+ dialect: "postgres",
157
+ };
158
+ }
159
+
160
+ var _seq = 0;
161
+
162
+ // Read complete statement blocks out of client.buf as the sentinel for
163
+ // the in-flight statement appears.
164
+ function _drain(client) {
165
+ if (!client.pending) return;
166
+ var sentinel = client.pending.sentinel;
167
+ var marker = "\n" + sentinel + "\n";
168
+ // Sentinel may also appear at buffer start (block produced no output).
169
+ var idx = client.buf.indexOf(marker);
170
+ var startAtZero = client.buf.indexOf(sentinel + "\n") === 0;
171
+ var block;
172
+ if (idx !== -1) {
173
+ block = client.buf.slice(0, idx);
174
+ client.buf = client.buf.slice(idx + marker.length);
175
+ } else if (startAtZero) {
176
+ block = "";
177
+ client.buf = client.buf.slice((sentinel + "\n").length);
178
+ } else {
179
+ return; // sentinel not yet fully received
180
+ }
181
+ var p = client.pending;
182
+ client.pending = null;
183
+ var parsed;
184
+ try {
185
+ parsed = _parseBlock(block);
186
+ } catch (e) {
187
+ return p.reject(e);
188
+ }
189
+ if (parsed.error) return p.reject(parsed.error);
190
+ p.resolve({ rows: parsed.rows, rowCount: parsed.rowCount });
191
+ }
192
+
193
+ // Substitute Postgres $1/$2 placeholders with quoted literals. Every
194
+ // test value is operator-controlled (ids / regions / numbers / null);
195
+ // strings are single-quote-escaped, numbers inlined, null → NULL. The
196
+ // framework passes params out-of-band to a real driver; this shim folds
197
+ // them in because psql-over-stdin has no bind protocol.
198
+ function _bindParams(sql, params) {
199
+ return sql.replace(/\$(\d+)/g, function (_m, n) {
200
+ var i = Number(n) - 1;
201
+ if (i < 0 || i >= params.length) {
202
+ throw new Error("placeholder $" + n + " has no matching param");
203
+ }
204
+ var v = params[i];
205
+ if (v === null || v === undefined) return "NULL";
206
+ if (typeof v === "number") return String(v);
207
+ if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
208
+ return "'" + String(v).replace(/'/g, "''") + "'";
209
+ });
210
+ }
211
+
212
+ // Command-tag lines psql prints for non-SELECT statements.
213
+ var _CMD_TAG_RE = /^(INSERT|UPDATE|DELETE|MERGE|SELECT|COPY|MOVE)\b(?:\s+\d+)*\s*$/;
214
+ // A control-keyword tag (BEGIN / COMMIT / SET / CREATE TABLE / ...) — no
215
+ // rows, no countable affected rows for our purposes.
216
+ var _CTRL_TAG_RE = /^(BEGIN|COMMIT|ROLLBACK|SET|RESET|SAVEPOINT|RELEASE|START|CREATE|DROP|ALTER|GRANT|REVOKE|TRUNCATE|COMMENT|DO|CALL|VACUUM|ANALYZE|EXPLAIN|TABLE|SHOW|DISCARD)\b/;
217
+
218
+ function _parseBlock(block) {
219
+ var lines = block.split(/\r?\n/);
220
+ // Drop a trailing empty line from the final newline.
221
+ while (lines.length && lines[lines.length - 1] === "") lines.pop();
222
+
223
+ // Surface a real Postgres error with its SQLSTATE so the framework's
224
+ // SQLSTATE-keyed paths (42501 RLS-denied, 28000 auth, 40001 deadlock)
225
+ // see a coded error.
226
+ for (var i = 0; i < lines.length; i++) {
227
+ var em = /^ERROR:\s+([0-9A-Za-z]{5}):\s*(.*)$/.exec(lines[i]);
228
+ if (em) {
229
+ var err = new Error("Postgres " + em[1] + ": " + em[2]);
230
+ err.code = em[1];
231
+ return { error: err };
232
+ }
233
+ }
234
+
235
+ // Affected-row count from a DML command tag (e.g. "INSERT 0 3" → 3,
236
+ // "UPDATE 2" → 2). The tag is the LAST tag-shaped line of the block.
237
+ var affected = null;
238
+ var dataLines = [];
239
+ for (var j = 0; j < lines.length; j++) {
240
+ var ln = lines[j];
241
+ if (/^(NOTICE|WARNING|DETAIL|HINT|LINE|LOCATION|CONTEXT|STATEMENT):/.test(ln)) continue;
242
+ var tm = _CMD_TAG_RE.exec(ln);
243
+ if (tm) {
244
+ var nums = ln.trim().split(/\s+/).slice(1).map(Number);
245
+ if (nums.length) affected = nums[nums.length - 1];
246
+ continue;
247
+ }
248
+ if (_CTRL_TAG_RE.test(ln) && ln.indexOf("\t") === -1) continue;
249
+ dataLines.push(ln);
250
+ }
251
+
252
+ // dataLines (if any) carry a header row first, then data rows.
253
+ var rows = [];
254
+ if (dataLines.length >= 1) {
255
+ var headers = dataLines[0].split("\t");
256
+ for (var k = 1; k < dataLines.length; k++) {
257
+ var cells = dataLines[k].split("\t");
258
+ var row = {};
259
+ for (var c = 0; c < headers.length; c++) {
260
+ var cell = cells[c];
261
+ if (cell === NULL_SENTINEL || cell === undefined) { row[headers[c]] = null; continue; }
262
+ // Native-type coercion at the driver boundary. A real pg driver
263
+ // returns a Postgres `boolean` as a JS true/false; the psql text
264
+ // protocol renders it 't'/'f'. declareRowPolicy.up() reads the
265
+ // boolean pg_class.relrowsecurity to decide whether to ENABLE RLS
266
+ // — left as the string "f" it would be JS-truthy and the ENABLE
267
+ // would be skipped. Coerce the boolean system columns the
268
+ // framework reads so the shim matches a real driver's typing.
269
+ if (_BOOL_COLUMNS[headers[c]] === true && (cell === "t" || cell === "f")) {
270
+ row[headers[c]] = (cell === "t");
271
+ } else {
272
+ row[headers[c]] = cell;
273
+ }
274
+ }
275
+ rows.push(row);
276
+ }
277
+ }
278
+ var rowCount = (affected !== null) ? affected : rows.length;
279
+ return { rows: rows, rowCount: rowCount, error: null };
280
+ }
281
+
282
+ // Postgres system columns the framework reads as JS booleans (a real pg
283
+ // driver coerces these; the psql text protocol renders them 't'/'f').
284
+ var _BOOL_COLUMNS = { relrowsecurity: true, relforcerowsecurity: true };
285
+
286
+ // Run fn() under the gdpr posture, restoring the prior posture in a
287
+ // finally so parallel smoke files aren't poisoned.
288
+ async function _underGdpr(fn) {
289
+ var prior = b.compliance.current();
290
+ b.compliance.clear();
291
+ b.compliance.set("gdpr");
292
+ try { await fn(); }
293
+ finally {
294
+ b.compliance.clear();
295
+ if (prior) b.compliance.set(prior);
296
+ }
297
+ }
298
+
299
+ async function _expectThrow(label, fn, expectedCode) {
300
+ var threw = null;
301
+ try { await fn(); } catch (e) { threw = e; }
302
+ check(label + ": threw " + expectedCode,
303
+ threw !== null && threw.code === expectedCode);
304
+ return threw;
305
+ }
306
+
307
+ async function run() {
308
+ var pg = await services.requireService("postgres");
309
+ if (!pg.ok) throw new Error("postgres unreachable: " + pg.reason);
310
+
311
+ // ---- fresh schema ----
312
+ _psql([
313
+ "DROP TABLE IF EXISTS orders;",
314
+ "DROP TABLE IF EXISTS eu_rows;",
315
+ "DROP TABLE IF EXISTS rls_sessions;",
316
+ "DROP ROLE IF EXISTS blamejs_rls_app;",
317
+ ].join("\n"));
318
+
319
+ var driver = _makeDockerPgDriver();
320
+ b.externalDb._resetForTest();
321
+ b.externalDb.init({
322
+ backends: {
323
+ ops: {
324
+ connect: driver.connect, query: driver.query, close: driver.close,
325
+ dialect: "postgres", residencyTag: "eu",
326
+ },
327
+ },
328
+ });
329
+
330
+ // ====================================================================
331
+ // 1. CRUD round-trip against real Postgres.
332
+ // ====================================================================
333
+ await b.externalDb.query(
334
+ "CREATE TABLE orders (id text PRIMARY KEY, region text, total int)", []);
335
+ check("postgres: CREATE TABLE ran", true);
336
+
337
+ var ins = await b.externalDb.query(
338
+ "INSERT INTO orders (id, region, total) VALUES ($1, $2, $3)",
339
+ ["o-1", "eu", 100]);
340
+ check("postgres: INSERT affectedRows = 1", ins.rowCount === 1);
341
+
342
+ await b.externalDb.query(
343
+ "INSERT INTO orders (id, region, total) VALUES ($1, $2, $3)",
344
+ ["o-2", "eu", 250]);
345
+
346
+ var sel = await b.externalDb.query(
347
+ "SELECT id, region, total FROM orders WHERE id = $1", ["o-1"]);
348
+ check("postgres: SELECT returns one row", sel.rowCount === 1 && sel.rows.length === 1);
349
+ check("postgres: SELECT round-trips id", sel.rows[0].id === "o-1");
350
+ check("postgres: SELECT round-trips region", sel.rows[0].region === "eu");
351
+ check("postgres: SELECT round-trips total", sel.rows[0].total === "100");
352
+
353
+ var upd = await b.externalDb.query(
354
+ "UPDATE orders SET total = $1 WHERE id = $2", [999, "o-1"]);
355
+ check("postgres: UPDATE affectedRows = 1", upd.rowCount === 1);
356
+ var selU = await b.externalDb.query("SELECT total FROM orders WHERE id = $1", ["o-1"]);
357
+ check("postgres: UPDATE persisted", selU.rows[0].total === "999");
358
+
359
+ var del = await b.externalDb.query("DELETE FROM orders WHERE id = $1", ["o-2"]);
360
+ check("postgres: DELETE affectedRows = 1", del.rowCount === 1);
361
+ var selAll = await b.externalDb.query("SELECT id FROM orders ORDER BY id", []);
362
+ check("postgres: one row remains after DELETE",
363
+ selAll.rowCount === 1 && selAll.rows[0].id === "o-1");
364
+
365
+ // NULL round-trips as a real null (distinguished from empty string).
366
+ await b.externalDb.query(
367
+ "INSERT INTO orders (id, region, total) VALUES ($1, $2, $3)",
368
+ ["o-null", null, 0]);
369
+ var selNull = await b.externalDb.query(
370
+ "SELECT region FROM orders WHERE id = $1", ["o-null"]);
371
+ check("postgres: NULL column round-trips as null", selNull.rows[0].region === null);
372
+ await b.externalDb.query("DELETE FROM orders WHERE id = $1", ["o-null"]);
373
+
374
+ // ====================================================================
375
+ // 2. read/write classifier on real SQL drives the gate.
376
+ // Under gdpr + eu backend: SELECT (read) passes untagged; a CTE-
377
+ // wrapped INSERT (write) is refused untagged — proving the
378
+ // classifier resolves the CTE main verb against real SQL the same
379
+ // way Postgres would execute it.
380
+ // ====================================================================
381
+ await _underGdpr(async function () {
382
+ var s = await b.externalDb.query("SELECT id FROM orders WHERE id = $1", ["o-1"]);
383
+ check("classifier: SELECT classified read → passes untagged, rows returned",
384
+ s.rowCount === 1);
385
+ });
386
+
387
+ await _underGdpr(async function () {
388
+ await _expectThrow("classifier: WITH ... INSERT (CTE write) refused untagged",
389
+ function () {
390
+ return b.externalDb.query(
391
+ "WITH src AS (SELECT 'o-cte' AS id) INSERT INTO orders (id) SELECT id FROM src", []);
392
+ },
393
+ "RESIDENCY_GATE_REQUIRED");
394
+ });
395
+ // The refused CTE write never reached Postgres.
396
+ var cteCheck = _psql("SELECT count(*) AS n FROM orders WHERE id = 'o-cte';");
397
+ check("classifier: refused CTE write did not persist", /\b0\b/.test(cteCheck.trim()));
398
+
399
+ // The SAME CTE write, run directly through psql, DOES place the row —
400
+ // confirming the statement is a genuine write that Postgres executes,
401
+ // not a no-op the gate refused for free.
402
+ _psql("WITH src AS (SELECT 'o-cte-real' AS id) INSERT INTO orders (id) SELECT id FROM src;");
403
+ var cteReal = _psql("SELECT count(*) AS n FROM orders WHERE id = 'o-cte-real';");
404
+ check("classifier: that CTE shape is a real write on Postgres", /\b1\b/.test(cteReal.trim()));
405
+ _psql("DELETE FROM orders WHERE id = 'o-cte-real';");
406
+
407
+ // ====================================================================
408
+ // 3. residency write gate WIRED on the real query path.
409
+ // eu backend + gdpr posture:
410
+ // - cross-border tag "us" → RESIDENCY_TAG_MISMATCH, row absent
411
+ // - in-region tag "eu" → succeeds, row present
412
+ // ====================================================================
413
+ await _underGdpr(async function () {
414
+ await _expectThrow("residency: cross-border write (tag 'us') refused",
415
+ function () {
416
+ return b.externalDb.query(
417
+ "INSERT INTO orders (id, region, total) VALUES ($1, $2, $3)",
418
+ ["o-us", "us", 1],
419
+ { rowResidencyTag: "us" });
420
+ },
421
+ "RESIDENCY_TAG_MISMATCH");
422
+ });
423
+ var usCheck = _psql("SELECT count(*) AS n FROM orders WHERE id = 'o-us';");
424
+ check("residency: refused cross-border write did NOT persist", /\b0\b/.test(usCheck.trim()));
425
+
426
+ await _underGdpr(async function () {
427
+ var ok = await b.externalDb.query(
428
+ "INSERT INTO orders (id, region, total) VALUES ($1, $2, $3)",
429
+ ["o-eu", "eu", 7],
430
+ { rowResidencyTag: "eu" });
431
+ check("residency: in-region write (tag 'eu') succeeded", ok.rowCount === 1);
432
+ });
433
+ var euCheck = _psql("SELECT count(*) AS n FROM orders WHERE id = 'o-eu';");
434
+ check("residency: in-region write DID persist", /\b1\b/.test(euCheck.trim()));
435
+
436
+ // Untagged write under gdpr + eu backend → gate required (wire not reached).
437
+ await _underGdpr(async function () {
438
+ await _expectThrow("residency: untagged write refused",
439
+ function () {
440
+ return b.externalDb.query(
441
+ "INSERT INTO orders (id) VALUES ($1)", ["o-untagged"]);
442
+ },
443
+ "RESIDENCY_GATE_REQUIRED");
444
+ });
445
+ var untaggedCheck = _psql("SELECT count(*) AS n FROM orders WHERE id = 'o-untagged';");
446
+ check("residency: untagged-refused write did NOT persist", /\b0\b/.test(untaggedCheck.trim()));
447
+
448
+ // ====================================================================
449
+ // 4 + 5. declareRowPolicy RLS + transaction({ sessionGucs }) SET LOCAL.
450
+ // A restricted, non-owner, non-superuser role + an RLS policy keyed
451
+ // on the app.tenant_id GUC. Through the framework's transaction()
452
+ // SET LOCAL plumbing, the authorized tenant sees its row and an
453
+ // unauthorized tenant sees zero — proving both the generated RLS DDL
454
+ // blocks rows AND the sessionGucs bindings take effect for the
455
+ // policy on a real session.
456
+ // ====================================================================
457
+
458
+ // Restricted role + table owned by the superuser; the role gets DML
459
+ // grants but is subject to RLS (RLS never applies to owners/superusers).
460
+ _psql([
461
+ "CREATE ROLE blamejs_rls_app NOLOGIN;",
462
+ "CREATE TABLE rls_sessions (id text PRIMARY KEY, tenant_id text NOT NULL, payload text);",
463
+ "INSERT INTO rls_sessions VALUES ('s-acme','acme','acme-secret'), ('s-globex','globex','globex-secret');",
464
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON rls_sessions TO blamejs_rls_app;",
465
+ ].join("\n"));
466
+
467
+ // Apply the framework-generated RLS migration. declareRowPolicy returns
468
+ // a migration spec whose up(xdb) issues the ENABLE + CREATE POLICY DDL;
469
+ // drive it with a thin xdb that calls the live backend's query path
470
+ // (explicit backend so a stray ALS role can't reroute it).
471
+ var policy = b.db.declareRowPolicy({
472
+ schema: "public",
473
+ table: "rls_sessions",
474
+ name: "tenant_isolation",
475
+ role: "blamejs_rls_app",
476
+ using: "tenant_id = current_setting('app.tenant_id', true)",
477
+ withCheck: "tenant_id = current_setting('app.tenant_id', true)",
478
+ command: "ALL",
479
+ });
480
+ check("declareRowPolicy: returns a migration spec", policy && typeof policy.up === "function");
481
+
482
+ var applyXdb = {
483
+ query: function (sql, params) {
484
+ return b.externalDb.query(sql, params, { backend: "ops" });
485
+ },
486
+ };
487
+ var applied = await policy.up(applyXdb, { externalDb: b.externalDb, backendName: "ops" });
488
+ check("declareRowPolicy: applied (policy name returned)",
489
+ applied && applied.policy === "public.rls_sessions.tenant_isolation");
490
+
491
+ // Confirm RLS is actually enabled on the real table.
492
+ var rlsState = _psql(
493
+ "SELECT relrowsecurity FROM pg_class WHERE relname = 'rls_sessions';");
494
+ check("RLS: ENABLE ROW LEVEL SECURITY took effect on the real table",
495
+ /\bt\b/.test(rlsState.trim()));
496
+ var polState = _psql(
497
+ "SELECT polname FROM pg_policy p JOIN pg_class c ON c.oid = p.polrelid " +
498
+ "WHERE c.relname = 'rls_sessions';");
499
+ check("RLS: the policy exists in pg_policy", /tenant_isolation/.test(polState));
500
+
501
+ // Authorized tenant — within a transaction set SET LOCAL role +
502
+ // app.tenant_id via sessionGucs; the RLS policy filters to that tenant.
503
+ var acmeRows = await b.externalDb.transaction(async function (tx) {
504
+ return await tx.query("SELECT id, tenant_id FROM rls_sessions ORDER BY id", []);
505
+ }, {
506
+ backend: "ops",
507
+ sessionGucs: { "role": "blamejs_rls_app", "app.tenant_id": "acme" },
508
+ });
509
+ check("RLS: authorized tenant 'acme' sees exactly its 1 row",
510
+ acmeRows.rowCount === 1 && acmeRows.rows.length === 1);
511
+ check("RLS: the visible row is acme's", acmeRows.rows[0].id === "s-acme");
512
+
513
+ // Unauthorized tenant — same table, a tenant value that owns no rows;
514
+ // RLS yields zero rows even though the rows physically exist.
515
+ var nobodyRows = await b.externalDb.transaction(async function (tx) {
516
+ return await tx.query("SELECT id, tenant_id FROM rls_sessions ORDER BY id", []);
517
+ }, {
518
+ backend: "ops",
519
+ sessionGucs: { "role": "blamejs_rls_app", "app.tenant_id": "nobody" },
520
+ });
521
+ check("RLS: unauthorized tenant 'nobody' sees ZERO rows", nobodyRows.rowCount === 0);
522
+
523
+ // The other real tenant ('globex') sees only its own row — confirms the
524
+ // GUC value, not a constant, drives the policy.
525
+ var globexRows = await b.externalDb.transaction(async function (tx) {
526
+ return await tx.query("SELECT id FROM rls_sessions ORDER BY id", []);
527
+ }, {
528
+ backend: "ops",
529
+ sessionGucs: { "role": "blamejs_rls_app", "app.tenant_id": "globex" },
530
+ });
531
+ check("RLS: tenant 'globex' sees exactly its own row",
532
+ globexRows.rowCount === 1 && globexRows.rows[0].id === "s-globex");
533
+
534
+ // Control: the owning superuser (no SET LOCAL role) bypasses RLS and
535
+ // sees every row — proves the prior zero/one results were RLS filtering,
536
+ // not an empty table or a broken session.
537
+ var allRows = await b.externalDb.query("SELECT id FROM rls_sessions ORDER BY id", []);
538
+ check("RLS: owner/superuser bypasses RLS and sees all rows", allRows.rowCount === 2);
539
+
540
+ // The WITH CHECK clause blocks a cross-tenant INSERT: as 'acme', try to
541
+ // write a 'globex' row → 42501 (policy violation). This proves the
542
+ // sessionGucs binding gates WRITES too, not just reads.
543
+ await _expectThrow("RLS: WITH CHECK blocks a cross-tenant INSERT (SQLSTATE 42501)",
544
+ function () {
545
+ return b.externalDb.transaction(async function (tx) {
546
+ await tx.query(
547
+ "INSERT INTO rls_sessions (id, tenant_id, payload) VALUES ($1, $2, $3)",
548
+ ["s-evil", "globex", "x"]);
549
+ }, {
550
+ backend: "ops",
551
+ sessionGucs: { "role": "blamejs_rls_app", "app.tenant_id": "acme" },
552
+ });
553
+ },
554
+ "42501");
555
+ var evilCheck = _psql("SELECT count(*) AS n FROM rls_sessions WHERE id = 's-evil';");
556
+ check("RLS: the cross-tenant INSERT did NOT persist (rolled back)",
557
+ /\b0\b/.test(evilCheck.trim()));
558
+
559
+ // ---- teardown ----
560
+ await b.externalDb.shutdown();
561
+ b.compliance.clear();
562
+ _psql([
563
+ "DROP TABLE IF EXISTS orders;",
564
+ "DROP TABLE IF EXISTS rls_sessions;",
565
+ "DROP ROLE IF EXISTS blamejs_rls_app;",
566
+ ].join("\n"));
567
+ }
568
+
569
+ module.exports = { run: run };
570
+
571
+ if (require.main === module) {
572
+ run().then(
573
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
574
+ function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
575
+ );
576
+ }