@blamejs/blamejs-shop 0.5.12 → 0.5.14

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 (522) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/sales-tax-filings.js +143 -0
  4. package/lib/vendor/MANIFEST.json +632 -416
  5. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
  6. package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
  7. package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
  8. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
  9. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
  10. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
  11. package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
  12. package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
  13. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
  14. package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
  15. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
  16. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
  17. package/lib/vendor/blamejs/.gitignore +2 -0
  18. package/lib/vendor/blamejs/CHANGELOG.md +122 -0
  19. package/lib/vendor/blamejs/NOTICE +26 -1
  20. package/lib/vendor/blamejs/SECURITY.md +1 -1
  21. package/lib/vendor/blamejs/api-snapshot.json +207 -5
  22. package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
  23. package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
  24. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
  25. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
  26. package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
  27. package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
  28. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
  29. package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
  30. package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
  31. package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
  32. package/lib/vendor/blamejs/index.js +6 -0
  33. package/lib/vendor/blamejs/lib/a2a.js +4 -4
  34. package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
  35. package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
  36. package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
  37. package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
  38. package/lib/vendor/blamejs/lib/archive-read.js +8 -0
  39. package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
  40. package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
  41. package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
  42. package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
  43. package/lib/vendor/blamejs/lib/audit.js +71 -8
  44. package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
  45. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
  46. package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
  47. package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
  48. package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
  49. package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
  50. package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
  51. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
  52. package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
  53. package/lib/vendor/blamejs/lib/backup/index.js +16 -5
  54. package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
  55. package/lib/vendor/blamejs/lib/base32.js +21 -0
  56. package/lib/vendor/blamejs/lib/break-glass.js +19 -10
  57. package/lib/vendor/blamejs/lib/bundler.js +5 -1
  58. package/lib/vendor/blamejs/lib/cache-status.js +3 -7
  59. package/lib/vendor/blamejs/lib/calendar.js +21 -1
  60. package/lib/vendor/blamejs/lib/cbor.js +12 -1
  61. package/lib/vendor/blamejs/lib/cert.js +29 -11
  62. package/lib/vendor/blamejs/lib/cli.js +94 -24
  63. package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
  64. package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
  65. package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
  66. package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
  67. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
  68. package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
  69. package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
  70. package/lib/vendor/blamejs/lib/content-digest.js +2 -1
  71. package/lib/vendor/blamejs/lib/cookies.js +13 -3
  72. package/lib/vendor/blamejs/lib/cose.js +31 -16
  73. package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
  74. package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
  75. package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
  76. package/lib/vendor/blamejs/lib/crypto.js +314 -19
  77. package/lib/vendor/blamejs/lib/csp.js +11 -3
  78. package/lib/vendor/blamejs/lib/daemon.js +270 -24
  79. package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
  80. package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
  81. package/lib/vendor/blamejs/lib/db-query.js +34 -7
  82. package/lib/vendor/blamejs/lib/db.js +18 -6
  83. package/lib/vendor/blamejs/lib/dbsc.js +17 -0
  84. package/lib/vendor/blamejs/lib/dev.js +21 -2
  85. package/lib/vendor/blamejs/lib/dsr.js +157 -102
  86. package/lib/vendor/blamejs/lib/external-db.js +41 -2
  87. package/lib/vendor/blamejs/lib/file-upload.js +25 -7
  88. package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
  89. package/lib/vendor/blamejs/lib/forms.js +113 -27
  90. package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
  91. package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
  92. package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
  93. package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
  94. package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
  95. package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
  96. package/lib/vendor/blamejs/lib/guard-html.js +26 -53
  97. package/lib/vendor/blamejs/lib/guard-image.js +24 -5
  98. package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
  99. package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
  100. package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
  101. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
  102. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
  103. package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
  104. package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
  105. package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
  106. package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
  107. package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
  108. package/lib/vendor/blamejs/lib/guard-time.js +1 -1
  109. package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
  110. package/lib/vendor/blamejs/lib/html-balance.js +3 -3
  111. package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
  112. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
  113. package/lib/vendor/blamejs/lib/http-client.js +39 -7
  114. package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
  115. package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
  116. package/lib/vendor/blamejs/lib/i18n.js +35 -21
  117. package/lib/vendor/blamejs/lib/incident-report.js +8 -1
  118. package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
  119. package/lib/vendor/blamejs/lib/json-path.js +23 -1
  120. package/lib/vendor/blamejs/lib/jwk.js +20 -3
  121. package/lib/vendor/blamejs/lib/link-header.js +2 -1
  122. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
  123. package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
  124. package/lib/vendor/blamejs/lib/log-stream.js +8 -3
  125. package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
  126. package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
  127. package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
  128. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
  129. package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
  130. package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
  131. package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
  132. package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
  133. package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
  134. package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
  135. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
  136. package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
  137. package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
  138. package/lib/vendor/blamejs/lib/mail.js +15 -5
  139. package/lib/vendor/blamejs/lib/metrics.js +268 -63
  140. package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
  141. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
  142. package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
  143. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
  144. package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
  145. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
  146. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
  147. package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
  148. package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
  149. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
  150. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
  151. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
  152. package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
  153. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
  154. package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
  155. package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
  156. package/lib/vendor/blamejs/lib/network-dns.js +21 -2
  157. package/lib/vendor/blamejs/lib/network-tls.js +69 -7
  158. package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
  159. package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
  160. package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
  161. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
  162. package/lib/vendor/blamejs/lib/outbox.js +1 -1
  163. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
  164. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
  165. package/lib/vendor/blamejs/lib/permissions.js +6 -3
  166. package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
  167. package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
  168. package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
  169. package/lib/vendor/blamejs/lib/pubsub.js +20 -9
  170. package/lib/vendor/blamejs/lib/queue-local.js +8 -1
  171. package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
  172. package/lib/vendor/blamejs/lib/redact.js +54 -0
  173. package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
  174. package/lib/vendor/blamejs/lib/restore.js +19 -0
  175. package/lib/vendor/blamejs/lib/retention.js +12 -4
  176. package/lib/vendor/blamejs/lib/router.js +60 -6
  177. package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
  178. package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
  179. package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
  180. package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
  181. package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
  182. package/lib/vendor/blamejs/lib/safe-object.js +80 -0
  183. package/lib/vendor/blamejs/lib/safe-path.js +52 -4
  184. package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
  185. package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
  186. package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
  187. package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
  188. package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
  189. package/lib/vendor/blamejs/lib/scheduler.js +8 -1
  190. package/lib/vendor/blamejs/lib/security-assert.js +6 -6
  191. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
  192. package/lib/vendor/blamejs/lib/self-update.js +517 -87
  193. package/lib/vendor/blamejs/lib/server-timing.js +2 -4
  194. package/lib/vendor/blamejs/lib/session.js +34 -12
  195. package/lib/vendor/blamejs/lib/sql.js +15 -1
  196. package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
  197. package/lib/vendor/blamejs/lib/static.js +55 -13
  198. package/lib/vendor/blamejs/lib/storage.js +8 -2
  199. package/lib/vendor/blamejs/lib/subject.js +6 -1
  200. package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
  201. package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
  202. package/lib/vendor/blamejs/lib/tsa.js +34 -13
  203. package/lib/vendor/blamejs/lib/uri-template.js +8 -1
  204. package/lib/vendor/blamejs/lib/vault/index.js +13 -0
  205. package/lib/vendor/blamejs/lib/vc.js +44 -5
  206. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
  207. package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
  208. package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
  209. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
  210. package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
  211. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
  212. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
  213. package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
  214. package/lib/vendor/blamejs/lib/watcher.js +89 -17
  215. package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
  216. package/lib/vendor/blamejs/lib/webhook.js +12 -2
  217. package/lib/vendor/blamejs/lib/ws-client.js +32 -1
  218. package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
  219. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
  220. package/lib/vendor/blamejs/package-lock.json +2 -2
  221. package/lib/vendor/blamejs/package.json +1 -1
  222. package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
  223. package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
  224. package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
  225. package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
  226. package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
  227. package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
  228. package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
  229. package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
  230. package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
  231. package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
  232. package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
  233. package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
  234. package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
  235. package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
  236. package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
  237. package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
  238. package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
  239. package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
  240. package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
  241. package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
  242. package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
  243. package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
  244. package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
  245. package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
  246. package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
  247. package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
  248. package/lib/vendor/blamejs/socket.yml +94 -0
  249. package/lib/vendor/blamejs/test/00-primitives.js +605 -0
  250. package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
  251. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
  252. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
  253. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
  254. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
  255. package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
  256. package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
  257. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
  258. package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
  259. package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
  260. package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
  263. package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
  269. package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
  271. package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
  283. package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
  285. package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
  294. package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
  295. package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
  303. package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
  308. package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
  315. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
  317. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
  318. package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
  319. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
  320. package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
  321. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
  322. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
  323. package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
  324. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
  325. package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
  326. package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
  327. package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
  328. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
  329. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
  330. package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
  331. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
  332. package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
  333. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
  334. package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
  335. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
  336. package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
  337. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
  338. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
  339. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
  340. package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
  341. package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
  342. package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
  343. package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
  344. package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
  345. package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
  346. package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
  347. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
  348. package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
  349. package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
  350. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
  351. package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
  352. package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
  353. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  354. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
  355. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
  356. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
  357. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
  358. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
  359. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
  360. package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
  361. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
  362. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
  363. package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
  364. package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
  365. package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
  366. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
  367. package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
  368. package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
  369. package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
  370. package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
  371. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
  372. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
  373. package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
  374. package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
  375. package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
  376. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
  377. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
  378. package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
  379. package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
  380. package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
  381. package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
  382. package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
  383. package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
  384. package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
  385. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
  386. package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
  387. package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
  388. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
  389. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
  390. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
  391. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
  392. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
  393. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
  394. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
  395. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
  396. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
  397. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
  398. package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
  399. package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
  400. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
  401. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
  402. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
  403. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
  404. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
  405. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
  406. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
  407. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
  408. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
  409. package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
  410. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
  411. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
  412. package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
  413. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
  414. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
  415. package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
  416. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
  417. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
  418. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
  419. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
  420. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
  421. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
  422. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
  423. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
  424. package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
  425. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
  426. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
  427. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
  428. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
  429. package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
  430. package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
  431. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
  432. package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
  433. package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
  434. package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
  435. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
  436. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
  437. package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
  438. package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
  439. package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
  440. package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
  441. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
  442. package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
  443. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
  444. package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
  445. package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
  446. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
  447. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
  448. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
  449. package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
  450. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
  451. package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
  452. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
  453. package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
  454. package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
  455. package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
  456. package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
  457. package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
  458. package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
  459. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
  460. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
  461. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
  462. package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
  463. package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
  464. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
  465. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
  466. package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
  467. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
  468. package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
  469. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
  470. package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
  471. package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
  472. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
  473. package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
  474. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
  475. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
  476. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
  477. package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
  478. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
  479. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
  480. package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
  481. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
  482. package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
  483. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
  484. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
  485. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
  486. package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
  487. package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
  488. package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
  489. package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
  490. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
  491. package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
  492. package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
  493. package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
  494. package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
  495. package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
  496. package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
  497. package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
  498. package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
  499. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
  500. package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
  501. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
  502. package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
  503. package/lib/vendor/blamejs/test/smoke.js +2 -0
  504. package/package.json +1 -1
  505. package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
  506. package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
  507. package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
  508. package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
  509. package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
  510. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
  511. package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
  512. package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
  513. package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
  514. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
  515. package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
  516. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
  517. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
  518. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
  519. package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
  520. package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
  521. package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
  522. package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
@@ -0,0 +1,1214 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * external-db — error / defensive / adversarial branch coverage for
6
+ * b.externalDb. Drives the PUBLIC API with in-memory fake drivers (no real
7
+ * Postgres): init/config validation, backend-pick precedence, query retry +
8
+ * connection-destroy paths, transaction (deadlock retry / rollback /
9
+ * sessionGucs / non-atomic refusal), health checks, pool internals
10
+ * (waiter/drain/destroy/reap), read-replica routing + health + config
11
+ * residency, connectAs SET wiring, runAs role routing, and the internal SQL
12
+ * classifiers.
13
+ *
14
+ * Fault injection covers every catch/else: a driver that throws a chosen
15
+ * SQLSTATE on a matched statement, a connect() that throws, a ping() that
16
+ * throws, and transaction bodies that throw transient (40P01/40001) vs
17
+ * permanent errors.
18
+ */
19
+
20
+ var helpers = require("../helpers");
21
+ var b = helpers.b;
22
+ var check = helpers.check;
23
+
24
+ // ---- shared fixtures -------------------------------------------------------
25
+
26
+ // In-memory driver. Records every SQL string on `seen`. Options:
27
+ // connectThrows: <code> → connect() rejects with err.code = <code>
28
+ // pingThrows: <code> → adds a ping() that rejects
29
+ // ping: true → adds a resolving ping()
30
+ // failOn: { re, code, times } → query() throws err.code=<code> the first
31
+ // <times> statements matching <re>
32
+ // roles: [names] → a pg_roles SELECT returns those rolname rows
33
+ function mkDriver(label, opts) {
34
+ opts = opts || {};
35
+ var seen = [];
36
+ var roles = opts.roles || [];
37
+ var failOn = opts.failOn ? { re: opts.failOn.re, code: opts.failOn.code, times: opts.failOn.times } : null;
38
+ var d = {
39
+ label: label,
40
+ seen: seen,
41
+ connect: async function () {
42
+ if (opts.connectThrows) {
43
+ var ce = new Error(label + " connect fail");
44
+ ce.code = opts.connectThrows;
45
+ throw ce;
46
+ }
47
+ return { id: label };
48
+ },
49
+ query: async function (_client, sql, _params) {
50
+ seen.push(sql);
51
+ if (failOn && failOn.re.test(sql) && failOn.times > 0) {
52
+ failOn.times -= 1;
53
+ var e = new Error(label + " query fail");
54
+ e.code = failOn.code;
55
+ throw e;
56
+ }
57
+ if (/pg_roles/i.test(sql)) {
58
+ return { rows: roles.map(function (n) { return { rolname: n }; }), rowCount: roles.length };
59
+ }
60
+ if (/^SELECT\s+1\b/i.test(sql)) return { rows: [{ n: 1 }], rowCount: 1 };
61
+ if (/^(BEGIN|COMMIT|ROLLBACK|SET\b|SAVEPOINT|RELEASE)/i.test(sql)) return { rows: [], rowCount: 0 };
62
+ if (/^(SELECT|SHOW|PRAGMA|DESC|VALUES|TABLE)\b/i.test(sql)) return { rows: [{ src: label }], rowCount: 1 };
63
+ return { rows: [], rowCount: 1 };
64
+ },
65
+ close: async function () { /* no-op */ },
66
+ };
67
+ if (opts.pingThrows) {
68
+ d.ping = async function () { var pe = new Error(label + " ping fail"); pe.code = opts.pingThrows; throw pe; };
69
+ } else if (opts.ping) {
70
+ d.ping = async function () { return true; };
71
+ }
72
+ return d;
73
+ }
74
+
75
+ function _saw(driver, re) {
76
+ return driver.seen.some(function (s) { return re.test(s); });
77
+ }
78
+
79
+ function okBackend() {
80
+ return {
81
+ connect: async function () { return {}; },
82
+ query: async function () { return { rows: [], rowCount: 0 }; },
83
+ };
84
+ }
85
+
86
+ async function expectThrow(label, fn, code) {
87
+ var threw = null;
88
+ try { await fn(); } catch (e) { threw = e; }
89
+ check(label, threw !== null && (code === null || threw.code === code));
90
+ return threw;
91
+ }
92
+
93
+ function expectThrowSync(label, fn, code) {
94
+ var threw = null;
95
+ try { fn(); } catch (e) { threw = e; }
96
+ check(label, threw !== null && (code === null || threw.code === code));
97
+ return threw;
98
+ }
99
+
100
+ function expectInitThrow(label, initOpts, code) {
101
+ b.externalDb._resetForTest();
102
+ var threw = null;
103
+ try { b.externalDb.init(initOpts); } catch (e) { threw = e; }
104
+ check(label, threw !== null && (code === null || threw.code === code));
105
+ b.externalDb._resetForTest();
106
+ }
107
+
108
+ // Run fn() under the gdpr (cross-border-regulated) posture, restoring
109
+ // whatever posture was pinned before in a finally so the rest of this
110
+ // file's checks run unregulated. clear()+set() because compliance.set
111
+ // refuses a runtime switch when a different posture is already pinned.
112
+ async function _underGdpr(fn) {
113
+ var prior = b.compliance.current();
114
+ b.compliance.clear();
115
+ b.compliance.set("gdpr");
116
+ try { await fn(); }
117
+ finally { b.compliance.clear(); if (prior) b.compliance.set(prior); }
118
+ }
119
+
120
+ // ---- not-initialized guards ------------------------------------------------
121
+
122
+ async function testNotInitialized() {
123
+ b.externalDb._resetForTest();
124
+ await expectThrow("query before init → NOT_INITIALIZED",
125
+ function () { return b.externalDb.query("SELECT 1"); }, "NOT_INITIALIZED");
126
+ await expectThrow("transaction before init → NOT_INITIALIZED",
127
+ function () { return b.externalDb.transaction(async function () {}); }, "NOT_INITIALIZED");
128
+ await expectThrow("healthCheck before init → NOT_INITIALIZED",
129
+ function () { return b.externalDb.healthCheck(); }, "NOT_INITIALIZED");
130
+ await expectThrow("read.query before init → NOT_INITIALIZED",
131
+ function () { return b.externalDb.read.query("SELECT 1"); }, "NOT_INITIALIZED");
132
+ await expectThrow("assertRoleHardening before init → NOT_INITIALIZED",
133
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: [] }); }, "NOT_INITIALIZED");
134
+ expectThrowSync("configurePool before init → NOT_INITIALIZED",
135
+ function () { b.externalDb.configurePool("main", { min: 1 }); }, "NOT_INITIALIZED");
136
+ expectThrowSync("supportsTransactions before init → NOT_INITIALIZED",
137
+ function () { b.externalDb.supportsTransactions(); }, "NOT_INITIALIZED");
138
+
139
+ check("listBackends before init → []",
140
+ Array.isArray(b.externalDb.listBackends()) && b.externalDb.listBackends().length === 0);
141
+ await b.externalDb.shutdown();
142
+ check("shutdown before init is a no-op", true);
143
+ }
144
+
145
+ // ---- init config validation ------------------------------------------------
146
+
147
+ function testInitValidation() {
148
+ expectInitThrow("init(undefined) → throws", undefined, null);
149
+ expectInitThrow("init missing backends → throws", {}, null);
150
+ expectInitThrow("init backend missing connect → INVALID_CONFIG",
151
+ { backends: { main: { query: async function () {} } } }, "INVALID_CONFIG");
152
+ expectInitThrow("init backend missing query → INVALID_CONFIG",
153
+ { backends: { main: { connect: async function () {} } } }, "INVALID_CONFIG");
154
+ expectInitThrow("init unknown dialect → INVALID_CONFIG",
155
+ { backends: { main: { connect: async function () {}, query: async function () {}, dialect: "oracle" } } },
156
+ "INVALID_CONFIG");
157
+ expectInitThrow("init applicationName empty string → INVALID_CONFIG",
158
+ { backends: { main: { connect: async function () {}, query: async function () {}, applicationName: "" } } },
159
+ "INVALID_CONFIG");
160
+ expectInitThrow("init supportsTransactions non-boolean → INVALID_CONFIG",
161
+ { backends: { main: { connect: async function () {}, query: async function () {}, supportsTransactions: "yes" } } },
162
+ "INVALID_CONFIG");
163
+ expectInitThrow("init batch non-function → INVALID_CONFIG",
164
+ { backends: { main: { connect: async function () {}, query: async function () {}, batch: "nope" } } },
165
+ "INVALID_CONFIG");
166
+
167
+ // dbRoleBackends validation
168
+ expectInitThrow("dbRoleBackends non-object → INVALID_CONFIG",
169
+ { backends: { main: okBackend() }, dbRoleBackends: [] }, "INVALID_CONFIG");
170
+ expectInitThrow("dbRoleBackends invalid role identifier → INVALID_CONFIG",
171
+ { backends: { main: okBackend() }, dbRoleBackends: { "1bad": "main" } }, "INVALID_CONFIG");
172
+ expectInitThrow("dbRoleBackends backend name non-string → INVALID_CONFIG",
173
+ { backends: { main: okBackend() }, dbRoleBackends: { good_role: 123 } }, "INVALID_CONFIG");
174
+ expectInitThrow("dbRoleBackends unknown backend → INVALID_CONFIG",
175
+ { backends: { main: okBackend() }, dbRoleBackends: { good_role: "nope" } }, "INVALID_CONFIG");
176
+
177
+ // replica config validation
178
+ expectInitThrow("replicas non-array → INVALID_CONFIG",
179
+ { backends: { main: { connect: async function () {}, query: async function () {}, replicas: "x" } } },
180
+ "INVALID_CONFIG");
181
+ expectInitThrow("replicas empty array → INVALID_CONFIG",
182
+ { backends: { main: { connect: async function () {}, query: async function () {}, replicas: [] } } },
183
+ "INVALID_CONFIG");
184
+ expectInitThrow("replica missing connect → INVALID_CONFIG",
185
+ { backends: { main: { connect: async function () {}, query: async function () {},
186
+ replicas: [{ query: async function () {} }] } } }, "INVALID_CONFIG");
187
+ expectInitThrow("replica missing query → INVALID_CONFIG",
188
+ { backends: { main: { connect: async function () {}, query: async function () {},
189
+ replicas: [{ connect: async function () {} }] } } }, "INVALID_CONFIG");
190
+ expectInitThrow("replica weight non-positive → INVALID_CONFIG",
191
+ { backends: { main: { connect: async function () {}, query: async function () {},
192
+ replicas: [{ connect: async function () {}, query: async function () {}, weight: 0 }] } } },
193
+ "INVALID_CONFIG");
194
+ expectInitThrow("replica cross-border residency mismatch → RESIDENCY_MISMATCH",
195
+ { backends: { main: { connect: async function () {}, query: async function () {}, residencyTag: "EU",
196
+ replicas: [{ connect: async function () {}, query: async function () {}, residencyTag: "US" }] } } },
197
+ "RESIDENCY_MISMATCH");
198
+ }
199
+
200
+ // ---- init idempotency ------------------------------------------------------
201
+
202
+ function testInitIdempotent() {
203
+ b.externalDb._resetForTest();
204
+ var idemD = mkDriver("idem");
205
+ b.externalDb.init({ backends: { main: { connect: idemD.connect, query: idemD.query, close: idemD.close } } });
206
+ b.externalDb.init({ backends: { other: okBackend() } }); // second init ignored
207
+ var names = b.externalDb.listBackends().map(function (x) { return x.name; });
208
+ check("init is idempotent — second init ignored", names.length === 1 && names[0] === "main");
209
+ var row = b.externalDb.listBackends()[0];
210
+ check("listBackends surfaces dialect/classifications/breaker/pool",
211
+ row.dialect === "postgres" && Array.isArray(row.classifications) &&
212
+ row.breakerState === "closed" && row.pool && typeof row.pool.idle === "number");
213
+ b.externalDb._resetForTest();
214
+ }
215
+
216
+ // ---- backend-pick precedence ----------------------------------------------
217
+
218
+ async function testPickBackend() {
219
+ b.externalDb._resetForTest();
220
+ var persD = mkDriver("personal");
221
+ var opsD = mkDriver("ops");
222
+ b.externalDb.init({ backends: {
223
+ p: { connect: persD.connect, query: persD.query, close: persD.close, classifications: ["personal"] },
224
+ o: { connect: opsD.connect, query: opsD.query, close: opsD.close, classifications: ["operational"] },
225
+ } });
226
+ var cRes = await b.externalDb.query("SELECT id FROM t", [], { classification: "personal" });
227
+ check("classification routes to serving backend", cRes.rows[0].src === "personal");
228
+ await expectThrow("classification with no serving backend → NO_BACKEND_FOR_CLASSIFICATION",
229
+ function () { return b.externalDb.query("SELECT 1", [], { classification: "secret" }); },
230
+ "NO_BACKEND_FOR_CLASSIFICATION");
231
+ await expectThrow("explicit backend not serving classification → CLASSIFICATION_MISMATCH",
232
+ function () { return b.externalDb.query("SELECT 1", [], { backend: "p", classification: "operational" }); },
233
+ "CLASSIFICATION_MISMATCH");
234
+ await expectThrow("explicit unknown backend → UNKNOWN_BACKEND",
235
+ function () { return b.externalDb.query("SELECT 1", [], { backend: "nope" }); }, "UNKNOWN_BACKEND");
236
+ b.externalDb._resetForTest();
237
+ }
238
+
239
+ // defaultBackend must name a REGISTERED backend — a typo is refused at
240
+ // init() with a typed INVALID_CONFIG error rather than surfacing as an
241
+ // opaque TypeError when the first query dereferences a missing pool.
242
+ async function testDefaultBackendValidation() {
243
+ expectInitThrow("init defaultBackend typo → INVALID_CONFIG",
244
+ { backends: { main: okBackend() }, defaultBackend: "does_not_exist" }, "INVALID_CONFIG");
245
+ expectInitThrow("init defaultBackend non-string → INVALID_CONFIG",
246
+ { backends: { main: okBackend() }, defaultBackend: 123 }, "INVALID_CONFIG");
247
+ expectInitThrow("init defaultBackend empty string → INVALID_CONFIG",
248
+ { backends: { main: okBackend() }, defaultBackend: "" }, "INVALID_CONFIG");
249
+
250
+ // valid defaultBackend routes the default (no opts.backend / classification
251
+ // / role) query to the named backend — not the first-declared one.
252
+ b.externalDb._resetForTest();
253
+ var aD = mkDriver("A");
254
+ var bD = mkDriver("B");
255
+ b.externalDb.init({
256
+ backends: {
257
+ a: { connect: aD.connect, query: aD.query, close: aD.close },
258
+ b: { connect: bD.connect, query: bD.query, close: bD.close },
259
+ },
260
+ defaultBackend: "b",
261
+ });
262
+ var dres = await b.externalDb.query("SELECT id FROM t");
263
+ check("valid defaultBackend routes default query to the named backend", dres.rows[0].src === "B");
264
+ b.externalDb._resetForTest();
265
+
266
+ // omitted defaultBackend still falls back to the first declared backend
267
+ b.externalDb._resetForTest();
268
+ var cD = mkDriver("C");
269
+ var dD = mkDriver("D");
270
+ b.externalDb.init({
271
+ backends: {
272
+ c: { connect: cD.connect, query: cD.query, close: cD.close },
273
+ d: { connect: dD.connect, query: dD.query, close: dD.close },
274
+ },
275
+ });
276
+ var fres = await b.externalDb.query("SELECT id FROM t");
277
+ check("omitted defaultBackend falls back to first declared backend", fres.rows[0].src === "C");
278
+ b.externalDb._resetForTest();
279
+ }
280
+
281
+ // ---- query retry + error paths --------------------------------------------
282
+
283
+ async function testQueryPaths() {
284
+ // transient ECONNRESET retried, then succeeds (connection-destroy path)
285
+ b.externalDb._resetForTest();
286
+ var qD = mkDriver("q", { failOn: { re: /RETRYME/, code: "ECONNRESET", times: 1 } });
287
+ b.externalDb.init({ backends: { main: { connect: qD.connect, query: qD.query, close: qD.close,
288
+ retry: { maxAttempts: 3, baseDelayMs: 1, maxDelayMs: 1, jitterFactor: 0 } } } });
289
+ var qr = await b.externalDb.query("SELECT RETRYME FROM t");
290
+ check("query retries transient ECONNRESET then succeeds", qr.rows[0].src === "q");
291
+ check("query attempted twice (retry re-acquired)",
292
+ qD.seen.filter(function (s) { return /RETRYME/.test(s); }).length === 2);
293
+
294
+ // connection error exhausts retries and rethrows (destroy path each attempt)
295
+ b.externalDb._resetForTest();
296
+ var qD2 = mkDriver("q2", { failOn: { re: /SELECT/i, code: "ECONNRESET", times: 99 } });
297
+ b.externalDb.init({ backends: { main: { connect: qD2.connect, query: qD2.query, close: qD2.close,
298
+ retry: { maxAttempts: 2, baseDelayMs: 1, maxDelayMs: 1, jitterFactor: 0 } } } });
299
+ await expectThrow("query connection error exhausts retries and rethrows",
300
+ function () { return b.externalDb.query("SELECT id FROM t"); }, "ECONNRESET");
301
+
302
+ // non-connection 42501 → release path + db.role.denied + auth audit
303
+ b.externalDb._resetForTest();
304
+ var qD3 = mkDriver("q3", { failOn: { re: /secret/i, code: "42501", times: 99 } });
305
+ b.externalDb.init({ backends: { main: { connect: qD3.connect, query: qD3.query, close: qD3.close } } });
306
+ await expectThrow("query 42501 surfaces (release path + auth audit)",
307
+ function () { return b.externalDb.query("SELECT * FROM secret"); }, "42501");
308
+
309
+ // 28P01 auth-failure path
310
+ b.externalDb._resetForTest();
311
+ var qD4 = mkDriver("q4", { failOn: { re: /login/i, code: "28P01", times: 99 } });
312
+ b.externalDb.init({ backends: { main: { connect: qD4.connect, query: qD4.query, close: qD4.close } } });
313
+ await expectThrow("query 28P01 surfaces + auth audit",
314
+ function () { return b.externalDb.query("SELECT login FROM t"); }, "28P01");
315
+
316
+ b.externalDb._resetForTest();
317
+ }
318
+
319
+ // ---- transaction validation + execution -----------------------------------
320
+
321
+ async function testTransactionValidation() {
322
+ b.externalDb._resetForTest();
323
+ var okD = mkDriver("txok");
324
+ var noD = mkDriver("txno");
325
+ b.externalDb.init({ backends: {
326
+ ok: { connect: okD.connect, query: okD.query, close: okD.close },
327
+ no: { connect: noD.connect, query: noD.query, close: noD.close, supportsTransactions: false },
328
+ } });
329
+ check("supportsTransactions default backend → true", b.externalDb.supportsTransactions() === true);
330
+ check("supportsTransactions non-atomic backend → false", b.externalDb.supportsTransactions({ backend: "no" }) === false);
331
+
332
+ await expectThrow("transaction fn not function → INVALID_FN",
333
+ function () { return b.externalDb.transaction("notfn"); }, "INVALID_FN");
334
+ await expectThrow("transaction on non-atomic backend → NON_ATOMIC_BACKEND",
335
+ function () { return b.externalDb.transaction(async function () {}, { backend: "no" }); }, "NON_ATOMIC_BACKEND");
336
+ check("non-atomic tx refused before BEGIN reached the wire",
337
+ noD.seen.every(function (s) { return !/BEGIN/i.test(s); }));
338
+
339
+ await expectThrow("deadlockRetries 2.5 → INVALID_OPT",
340
+ function () { return b.externalDb.transaction(async function () {}, { deadlockRetries: 2.5 }); }, "INVALID_OPT");
341
+ await expectThrow("deadlockRetries -1 → INVALID_OPT",
342
+ function () { return b.externalDb.transaction(async function () {}, { deadlockRetries: -1 }); }, "INVALID_OPT");
343
+ await expectThrow("deadlockRetries Infinity → INVALID_OPT",
344
+ function () { return b.externalDb.transaction(async function () {}, { deadlockRetries: Infinity }); }, "INVALID_OPT");
345
+ await expectThrow("deadlockRetries string → INVALID_OPT",
346
+ function () { return b.externalDb.transaction(async function () {}, { deadlockRetries: "3" }); }, "INVALID_OPT");
347
+ await expectThrow("tx rowResidencyTag empty → INVALID_OPT",
348
+ function () { return b.externalDb.transaction(async function () {}, { rowResidencyTag: "" }); }, "INVALID_OPT");
349
+
350
+ await expectThrow("sessionGucs non-object → INVALID_SESSION_GUCS",
351
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: "x" }); }, "INVALID_SESSION_GUCS");
352
+ await expectThrow("sessionGucs null value → INVALID_SESSION_GUCS",
353
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: { "app.k": null } }); }, "INVALID_SESSION_GUCS");
354
+ await expectThrow("sessionGucs object value → INVALID_SESSION_GUCS",
355
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: { "app.k": {} } }); }, "INVALID_SESSION_GUCS");
356
+ await expectThrow("sessionGucs invalid identifier → INVALID_SESSION_GUCS",
357
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: { "1bad name": "v" } }); }, "INVALID_SESSION_GUCS");
358
+ await expectThrow("sessionGucs oversized value → INVALID_SESSION_GUCS",
359
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: { "app.k": "x".repeat(5000) } }); }, "INVALID_SESSION_GUCS");
360
+
361
+ b.externalDb._resetForTest();
362
+ }
363
+
364
+ async function testTransactionExec() {
365
+ // deadlock retry then success
366
+ b.externalDb._resetForTest();
367
+ var txD = mkDriver("tx");
368
+ b.externalDb.init({ backends: { main: { connect: txD.connect, query: txD.query, close: txD.close } } });
369
+ var dlAttempts = 0;
370
+ var dlResult = await b.externalDb.transaction(async function (tx) {
371
+ dlAttempts += 1;
372
+ if (dlAttempts === 1) { var e = new Error("deadlock"); e.code = "40P01"; throw e; }
373
+ await tx.query("SELECT id FROM t");
374
+ return "committed";
375
+ }, { deadlockRetries: 3 });
376
+ check("transaction retries transient 40P01 then commits", dlResult === "committed" && dlAttempts === 2);
377
+ check("transaction retry issued ROLLBACK then COMMIT", _saw(txD, /^ROLLBACK\b/) && _saw(txD, /^COMMIT\b/));
378
+
379
+ // deadlock exhausted → throws the transient code
380
+ b.externalDb._resetForTest();
381
+ var txD2 = mkDriver("tx2");
382
+ b.externalDb.init({ backends: { main: { connect: txD2.connect, query: txD2.query, close: txD2.close } } });
383
+ await expectThrow("transaction deadlock exhausted (0 retries) → throws 40001",
384
+ function () {
385
+ return b.externalDb.transaction(async function () { var e = new Error("dl"); e.code = "40001"; throw e; },
386
+ { deadlockRetries: 0 });
387
+ }, "40001");
388
+ check("exhausted deadlock issued ROLLBACK", _saw(txD2, /^ROLLBACK\b/));
389
+
390
+ // non-transient body throw rolls back and rethrows
391
+ b.externalDb._resetForTest();
392
+ var txD3 = mkDriver("tx3");
393
+ b.externalDb.init({ backends: { main: { connect: txD3.connect, query: txD3.query, close: txD3.close } } });
394
+ await expectThrow("transaction body throw rolls back and rethrows",
395
+ function () { return b.externalDb.transaction(async function () { throw new Error("body boom"); }); }, null);
396
+ check("non-transient tx failure issued ROLLBACK", _saw(txD3, /^ROLLBACK\b/));
397
+ check("non-transient tx failure did not COMMIT", txD3.seen.every(function (s) { return !/^COMMIT\b/i.test(s); }));
398
+
399
+ // commit success + sessionGucs (string/number/boolean) + timeouts
400
+ b.externalDb._resetForTest();
401
+ var txD4 = mkDriver("tx4");
402
+ b.externalDb.init({ backends: { main: { connect: txD4.connect, query: txD4.query, close: txD4.close } } });
403
+ var okVal = await b.externalDb.transaction(async function (tx) {
404
+ var r = await tx.query("SELECT id FROM t");
405
+ return r.rows[0].src;
406
+ }, { sessionGucs: { "app.tenant_id": "acme", "app.flag": true, "app.n": 7 },
407
+ statementTimeoutMs: 5000, idleInTransactionTimeoutMs: 3000 });
408
+ check("transaction commits and returns body value", okVal === "tx4");
409
+ check("transaction issued BEGIN + COMMIT", _saw(txD4, /^BEGIN\b/) && _saw(txD4, /^COMMIT\b/));
410
+ check("transaction applied SET LOCAL statement_timeout", _saw(txD4, /^SET LOCAL statement_timeout = 5000$/));
411
+ check("transaction applied SET LOCAL idle timeout", _saw(txD4, /^SET LOCAL idle_in_transaction_session_timeout = 3000$/));
412
+ check("transaction applied sessionGucs string", _saw(txD4, /^SET LOCAL "app"\."tenant_id" = 'acme'$/));
413
+ check("transaction applied sessionGucs boolean", _saw(txD4, /^SET LOCAL "app"\."flag" = true$/));
414
+ check("transaction applied sessionGucs number", _saw(txD4, /^SET LOCAL "app"\."n" = 7$/));
415
+
416
+ // write.query / write.transaction aliases route to primary
417
+ var wRes = await b.externalDb.write.query("SELECT id FROM t");
418
+ check("write.query routes to primary", wRes.rows[0].src === "tx4");
419
+ var wtRes = await b.externalDb.write.transaction(async function (tx) { await tx.query("SELECT 1"); return 42; });
420
+ check("write.transaction runs a transaction and returns", wtRes === 42);
421
+
422
+ b.externalDb._resetForTest();
423
+ }
424
+
425
+ // ---- health check ----------------------------------------------------------
426
+
427
+ async function testHealthCheck() {
428
+ b.externalDb._resetForTest();
429
+ var hcD = mkDriver("hc", { ping: true });
430
+ b.externalDb.init({ backends: { main: { connect: hcD.connect, query: hcD.query, close: hcD.close, ping: hcD.ping } } });
431
+ var h1 = await b.externalDb.healthCheck("main");
432
+ check("healthCheck(main) with ping hook → ok + breaker state + pool",
433
+ h1.ok === true && h1.breakerState === "closed" && h1.pool && typeof h1.pool.idle === "number");
434
+ var hAll = await b.externalDb.healthCheck();
435
+ check("healthCheck() → map keyed by backend", hAll.main && hAll.main.ok === true);
436
+ var hUnknown = await b.externalDb.healthCheck("nope");
437
+ check("healthCheck(unknown) → ok:false unknown backend", hUnknown.ok === false && /unknown backend/.test(hUnknown.error));
438
+
439
+ // no ping hook → falls back to SELECT 1
440
+ b.externalDb._resetForTest();
441
+ var hc2 = mkDriver("hc2");
442
+ b.externalDb.init({ backends: { main: { connect: hc2.connect, query: hc2.query, close: hc2.close } } });
443
+ var h2 = await b.externalDb.healthCheck("main");
444
+ check("healthCheck without ping hook uses SELECT 1", h2.ok === true && _saw(hc2, /^SELECT 1$/));
445
+
446
+ // ping throws → destroy client, ok:false
447
+ b.externalDb._resetForTest();
448
+ var hc3 = mkDriver("hc3", { pingThrows: "ETIMEDOUT" });
449
+ b.externalDb.init({ backends: { main: { connect: hc3.connect, query: hc3.query, close: hc3.close, ping: hc3.ping } } });
450
+ var h3 = await b.externalDb.healthCheck("main");
451
+ check("healthCheck ping failure → ok:false with error", h3.ok === false && /ping fail/.test(h3.error));
452
+
453
+ // connect throws → outer catch, ok:false
454
+ b.externalDb._resetForTest();
455
+ var hc4 = mkDriver("hc4", { connectThrows: "ECONNREFUSED" });
456
+ b.externalDb.init({ backends: { main: { connect: hc4.connect, query: hc4.query, close: hc4.close } } });
457
+ var h4 = await b.externalDb.healthCheck("main");
458
+ check("healthCheck connect failure → ok:false", h4.ok === false && /connect fail/.test(h4.error));
459
+
460
+ b.externalDb._resetForTest();
461
+ }
462
+
463
+ // ---- shutdown (with replicas) ---------------------------------------------
464
+
465
+ async function testShutdown() {
466
+ b.externalDb._resetForTest();
467
+ var sdP = mkDriver("sdP");
468
+ var sdR = mkDriver("sdR");
469
+ b.externalDb.init({ backends: { main: { connect: sdP.connect, query: sdP.query, close: sdP.close,
470
+ replicas: [{ connect: sdR.connect, query: sdR.query, close: sdR.close }] } } });
471
+ await b.externalDb.shutdown();
472
+ check("shutdown drains and clears backends", b.externalDb.listBackends().length === 0);
473
+ b.externalDb._resetForTest();
474
+ }
475
+
476
+ // ---- configurePool ---------------------------------------------------------
477
+
478
+ function testConfigurePool() {
479
+ b.externalDb._resetForTest();
480
+ var cpD = mkDriver("cp");
481
+ b.externalDb.init({ backends: { main: { connect: cpD.connect, query: cpD.query, close: cpD.close } } });
482
+ expectThrowSync("configurePool bad backendName type → INVALID_CONFIG",
483
+ function () { b.externalDb.configurePool(123, { min: 1 }); }, "INVALID_CONFIG");
484
+ expectThrowSync("configurePool unknown backend → UNKNOWN_BACKEND",
485
+ function () { b.externalDb.configurePool("nope", { min: 1 }); }, "UNKNOWN_BACKEND");
486
+ expectThrowSync("configurePool opts not object → INVALID_CONFIG",
487
+ function () { b.externalDb.configurePool("main", null); }, "INVALID_CONFIG");
488
+ expectThrowSync("configurePool unknown option → INVALID_CONFIG",
489
+ function () { b.externalDb.configurePool("main", { bogus: 1 }); }, "INVALID_CONFIG");
490
+ expectThrowSync("configurePool non-positive min → INVALID_CONFIG",
491
+ function () { b.externalDb.configurePool("main", { min: 0 }); }, "INVALID_CONFIG");
492
+ expectThrowSync("configurePool min > max → INVALID_CONFIG",
493
+ function () { b.externalDb.configurePool("main", { min: 10, max: 2 }); }, "INVALID_CONFIG");
494
+ var cpOk = true;
495
+ try { b.externalDb.configurePool("main", { min: 2, max: 20, idleTimeoutMs: 120000 }); } catch (_e) { cpOk = false; }
496
+ check("configurePool valid resize succeeds", cpOk);
497
+ b.externalDb._resetForTest();
498
+ }
499
+
500
+ // ---- adapters.connectAs ----------------------------------------------------
501
+
502
+ async function testConnectAs() {
503
+ var fn = function () { return {}; };
504
+ expectThrowSync("connectAs opts not object → INVALID_CONFIG",
505
+ function () { b.externalDb.adapters.connectAs(fn, null); }, "INVALID_CONFIG");
506
+ expectThrowSync("connectAs missing query → INVALID_CONFIG",
507
+ function () { b.externalDb.adapters.connectAs(fn, {}); }, "INVALID_CONFIG");
508
+ expectThrowSync("connectAs connect not function → INVALID_CONFIG",
509
+ function () { b.externalDb.adapters.connectAs(null, { query: fn }); }, "INVALID_CONFIG");
510
+ expectThrowSync("connectAs unknown option → INVALID_CONFIG",
511
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, bogus: 1 }); }, "INVALID_CONFIG");
512
+ expectThrowSync("connectAs searchPath empty → INVALID_CONFIG",
513
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, searchPath: [] }); }, "INVALID_CONFIG");
514
+ expectThrowSync("connectAs applicationName non-string → INVALID_CONFIG",
515
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, applicationName: 123 }); }, "INVALID_CONFIG");
516
+ expectThrowSync("connectAs statementTimeoutMs non-positive → INVALID_CONFIG",
517
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, statementTimeoutMs: 0 }); }, "INVALID_CONFIG");
518
+ expectThrowSync("connectAs gucs not object → INVALID_CONFIG",
519
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, gucs: "x" }); }, "INVALID_CONFIG");
520
+ expectThrowSync("connectAs gucs non-finite number → INVALID_CONFIG",
521
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, gucs: { work_mem: Infinity } }); }, "INVALID_CONFIG");
522
+ expectThrowSync("connectAs gucs string with newline → INVALID_CONFIG",
523
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, gucs: { search_path: "a\nb" } }); }, "INVALID_CONFIG");
524
+ expectThrowSync("connectAs role invalid identifier → throws",
525
+ function () { b.externalDb.adapters.connectAs(fn, { query: fn, role: "1bad" }); }, null);
526
+
527
+ // valid connectAs wiring — every SET runs on the freshly-acquired client
528
+ b.externalDb._resetForTest();
529
+ var caD = mkDriver("ca");
530
+ var wrapped = b.externalDb.adapters.connectAs(caD.connect, {
531
+ query: caD.query,
532
+ role: "analytics_user",
533
+ searchPath: ["analytics", "public"],
534
+ applicationName: "wiki:analytics",
535
+ statementTimeoutMs: 30000,
536
+ gucs: { idle_in_transaction_session_timeout: "60s", work_mem: 4096 },
537
+ });
538
+ b.externalDb.init({ backends: { main: { dialect: "postgres", connect: wrapped, query: caD.query, close: caD.close } } });
539
+ await b.externalDb.query("SELECT id FROM t");
540
+ check("connectAs issues SET ROLE", _saw(caD, /^SET ROLE "analytics_user"$/));
541
+ check("connectAs issues SET search_path", _saw(caD, /^SET search_path TO "analytics", "public"$/));
542
+ check("connectAs issues SET application_name", _saw(caD, /^SET application_name TO 'wiki:analytics'$/));
543
+ check("connectAs issues SET statement_timeout", _saw(caD, /^SET statement_timeout TO 30000$/));
544
+ check("connectAs issues numeric guc", _saw(caD, /^SET "work_mem" TO 4096$/));
545
+ check("connectAs issues string guc", _saw(caD, /^SET "idle_in_transaction_session_timeout" TO '60s'$/));
546
+ b.externalDb._resetForTest();
547
+ }
548
+
549
+ // ---- runAs / currentRole ---------------------------------------------------
550
+
551
+ async function testRunAs() {
552
+ check("currentRole() outside runAs → null", b.externalDb.currentRole() === null);
553
+ expectThrowSync("runAs fn not function → INVALID_FN",
554
+ function () { b.externalDb.runAs("r", "notfn"); }, "INVALID_FN");
555
+ expectThrowSync("runAs role not string → INVALID_ROLE",
556
+ function () { b.externalDb.runAs(123, function () {}); }, "INVALID_ROLE");
557
+ expectThrowSync("runAs role empty → INVALID_ROLE",
558
+ function () { b.externalDb.runAs("", function () {}); }, "INVALID_ROLE");
559
+ expectThrowSync("runAs role invalid identifier → throws",
560
+ function () { b.externalDb.runAs("1bad", function () {}); }, null);
561
+
562
+ b.externalDb._resetForTest();
563
+ var mainD = mkDriver("main");
564
+ var anaD = mkDriver("analytics");
565
+ b.externalDb.init({
566
+ backends: {
567
+ main: { connect: mainD.connect, query: mainD.query, close: mainD.close },
568
+ analytics: { connect: anaD.connect, query: anaD.query, close: anaD.close },
569
+ },
570
+ dbRoleBackends: { analytics_user: "analytics" },
571
+ });
572
+ var routedSrc = await b.externalDb.runAs("analytics_user", async function () {
573
+ check("currentRole inside runAs", b.externalDb.currentRole() === "analytics_user");
574
+ var r = await b.externalDb.query("SELECT id FROM t");
575
+ return r.rows[0].src;
576
+ });
577
+ check("runAs routes query to role-mapped backend", routedSrc === "analytics");
578
+ check("currentRole after runAs → null", b.externalDb.currentRole() === null);
579
+ await b.externalDb.runAs(null, async function () {
580
+ check("runAs(null) clears role", b.externalDb.currentRole() === null);
581
+ });
582
+ b.externalDb._resetForTest();
583
+ }
584
+
585
+ // ---- assertRoleHardening (uncovered branches) -----------------------------
586
+
587
+ async function testAssertRoleHardening() {
588
+ b.externalDb._resetForTest();
589
+ var arD = mkDriver("ar", { roles: ["app_user", "postgres", "pg_signal_backend"] });
590
+ b.externalDb.init({ backends: { main: { connect: arD.connect, query: arD.query, close: arD.close } } });
591
+ await expectThrow("assertRoleHardening opts missing → INVALID_CONFIG",
592
+ function () { return b.externalDb.assertRoleHardening(); }, "INVALID_CONFIG");
593
+ await expectThrow("assertRoleHardening mode invalid → INVALID_CONFIG",
594
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: ["app_user"], mode: "bogus" }); }, "INVALID_CONFIG");
595
+ await expectThrow("assertRoleHardening declaredRoles element not string → INVALID_CONFIG",
596
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: [123] }); }, "INVALID_CONFIG");
597
+ await expectThrow("assertRoleHardening unknown backend → UNKNOWN_BACKEND",
598
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: ["x"], backend: "nope" }); }, "UNKNOWN_BACKEND");
599
+ var arOk = await b.externalDb.assertRoleHardening({ declaredRoles: ["app_user"], mode: "audit" });
600
+ check("assertRoleHardening ok path filters system roles",
601
+ arOk.unrecognized.length === 0 && arOk.missing.length === 0 && arOk.observed.indexOf("postgres") === -1);
602
+
603
+ // non-postgres dialect → skipped
604
+ b.externalDb._resetForTest();
605
+ var arSqlite = mkDriver("arsqlite");
606
+ b.externalDb.init({ backends: { main: { dialect: "sqlite", connect: arSqlite.connect, query: arSqlite.query, close: arSqlite.close } } });
607
+ var arSkip = await b.externalDb.assertRoleHardening({ declaredRoles: ["x"] });
608
+ check("assertRoleHardening non-postgres → skipped with empty observed",
609
+ arSkip.observed.length === 0 && arSkip.unrecognized.length === 0);
610
+
611
+ // pg_roles unreadable → ROLE_HARDENING_UNREADABLE
612
+ b.externalDb._resetForTest();
613
+ var arFail = mkDriver("arfail", { failOn: { re: /pg_roles/i, code: "XX000", times: 99 } });
614
+ b.externalDb.init({ backends: { main: { connect: arFail.connect, query: arFail.query, close: arFail.close } } });
615
+ await expectThrow("assertRoleHardening pg_roles unreadable → ROLE_HARDENING_UNREADABLE",
616
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: ["x"] }); }, "ROLE_HARDENING_UNREADABLE");
617
+
618
+ b.externalDb._resetForTest();
619
+ }
620
+
621
+ // ---- statement classifiers -------------------------------------------------
622
+
623
+ function testStatementHelpers() {
624
+ var srr = b.externalDb._statementReturnsRows;
625
+ check("statementReturnsRows SELECT → true", srr("SELECT * FROM t") === true);
626
+ check("statementReturnsRows INSERT → false", srr("INSERT INTO t (a) VALUES (1)") === false);
627
+ check("statementReturnsRows INSERT RETURNING → true", srr("INSERT INTO t (a) VALUES (1) RETURNING id") === true);
628
+ check("statementReturnsRows WITH CTE SELECT → true", srr("WITH x AS (SELECT 1) SELECT * FROM x") === true);
629
+ check("statementReturnsRows SHOW → true", srr("SHOW TABLES") === true);
630
+ check("statementReturnsRows empty → false", srr("") === false);
631
+ check("statementReturnsRows non-string → false", srr(null) === false);
632
+ check("statementReturnsRows UPDATE RETURNING → true", srr("UPDATE t SET a = 1 RETURNING *") === true);
633
+
634
+ // EXPLAIN always returns a plan row set to the caller — with or without
635
+ // ANALYZE, wrapping any inner statement (EXPLAIN ANALYZE INSERT executes
636
+ // the write AND yields the plan). This is the row-set question, distinct
637
+ // from the residency gate's read/write class. An EXPLAIN prefix that does
638
+ // not resolve (no inner statement, unbalanced option parens, unterminated
639
+ // span) stays fail-closed false.
640
+ check("statementReturnsRows EXPLAIN SELECT → true (plan rows)", srr("EXPLAIN SELECT a FROM t") === true);
641
+ check("statementReturnsRows EXPLAIN ANALYZE SELECT → true (plan rows)", srr("EXPLAIN ANALYZE SELECT a FROM t") === true);
642
+ check("statementReturnsRows EXPLAIN ANALYZE INSERT → true (executes write, returns plan)", srr("EXPLAIN ANALYZE INSERT INTO t (a) VALUES (1)") === true);
643
+ check("statementReturnsRows EXPLAIN (ANALYZE, FORMAT JSON) SELECT → true", srr("EXPLAIN (ANALYZE, FORMAT JSON) SELECT a FROM t") === true);
644
+ check("statementReturnsRows EXPLAIN (VERBOSE) SELECT → true (plan rows)", srr("EXPLAIN (VERBOSE) SELECT a FROM t") === true);
645
+ check("statementReturnsRows EXPLAIN ANALYZE with no inner statement → false (unresolvable)", srr("EXPLAIN ANALYZE") === false);
646
+ check("statementReturnsRows EXPLAIN unbalanced option parens → false (unresolvable)", srr("EXPLAIN (ANALYZE SELECT 1") === false);
647
+ check("statementReturnsRows EXPLAIN unterminated string → false (unresolvable)", srr("EXPLAIN 'oops") === false);
648
+
649
+ // WITH (CTE) main-verb resolution walks past the CTE list (opaque spans
650
+ // skipped, parens depth-tracked) to the effective verb.
651
+ check("statementReturnsRows WITH CTE INSERT → false (effective write)", srr("WITH s AS (SELECT 1) INSERT INTO t (a) SELECT 1 FROM s") === false);
652
+ check("statementReturnsRows WITH CTE string span → SELECT read", srr("WITH s AS (SELECT 'x') SELECT * FROM s") === true);
653
+ check("statementReturnsRows WITH RECURSIVE ... SELECT → true", srr("WITH RECURSIVE s AS (SELECT 1) SELECT * FROM s") === true);
654
+ check("statementReturnsRows unresolvable WITH (no main verb) → false", srr("WITH x AS (SELECT 1)") === false);
655
+ check("statementReturnsRows WITH + unterminated span → false", srr("WITH x AS (SELECT 'oops") === false);
656
+
657
+ // _hasTopLevelReturning skips every opaque-span shape before matching a
658
+ // top-level RETURNING (a doubled-quote string, a bracket identifier, a
659
+ // dollar-quoted body, a bare $n placeholder, line / block comments).
660
+ check("statementReturnsRows INSERT string literal + RETURNING → true", srr("INSERT INTO t (a) VALUES ('o''k') RETURNING id") === true);
661
+ check("statementReturnsRows UPDATE bracket identifier + RETURNING → true", srr("UPDATE [my tbl] SET a = 1 RETURNING x") === true);
662
+ check("statementReturnsRows INSERT dollar-quoted body + RETURNING → true", srr("INSERT INTO t (a) VALUES ($tag$hi$tag$) RETURNING id") === true);
663
+ check("statementReturnsRows INSERT $1 placeholder (not a span) + RETURNING → true", srr("INSERT INTO t1 (a) VALUES ($1) RETURNING id1") === true);
664
+ check("statementReturnsRows INSERT line comment before RETURNING → true", srr("INSERT INTO t (a) VALUES (1) -- note\n RETURNING id") === true);
665
+ check("statementReturnsRows INSERT block comment before RETURNING → true", srr("INSERT INTO t /* c */ (a) VALUES (1) RETURNING id") === true);
666
+ check("statementReturnsRows INSERT unterminated string swallows RETURNING → false", srr("INSERT INTO t (a) VALUES ('oops RETURNING id") === false);
667
+ check("statementReturnsRows RETURNING inside a subquery paren does not count → false", srr("INSERT INTO t SELECT * FROM (SELECT returning FROM x) y") === false);
668
+
669
+ // Leading-keyword classification fall-throughs.
670
+ check("statementReturnsRows VALUES row constructor → true (SELECT class)", srr("VALUES (1), (2)") === true);
671
+ check("statementReturnsRows TABLE t → true (SELECT class)", srr("TABLE users") === true);
672
+ check("statementReturnsRows DESCRIBE → true (READ_INFO)", srr("DESCRIBE users") === true);
673
+ check("statementReturnsRows PRAGMA → true (READ_INFO)", srr("PRAGMA table_info(t)") === true);
674
+ check("statementReturnsRows unknown leading keyword → false (OTHER class)", srr("FLOOB t") === false);
675
+ check("statementReturnsRows no leading keyword → false (UNKNOWN)", srr("### not sql") === false);
676
+
677
+ var xr = b.externalDb._extractTargetRelation;
678
+ check("extractTargetRelation NUL in quoted identifier → null",
679
+ xr('SELECT * FROM "a' + String.fromCharCode(0) + 'b"') === null);
680
+ check("extractTargetRelation non-string → null", xr(null) === null);
681
+ }
682
+
683
+ // ---- residency advisory (unregulated posture) -----------------------------
684
+
685
+ async function testResidencyAdvisory() {
686
+ b.compliance.clear(); // ensure no cross-border-regulated posture is pinned
687
+ b.externalDb._resetForTest();
688
+ var advD = mkDriver("adv");
689
+ b.externalDb.init({ backends: { main: { connect: advD.connect, query: advD.query, close: advD.close, residencyTag: "eu" } } });
690
+ await b.externalDb.query("INSERT INTO t (a) VALUES (1)", [], { rowResidencyTag: "eu" });
691
+ check("unregulated posture + tag on write → advisory, statement reaches wire", _saw(advD, /INSERT INTO t/));
692
+ var advRead = await b.externalDb.query("SELECT a FROM t", [], { rowResidencyTag: "eu" });
693
+ check("unregulated posture + tag on read → passes (no advisory branch)", advRead.rows[0].src === "adv");
694
+ b.externalDb._resetForTest();
695
+ }
696
+
697
+ // ---- residency write gate (cross-border-regulated posture) ----------------
698
+
699
+ // Under a cross-border-regulated posture (gdpr) the per-row residency write
700
+ // gate engages for a residency-tagged backend: untagged writes are refused,
701
+ // reads / matching tags / COPY ... TO exports pass, and write verbs wearing a
702
+ // harmless leading keyword (CTE-INSERT, EXPLAIN ANALYZE write, COPY ... FROM,
703
+ // CALL, a COPY with neither FROM/TO) are still gated fail-closed. Exercises
704
+ // _assertRowResidency's enforced path, _hasTrailingStatement, _copyLoadsRows,
705
+ // and the read-replica residency read gate.
706
+ async function testResidencyGate() {
707
+ function initEu() {
708
+ b.externalDb._resetForTest();
709
+ var d = mkDriver("eu");
710
+ b.externalDb.init({ backends: { main: {
711
+ connect: d.connect, query: d.query, close: d.close, residencyTag: "EU",
712
+ } } });
713
+ return d;
714
+ }
715
+
716
+ var g1 = initEu();
717
+ await _underGdpr(async function () {
718
+ await expectThrow("gdpr + EU backend + untagged DML → RESIDENCY_GATE_REQUIRED",
719
+ function () { return b.externalDb.query("INSERT INTO t (a) VALUES (1)"); },
720
+ "RESIDENCY_GATE_REQUIRED");
721
+ });
722
+ check("RESIDENCY_GATE_REQUIRED refused before the wire",
723
+ g1.seen.every(function (s) { return !/INSERT INTO t/.test(s); }));
724
+
725
+ var g2 = initEu();
726
+ await _underGdpr(async function () {
727
+ await b.externalDb.query("INSERT INTO t (a) VALUES (1)", [], { rowResidencyTag: "EU" });
728
+ });
729
+ check("matching rowResidencyTag reaches the wire", _saw(g2, /INSERT INTO t/));
730
+
731
+ var g3 = initEu();
732
+ await _underGdpr(async function () {
733
+ await expectThrow("mismatched rowResidencyTag → RESIDENCY_TAG_MISMATCH",
734
+ function () { return b.externalDb.query("UPDATE t SET a = 1", [], { rowResidencyTag: "US" }); },
735
+ "RESIDENCY_TAG_MISMATCH");
736
+ });
737
+ check("RESIDENCY_TAG_MISMATCH refused before the wire",
738
+ g3.seen.every(function (s) { return !/UPDATE t/.test(s); }));
739
+
740
+ var g4 = initEu();
741
+ await _underGdpr(async function () {
742
+ await expectThrow("empty rowResidencyTag on the gated path → INVALID_OPT",
743
+ function () { return b.externalDb.query("DELETE FROM t", [], { rowResidencyTag: "" }); },
744
+ "INVALID_OPT");
745
+ });
746
+ check("empty-tag refusal never reached the wire",
747
+ g4.seen.every(function (s) { return !/DELETE FROM t/.test(s); }));
748
+
749
+ var g5 = initEu();
750
+ await _underGdpr(async function () {
751
+ await expectThrow("trailing statement on the gated path → MULTI_STATEMENT_REFUSED",
752
+ function () { return b.externalDb.query("SELECT 1; INSERT INTO t (a) VALUES (1)"); },
753
+ "MULTI_STATEMENT_REFUSED");
754
+ });
755
+ check("multi-statement refused before the wire",
756
+ g5.seen.every(function (s) { return !/INSERT INTO t/.test(s); }));
757
+
758
+ var g5b = initEu();
759
+ await _underGdpr(async function () {
760
+ await b.externalDb.query("INSERT INTO t (a) VALUES (1);", [], { rowResidencyTag: "EU" });
761
+ });
762
+ check("single statement with a bare trailing ; reaches the wire", _saw(g5b, /INSERT INTO t/));
763
+
764
+ initEu();
765
+ await _underGdpr(async function () {
766
+ await expectThrow("unresolvable WITH on the gated path → STATEMENT_UNRESOLVED_REFUSED",
767
+ function () { return b.externalDb.query("WITH x AS (SELECT 1)"); },
768
+ "STATEMENT_UNRESOLVED_REFUSED");
769
+ });
770
+
771
+ initEu();
772
+ await _underGdpr(async function () {
773
+ await expectThrow("WITH ... INSERT (CTE write) untagged → RESIDENCY_GATE_REQUIRED",
774
+ function () { return b.externalDb.query("WITH s AS (SELECT 1 AS id) INSERT INTO t (id) SELECT id FROM s"); },
775
+ "RESIDENCY_GATE_REQUIRED");
776
+ });
777
+
778
+ var g8 = initEu();
779
+ await _underGdpr(async function () {
780
+ await expectThrow("COPY ... FROM (bulk load) untagged → RESIDENCY_GATE_REQUIRED",
781
+ function () { return b.externalDb.query("COPY t (id) FROM STDIN"); },
782
+ "RESIDENCY_GATE_REQUIRED");
783
+ await b.externalDb.query("COPY (SELECT id FROM t) TO STDOUT"); // export → read → passes
784
+ });
785
+ check("COPY ... TO export reached the wire", _saw(g8, /COPY \(SELECT id FROM t\) TO STDOUT/));
786
+
787
+ initEu();
788
+ await _underGdpr(async function () {
789
+ await expectThrow("COPY with neither FROM/TO → fail-closed write → RESIDENCY_GATE_REQUIRED",
790
+ function () { return b.externalDb.query("COPY t"); },
791
+ "RESIDENCY_GATE_REQUIRED");
792
+ });
793
+
794
+ var g9 = initEu();
795
+ await _underGdpr(async function () {
796
+ await expectThrow("EXPLAIN ANALYZE INSERT untagged → RESIDENCY_GATE_REQUIRED",
797
+ function () { return b.externalDb.query("EXPLAIN ANALYZE INSERT INTO t (a) VALUES (1)"); },
798
+ "RESIDENCY_GATE_REQUIRED");
799
+ await b.externalDb.query("EXPLAIN SELECT a FROM t"); // plan-only read → passes
800
+ });
801
+ check("plain EXPLAIN reached the wire (read, not gated)", _saw(g9, /EXPLAIN SELECT a FROM t/));
802
+
803
+ initEu();
804
+ await _underGdpr(async function () {
805
+ await expectThrow("CALL routine untagged → RESIDENCY_GATE_REQUIRED",
806
+ function () { return b.externalDb.query("CALL do_load('x')"); },
807
+ "RESIDENCY_GATE_REQUIRED");
808
+ });
809
+
810
+ var g11 = initEu();
811
+ await _underGdpr(async function () {
812
+ var r = await b.externalDb.query("SELECT a FROM t");
813
+ check("SELECT under the gate passes untagged, rows returned", r.rows[0].src === "eu");
814
+ });
815
+ check("SELECT under the gate reached the wire", _saw(g11, /SELECT a FROM t/));
816
+
817
+ // ---- read-replica residency read gate ----
818
+ // Primary is unrestricted so init accepts a US replica with
819
+ // allowCrossBorder:false (the config gate compares primary↔replica); the
820
+ // ROW↔replica gate is separate and fires at read time under gdpr.
821
+ b.externalDb._resetForTest();
822
+ var rp = mkDriver("rp");
823
+ var rr = mkDriver("usrep");
824
+ b.externalDb.init({ backends: { main: {
825
+ connect: rp.connect, query: rp.query, close: rp.close,
826
+ replicas: [{ connect: rr.connect, query: rr.query, close: rr.close, residencyTag: "US", allowCrossBorder: false }],
827
+ replicaFallbackToPrimary: false,
828
+ } } });
829
+ await _underGdpr(async function () {
830
+ await expectThrow("omitted tag to a residency-tagged replica → REPLICA_RESIDENCY_TAG_REQUIRED",
831
+ function () { return b.externalDb.read.query("SELECT a FROM t"); },
832
+ "REPLICA_RESIDENCY_TAG_REQUIRED");
833
+ await expectThrow("EU row to US replica (no cross-border) → REPLICA_RESIDENCY_INCOMPATIBLE",
834
+ function () { return b.externalDb.read.query("SELECT a FROM t", [], { rowResidencyTag: "EU" }); },
835
+ "REPLICA_RESIDENCY_INCOMPATIBLE");
836
+ });
837
+ check("residency-refused replica reads never touched the replica wire",
838
+ rr.seen.every(function (s) { return !/SELECT a FROM t/.test(s); }));
839
+
840
+ b.externalDb._resetForTest();
841
+ var rp2 = mkDriver("rp2");
842
+ var rr2 = mkDriver("usrep2");
843
+ b.externalDb.init({ backends: { main: {
844
+ connect: rp2.connect, query: rp2.query, close: rp2.close,
845
+ replicas: [{ connect: rr2.connect, query: rr2.query, close: rr2.close, residencyTag: "US", allowCrossBorder: true }],
846
+ replicaFallbackToPrimary: false,
847
+ } } });
848
+ await _underGdpr(async function () {
849
+ var r = await b.externalDb.read.query("SELECT a FROM t", [], { rowResidencyTag: "EU" });
850
+ check("allowCrossBorder replica serves the audited cross-border read", r.rows[0].src === "usrep2");
851
+ });
852
+
853
+ b.externalDb._resetForTest();
854
+ b.compliance.clear();
855
+ }
856
+
857
+ // ---- read-replica routing + health ----------------------------------------
858
+
859
+ async function testReplicas() {
860
+ // no replicas → read.query routes to primary
861
+ b.externalDb._resetForTest();
862
+ var np = mkDriver("noreprimary");
863
+ b.externalDb.init({ backends: { main: { connect: np.connect, query: np.query, close: np.close } } });
864
+ var nrRes = await b.externalDb.read.query("SELECT id FROM t");
865
+ check("read.query without replicas routes to primary", nrRes.rows[0].src === "noreprimary");
866
+
867
+ // healthy replica → read routes to replica, not primary
868
+ b.externalDb._resetForTest();
869
+ var primary = mkDriver("primary");
870
+ var rep = mkDriver("replica");
871
+ b.externalDb.init({ backends: { main: { connect: primary.connect, query: primary.query, close: primary.close,
872
+ replicas: [{ connect: rep.connect, query: rep.query, close: rep.close }] } } });
873
+ var rr = await b.externalDb.read.query("SELECT id FROM t");
874
+ check("read.query routes to replica", rr.rows[0].src === "replica");
875
+ check("read.query did not touch primary", primary.seen.length === 0);
876
+
877
+ // replica connection error → fallback to primary
878
+ b.externalDb._resetForTest();
879
+ var primary2 = mkDriver("primary2");
880
+ var rep2 = mkDriver("replica2", { failOn: { re: /SELECT/i, code: "ECONNRESET", times: 99 } });
881
+ b.externalDb.init({ backends: { main: { connect: primary2.connect, query: primary2.query, close: primary2.close,
882
+ replicas: [{ connect: rep2.connect, query: rep2.query, close: rep2.close }], replicaFallbackToPrimary: true } } });
883
+ var rr2 = await b.externalDb.read.query("SELECT id FROM t");
884
+ check("replica ECONNRESET falls back to primary", rr2.rows[0].src === "primary2");
885
+
886
+ // all replicas unhealthy + fallback disabled → ALL_REPLICAS_UNHEALTHY
887
+ b.externalDb._resetForTest();
888
+ var primary3 = mkDriver("primary3");
889
+ var rep3 = mkDriver("replica3", { failOn: { re: /SELECT/i, code: "ECONNRESET", times: 99 } });
890
+ b.externalDb.init({ backends: { main: { connect: primary3.connect, query: primary3.query, close: primary3.close,
891
+ replicas: [{ connect: rep3.connect, query: rep3.query, close: rep3.close }], replicaFallbackToPrimary: false } } });
892
+ await expectThrow("replica read failure, fallback disabled → surfaces the error",
893
+ function () { return b.externalDb.read.query("SELECT id FROM t"); }, "ECONNRESET");
894
+ await expectThrow("all replicas unhealthy + fallback disabled → ALL_REPLICAS_UNHEALTHY",
895
+ function () { return b.externalDb.read.query("SELECT id FROM t"); }, "ALL_REPLICAS_UNHEALTHY");
896
+
897
+ // cross-border replica with allowCrossBorder:true → init accepts, read serves
898
+ b.externalDb._resetForTest();
899
+ var pX = mkDriver("pX");
900
+ var rX = mkDriver("rX");
901
+ b.externalDb.init({ backends: { main: { connect: pX.connect, query: pX.query, close: pX.close, residencyTag: "EU",
902
+ replicas: [{ connect: rX.connect, query: rX.query, close: rX.close, residencyTag: "US", allowCrossBorder: true }] } } });
903
+ var rX1 = await b.externalDb.read.query("SELECT id FROM t");
904
+ check("cross-border replica with allowCrossBorder serves read", rX1.rows[0].src === "rX");
905
+
906
+ b.externalDb._resetForTest();
907
+ }
908
+
909
+ // ---- Pool internals (waiter / drain / destroy / reap / connect-error) ------
910
+
911
+ async function testPoolInternals() {
912
+ var Pool = b.externalDb.Pool;
913
+
914
+ // waiter resolves on release
915
+ var pA = new Pool("poolA", { connect: async function () { return { id: "a" }; }, close: async function () {}, pool: { max: 1 } });
916
+ try {
917
+ var a1 = await pA.acquire();
918
+ var pendingA = pA.acquire();
919
+ check("pool at max queues a waiter", pA.stats().waiters === 1);
920
+ pA.release(a1);
921
+ var a2 = await pendingA;
922
+ check("pool waiter resolved on release with the released client", a2 === a1);
923
+ check("pool stats after waiter resolve", pA.stats().active === 1 && pA.stats().waiters === 0);
924
+ pA.release(a2);
925
+ check("pool stats after final release", pA.stats().idle === 1 && pA.stats().active === 0);
926
+ } finally { await pA.drain(); }
927
+
928
+ // drain rejects queued waiter with POOL_DRAINED
929
+ var pB = new Pool("poolB", { connect: async function () { return { id: "b" }; }, pool: { max: 1 } });
930
+ await pB.acquire();
931
+ var pendingB = pB.acquire();
932
+ await pB.drain();
933
+ var rejB = null;
934
+ try { await pendingB; } catch (e) { rejB = e; }
935
+ check("drain rejects queued waiter with POOL_DRAINED", rejB && rejB.code === "POOL_DRAINED");
936
+
937
+ // destroy resolves a queued waiter via a fresh acquire
938
+ var pC = new Pool("poolC", { connect: async function () { return { id: "c" + Math.random() }; }, close: async function () {}, pool: { max: 1 } });
939
+ try {
940
+ var c1 = await pC.acquire();
941
+ var pendingC = pC.acquire();
942
+ await pC.destroy(c1);
943
+ var c2 = await pendingC;
944
+ check("destroy resolves queued waiter via fresh acquire", !!c2);
945
+ pC.release(c2);
946
+ } finally { await pC.drain(); }
947
+
948
+ // reaper removes expired idle clients (min:0 opts out of the warm floor)
949
+ var pD = new Pool("poolD", { connect: async function () { return { id: "d" }; }, close: async function () {}, pool: { min: 0, max: 2, idleTimeoutMs: 1 } });
950
+ try {
951
+ var d1 = await pD.acquire();
952
+ pD.release(d1);
953
+ check("released client goes idle", pD.stats().idle === 1);
954
+ pD.idle[0].lastUsedAt = Date.now() - 1000; // backdate past idleTimeoutMs
955
+ pD._reapIdle();
956
+ check("reaper removes expired idle client", pD.stats().idle === 0);
957
+ } finally { await pD.drain(); }
958
+
959
+ // a pool with no close hook falls back to the default no-op close on drain
960
+ var pClose = new Pool("poolClose", { connect: async function () { return { id: "x" }; }, pool: { max: 1 } });
961
+ var pc1 = await pClose.acquire();
962
+ pClose.release(pc1);
963
+ await pClose.drain();
964
+ check("pool without a close hook drains via the default close", pClose.stats().idle === 0);
965
+
966
+ // acquire surfaces a connect error and decrements active
967
+ var pE = new Pool("poolE", { connect: async function () { var e = new Error("nope"); e.code = "ECONNREFUSED"; throw e; }, pool: { max: 2 } });
968
+ try {
969
+ var eThrew = null;
970
+ try { await pE.acquire(); } catch (e) { eThrew = e; }
971
+ check("pool acquire surfaces connect error and decrements active",
972
+ eThrew && eThrew.code === "ECONNREFUSED" && pE.stats().active === 0);
973
+ } finally { await pE.drain(); }
974
+ }
975
+
976
+ // ---- Pool min floor (reaper honors the idle-client floor) ------------------
977
+
978
+ // Pool `min` is documented as a floor on idle clients: the reaper must
979
+ // retain at least `min` warm idle clients even when they have all gone
980
+ // idle past idleTimeoutMs, so a warm connection survives quiet periods.
981
+ async function testPoolMinFloor() {
982
+ var Pool = b.externalDb.Pool;
983
+
984
+ // reaper honors min=1: with two idle clients both expired, one warm
985
+ // client survives (the floor), and repeat reaps never drop below it.
986
+ var pF = new Pool("poolF", { connect: async function () { return { id: "f" }; }, close: async function () {},
987
+ pool: { min: 1, max: 3, idleTimeoutMs: 1 } });
988
+ try {
989
+ var f1 = await pF.acquire();
990
+ var f2 = await pF.acquire();
991
+ pF.release(f1);
992
+ pF.release(f2);
993
+ check("two clients idle before reap", pF.stats().idle === 2);
994
+ pF.idle.forEach(function (e) { e.lastUsedAt = Date.now() - 1000; }); // backdate all past idleTimeoutMs
995
+ pF._reapIdle();
996
+ check("reaper keeps min=1 idle client as the floor (never drops below min)", pF.stats().idle === 1);
997
+ pF.idle[0].lastUsedAt = Date.now() - 1000;
998
+ pF._reapIdle();
999
+ check("reaper holds the floor across repeat runs", pF.stats().idle === 1);
1000
+ } finally { await pF.drain(); }
1001
+
1002
+ // reaper keeps every idle client when all are within the floor
1003
+ var pH = new Pool("poolH", { connect: async function () { return { id: "h" }; }, close: async function () {},
1004
+ pool: { min: 4, max: 4, idleTimeoutMs: 1 } });
1005
+ try {
1006
+ var h1 = await pH.acquire();
1007
+ var h2 = await pH.acquire();
1008
+ pH.release(h1);
1009
+ pH.release(h2);
1010
+ pH.idle.forEach(function (e) { e.lastUsedAt = Date.now() - 1000; });
1011
+ pH._reapIdle();
1012
+ check("idle count at/below min is fully retained by reaper", pH.stats().idle === 2);
1013
+ } finally { await pH.drain(); }
1014
+
1015
+ // min=0 opts out of the floor — every expired idle client is reaped
1016
+ var pG = new Pool("poolG", { connect: async function () { return { id: "g" }; }, close: async function () {},
1017
+ pool: { min: 0, max: 2, idleTimeoutMs: 1 } });
1018
+ try {
1019
+ var g1 = await pG.acquire();
1020
+ pG.release(g1);
1021
+ pG.idle[0].lastUsedAt = Date.now() - 1000;
1022
+ pG._reapIdle();
1023
+ check("min=0 reaps every expired idle client", pG.stats().idle === 0);
1024
+ } finally { await pG.drain(); }
1025
+ }
1026
+
1027
+ // ---- config validation + error-path coverage ------------------------------
1028
+
1029
+ // Additional init-config validation (applicationName / requireTls), the
1030
+ // transaction residency + 42501 failure paths, sessionGucs empty-name guard,
1031
+ // the residency gate's trailing-comment scan, a replica non-connection read
1032
+ // error with fallback, a connectAs SET failure surfaced from connect, and the
1033
+ // assertRoleHardening unrecognized / throw-mode branches.
1034
+ async function testMoreConfigAndPaths() {
1035
+ var no = async function () {};
1036
+
1037
+ // ---- includeSqlInAudit stamps db.statement on the OTel audit metadata ----
1038
+ b.externalDb._resetForTest();
1039
+ var incD = mkDriver("inc");
1040
+ b.externalDb.init({ backends: { main: { dialect: "postgres", connect: incD.connect, query: incD.query, close: incD.close } } });
1041
+ var incRes = await b.externalDb.query("SELECT id FROM t", [], { includeSqlInAudit: true });
1042
+ check("query with includeSqlInAudit:true succeeds (db.statement audit path)", incRes.rows[0].src === "inc");
1043
+
1044
+ // ---- applicationName validation + postgres SET-application_name wiring ----
1045
+ expectInitThrow("init applicationName with newline → INVALID_CONFIG",
1046
+ { backends: { main: { connect: no, query: no, applicationName: "bad\nname" } } }, "INVALID_CONFIG");
1047
+ expectInitThrow("init applicationName over 63 bytes → INVALID_CONFIG",
1048
+ { backends: { main: { connect: no, query: no, applicationName: "x".repeat(120) } } }, "INVALID_CONFIG");
1049
+
1050
+ // A valid applicationName on a postgres backend makes the connect wrapper
1051
+ // issue SET application_name (single-quote escaped) on every fresh client.
1052
+ b.externalDb._resetForTest();
1053
+ var anD = mkDriver("an");
1054
+ b.externalDb.init({ backends: { main: {
1055
+ dialect: "postgres", connect: anD.connect, query: anD.query, close: anD.close,
1056
+ applicationName: "app's-name",
1057
+ } } });
1058
+ await b.externalDb.query("SELECT id FROM t");
1059
+ check("postgres applicationName issues escaped SET application_name",
1060
+ _saw(anD, /^SET application_name TO 'app''s-name'$/));
1061
+
1062
+ // A driver that refuses SET application_name is tolerated best-effort — the
1063
+ // connection is kept and the operator's real query still runs.
1064
+ b.externalDb._resetForTest();
1065
+ var anD2 = mkDriver("an2", { failOn: { re: /^SET application_name/, code: "0A000", times: 99 } });
1066
+ b.externalDb.init({ backends: { main: {
1067
+ dialect: "postgres", connect: anD2.connect, query: anD2.query, close: anD2.close,
1068
+ applicationName: "app2",
1069
+ } } });
1070
+ var anRes = await b.externalDb.query("SELECT id FROM t");
1071
+ check("SET application_name failure is best-effort (query still runs)", anRes.rows[0].src === "an2");
1072
+
1073
+ // ---- requireTls posture gate (init-time) ----
1074
+ expectInitThrow("requireTls non-boolean → INVALID_CONFIG",
1075
+ { backends: { main: { connect: no, query: no, requireTls: "yes" } } }, "INVALID_CONFIG");
1076
+ expectInitThrow("requireTls true, no TLS declared → TLS_REQUIRED",
1077
+ { backends: { main: { connect: no, query: no, requireTls: true } } }, "TLS_REQUIRED");
1078
+ expectInitThrow("requireTls true, sslmode 'prefer' (plaintext fallback) → TLS_REQUIRED",
1079
+ { backends: { main: { connect: no, query: no, requireTls: true, sslmode: "prefer" } } }, "TLS_REQUIRED");
1080
+ expectInitThrow("requireTls true, tls:false → TLS_REQUIRED",
1081
+ { backends: { main: { connect: no, query: no, requireTls: true, tls: false } } }, "TLS_REQUIRED");
1082
+ b.externalDb._resetForTest();
1083
+ var tlsOk = true;
1084
+ try {
1085
+ b.externalDb.init({ backends: {
1086
+ a: { connect: no, query: no, requireTls: true, tls: true },
1087
+ b2: { connect: no, query: no, requireTls: true, ssl: { rejectUnauthorized: true } },
1088
+ c: { connect: no, query: no, requireTls: true, sslmode: "verify-full" },
1089
+ } });
1090
+ } catch (_e) { tlsOk = false; }
1091
+ check("requireTls satisfied by tls:true / ssl object / sslmode verify-full", tlsOk);
1092
+ b.externalDb._resetForTest();
1093
+
1094
+ // ---- transaction: per-call residency refusal rolls back; body 42501 ----
1095
+ b.externalDb._resetForTest();
1096
+ var txR = mkDriver("txr");
1097
+ b.externalDb.init({ backends: { main: { connect: txR.connect, query: txR.query, close: txR.close, residencyTag: "EU" } } });
1098
+ await _underGdpr(async function () {
1099
+ await expectThrow("tx per-call mismatched residency tag → RESIDENCY_TAG_MISMATCH (rolls back)",
1100
+ function () {
1101
+ return b.externalDb.transaction(async function (tx) {
1102
+ await tx.query("INSERT INTO t (a) VALUES (1)", [], { rowResidencyTag: "US" });
1103
+ }, { rowResidencyTag: "EU" });
1104
+ }, "RESIDENCY_TAG_MISMATCH");
1105
+ });
1106
+ check("tx residency refusal issued ROLLBACK, never COMMIT",
1107
+ _saw(txR, /^ROLLBACK\b/) && txR.seen.every(function (s) { return !/^COMMIT\b/i.test(s); }));
1108
+
1109
+ b.externalDb._resetForTest();
1110
+ var txDenied = mkDriver("txden");
1111
+ b.externalDb.init({ backends: { main: { connect: txDenied.connect, query: txDenied.query, close: txDenied.close } } });
1112
+ await expectThrow("transaction body 42501 rethrows after rollback",
1113
+ function () {
1114
+ return b.externalDb.transaction(async function () { var e = new Error("denied"); e.code = "42501"; throw e; });
1115
+ }, "42501");
1116
+ check("transaction 42501 issued ROLLBACK", _saw(txDenied, /^ROLLBACK\b/));
1117
+
1118
+ // ---- sessionGucs empty-string GUC name → INVALID_SESSION_GUCS ----
1119
+ b.externalDb._resetForTest();
1120
+ var gD = mkDriver("g");
1121
+ b.externalDb.init({ backends: { main: { connect: gD.connect, query: gD.query, close: gD.close } } });
1122
+ await expectThrow("sessionGucs empty-string name → INVALID_SESSION_GUCS",
1123
+ function () { return b.externalDb.transaction(async function () {}, { sessionGucs: { "": "v" } }); },
1124
+ "INVALID_SESSION_GUCS");
1125
+
1126
+ // ---- residency gate: a trailing comment after ; is not a second statement ----
1127
+ b.externalDb._resetForTest();
1128
+ var tcD = mkDriver("tc");
1129
+ b.externalDb.init({ backends: { main: { connect: tcD.connect, query: tcD.query, close: tcD.close, residencyTag: "EU" } } });
1130
+ await _underGdpr(async function () {
1131
+ await b.externalDb.query("INSERT INTO t (a) VALUES (1); -- to-EOF note", [], { rowResidencyTag: "EU" });
1132
+ await b.externalDb.query("INSERT INTO t (a) VALUES (2); -- newline note\n", [], { rowResidencyTag: "EU" });
1133
+ await b.externalDb.query("INSERT INTO t (a) VALUES (3); /* closed block */", [], { rowResidencyTag: "EU" });
1134
+ await b.externalDb.query("INSERT INTO t (a) VALUES (4); /* unterminated block", [], { rowResidencyTag: "EU" });
1135
+ });
1136
+ check("trailing line/block comments after ; are not second statements (all 4 reached the wire)",
1137
+ tcD.seen.filter(function (s) { return /INSERT INTO t/.test(s); }).length === 4);
1138
+
1139
+ // ---- replica non-connection (42501) read error → release + db.role.denied + fallback ----
1140
+ b.externalDb._resetForTest();
1141
+ var rpF = mkDriver("rpf");
1142
+ var rrF = mkDriver("rrf", { failOn: { re: /SELECT/i, code: "42501", times: 99 } });
1143
+ b.externalDb.init({ backends: { main: {
1144
+ connect: rpF.connect, query: rpF.query, close: rpF.close,
1145
+ replicas: [{ connect: rrF.connect, query: rrF.query, close: rrF.close }],
1146
+ replicaFallbackToPrimary: true,
1147
+ } } });
1148
+ var rfRes = await b.externalDb.read.query("SELECT id FROM t");
1149
+ check("replica non-connection (42501) read releases the client and falls back to primary",
1150
+ rfRes.rows[0].src === "rpf");
1151
+
1152
+ // ---- connectAs wrapper: a SET failing during connect surfaces from query ----
1153
+ b.externalDb._resetForTest();
1154
+ var caFail = mkDriver("cafail", { failOn: { re: /^SET ROLE/, code: "XX000", times: 99 } });
1155
+ var wrappedFail = b.externalDb.adapters.connectAs(caFail.connect, { query: caFail.query, role: "some_role" });
1156
+ b.externalDb.init({ backends: { main: { dialect: "postgres", connect: wrappedFail, query: caFail.query, close: caFail.close } } });
1157
+ await expectThrow("connectAs SET failure during connect surfaces from query",
1158
+ function () { return b.externalDb.query("SELECT id FROM t"); }, "XX000");
1159
+
1160
+ // ---- assertRoleHardening: non-array declaredRoles + unrecognized (audit + throw) ----
1161
+ b.externalDb._resetForTest();
1162
+ var arU = mkDriver("aru", { roles: ["app_user", "leftover_role"] });
1163
+ b.externalDb.init({ backends: { main: { connect: arU.connect, query: arU.query, close: arU.close } } });
1164
+ await expectThrow("assertRoleHardening non-array declaredRoles → INVALID_CONFIG",
1165
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: "nope" }); }, "INVALID_CONFIG");
1166
+ var arAudit = await b.externalDb.assertRoleHardening({ declaredRoles: ["app_user"], mode: "audit" });
1167
+ check("assertRoleHardening audit mode surfaces the unrecognized role",
1168
+ arAudit.unrecognized.length === 1 && arAudit.unrecognized[0] === "leftover_role");
1169
+ await expectThrow("assertRoleHardening throw mode raises ROLE_HARDENING_FAIL",
1170
+ function () { return b.externalDb.assertRoleHardening({ declaredRoles: ["app_user"], mode: "throw" }); },
1171
+ "ROLE_HARDENING_FAIL");
1172
+
1173
+ b.externalDb._resetForTest();
1174
+ b.compliance.clear();
1175
+ }
1176
+
1177
+ // ---- runner ----------------------------------------------------------------
1178
+
1179
+ async function run() {
1180
+ await testNotInitialized();
1181
+ testInitValidation();
1182
+ testInitIdempotent();
1183
+ await testPickBackend();
1184
+ await testDefaultBackendValidation();
1185
+ await testQueryPaths();
1186
+ await testTransactionValidation();
1187
+ await testTransactionExec();
1188
+ await testHealthCheck();
1189
+ await testShutdown();
1190
+ testConfigurePool();
1191
+ await testConnectAs();
1192
+ await testRunAs();
1193
+ await testAssertRoleHardening();
1194
+ testStatementHelpers();
1195
+ await testResidencyAdvisory();
1196
+ await testResidencyGate();
1197
+ await testReplicas();
1198
+ await testPoolInternals();
1199
+ await testPoolMinFloor();
1200
+ await testMoreConfigAndPaths();
1201
+
1202
+ // Leave the registry + compliance state clean for parallel smoke files.
1203
+ b.externalDb._resetForTest();
1204
+ b.compliance.clear();
1205
+ }
1206
+
1207
+ if (require.main === module) {
1208
+ run().then(
1209
+ function () { process.stdout.write("OK — external-db: " + helpers.getChecks() + " checks passed\n"); process.exit(0); },
1210
+ function (err) { console.error(err && err.stack); process.exit(1); }
1211
+ );
1212
+ }
1213
+
1214
+ module.exports = { run: run };