@blamejs/blamejs-shop 0.4.31 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/vendor/MANIFEST.json +392 -278
  4. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  5. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  6. package/lib/vendor/blamejs/.gitignore +6 -0
  7. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  8. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  9. package/lib/vendor/blamejs/README.md +8 -6
  10. package/lib/vendor/blamejs/SECURITY.md +19 -3
  11. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  12. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  13. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  14. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  15. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  16. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  17. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  18. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  19. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  20. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  23. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  24. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  25. package/lib/vendor/blamejs/index.js +4 -0
  26. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  27. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  28. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  29. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  30. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  31. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  32. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  33. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  34. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  35. package/lib/vendor/blamejs/lib/audit.js +259 -123
  36. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  37. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  38. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  39. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  40. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  41. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  42. package/lib/vendor/blamejs/lib/cache.js +174 -105
  43. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  44. package/lib/vendor/blamejs/lib/cli.js +19 -14
  45. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  46. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  47. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  48. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  49. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  50. package/lib/vendor/blamejs/lib/consent.js +82 -29
  51. package/lib/vendor/blamejs/lib/constants.js +27 -11
  52. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  53. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  54. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  55. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  56. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  57. package/lib/vendor/blamejs/lib/db.js +249 -99
  58. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  59. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  60. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  61. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  62. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  63. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  64. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  65. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  66. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  67. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  68. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  69. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  70. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  71. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  72. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  73. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  74. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  75. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  76. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  77. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  78. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  79. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  80. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  81. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  82. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  83. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  84. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  85. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  86. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  87. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  88. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  89. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  90. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  91. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  92. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  93. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  95. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  96. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  97. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  98. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  99. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  100. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  101. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  102. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  103. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  104. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  107. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  109. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  110. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  111. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  112. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  113. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  114. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  115. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  116. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  117. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  118. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  119. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  120. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  121. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  122. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  123. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  125. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  132. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  133. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  135. package/lib/vendor/blamejs/lib/mail.js +8 -4
  136. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  137. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  138. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  139. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  141. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  142. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  143. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  144. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  145. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  146. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  147. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  148. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  149. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  150. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  151. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  152. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  153. package/lib/vendor/blamejs/lib/observability.js +124 -0
  154. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  155. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  156. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  157. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  158. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  159. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  160. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  161. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  162. package/lib/vendor/blamejs/lib/queue.js +7 -0
  163. package/lib/vendor/blamejs/lib/redact.js +68 -11
  164. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  165. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  166. package/lib/vendor/blamejs/lib/retention.js +101 -40
  167. package/lib/vendor/blamejs/lib/router.js +212 -5
  168. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  169. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  170. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  171. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  172. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  173. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  174. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  175. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  176. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  177. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  178. package/lib/vendor/blamejs/lib/session.js +175 -77
  179. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  180. package/lib/vendor/blamejs/lib/sse.js +26 -0
  181. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  182. package/lib/vendor/blamejs/lib/static.js +177 -34
  183. package/lib/vendor/blamejs/lib/subject.js +96 -49
  184. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  185. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  186. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  187. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  188. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  189. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  190. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  191. package/lib/vendor/blamejs/package.json +2 -2
  192. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  193. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  194. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  200. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  201. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  202. package/lib/vendor/blamejs/scripts/release.js +398 -38
  203. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  204. package/lib/vendor/blamejs/test/10-state.js +140 -14
  205. package/lib/vendor/blamejs/test/20-db.js +65 -2
  206. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  207. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  208. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  209. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  210. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  211. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  212. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  213. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  214. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  215. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  220. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  221. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  222. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  223. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  224. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  225. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  226. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  227. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  228. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  229. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  230. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  231. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  232. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  233. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  234. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  235. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  236. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  237. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  238. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  248. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  258. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  270. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  271. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  272. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  273. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  277. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  278. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  291. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  302. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  308. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  310. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  311. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  312. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  313. package/lib/vendor/blamejs/test/smoke.js +79 -21
  314. package/package.json +1 -1
  315. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  316. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  317. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  318. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  319. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  320. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  321. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  322. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  323. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  324. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  325. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  326. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  327. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  328. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  329. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  330. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  331. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  332. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  334. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  335. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  336. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,610 @@
1
+ "use strict";
2
+ /**
3
+ * Live data-layer test against the docker MySQL container. Proves the
4
+ * dialect-threading foundation end-to-end on a real MySQL server: the
5
+ * canonical resolver `b.clusterStorage.dialect()` returns `"mysql"` in
6
+ * cluster mode, and every cluster-routed data-layer backend that threads
7
+ * that dialect into its `b.sql` calls runs against real MySQL with
8
+ * backtick-quoted identifiers and the MySQL `ON DUPLICATE KEY UPDATE`
9
+ * upsert form (not Postgres `ON CONFLICT` / double-quotes):
10
+ *
11
+ * - b.cache (backend: "cluster") — _blamejs_cache + _blamejs_cache_tags
12
+ * - b.session — _blamejs_sessions, sealed at rest
13
+ * - b.nonceStore (backend: "cluster")— _blamejs_api_encrypt_nonces
14
+ * - b.middleware.rateLimit (cluster) — _blamejs_rate_limit_counters
15
+ *
16
+ * The "driver" is a docker-exec mysql shim (batch mode, one statement per
17
+ * exec) modelled on cluster-provider-mysql.test.js. Each primitive routes
18
+ * its SQL through b.clusterStorage -> b.externalDb -> this driver -> real
19
+ * MySQL, so a dialect bug in the emitted SQL surfaces as a real MySQL ERROR,
20
+ * not a test artifact. size() / count() are asserted to return a JS number
21
+ * (the COUNT(*)/BIGINT-as-string coercion) on the real backend.
22
+ *
23
+ * The rate-limit take() is the headline: its per-column window-rollover
24
+ * conflict action is a CASE that reads the proposed row (VALUES(`col`)) and
25
+ * the existing row (`table`.`col`) — both spelled with the MySQL tokens, so
26
+ * the Postgres EXCLUDED form would be a hard ERROR here. b.sql also folds the
27
+ * RETURNING into a readback SELECT (MySQL upsert has no RETURNING); the
28
+ * backend runs that readback and coerces the BIGINT count back to a number.
29
+ *
30
+ * Cluster mode is wired via a no-op custom provider plus externalDbBackend,
31
+ * so isClusterMode() is true and each backend's clusterStorage.execute()
32
+ * calls dispatch to MySQL. The framework tables are created here by hand
33
+ * with the exact column shape frameworkSchema's MySQL DDL builder emits
34
+ * (camelCase, backtick-quoted, VARCHAR key columns, BIGINT ms-epoch);
35
+ * frameworkSchema.ensureSchema's own MySQL DDL is proven directly in
36
+ * framework-schema-mysql.test.js. b.session seals userId/data via the
37
+ * cryptoField registry that helpers.setupTestDb populates.
38
+ *
39
+ * RUN: node scripts/test-integration.js --skip-service-check data-layer-mysql
40
+ */
41
+ var execFileSync = require("node:child_process").execFileSync;
42
+ var fs = require("node:fs");
43
+ var os = require("node:os");
44
+ var path = require("node:path");
45
+ var helpers = require("../helpers");
46
+ var check = helpers.check;
47
+ var services = require("../helpers/services");
48
+ var b = require("../../");
49
+
50
+ var CONTAINER = "blamejs-test-mysql";
51
+ var DB_NAME = "blamejs_test";
52
+
53
+ var rateLimitModule = require("../../lib/middleware/rate-limit");
54
+
55
+ // Soft assertion: records a pass/fail without throwing, so every section's
56
+ // finding is collected even when an earlier one failed. The recorded
57
+ // results are replayed through the hard `check` at the end of run() so the
58
+ // file still FAILS (and the runner reports it) when any assertion is false,
59
+ // while ALL findings are printed first.
60
+ var _findings = [];
61
+ function softCheck(label, condition) {
62
+ _findings.push({ label: label, ok: !!condition });
63
+ console.log((condition ? " ok " : " FAIL ") + label);
64
+ }
65
+
66
+ // ---- docker-exec mysql driver ----
67
+ //
68
+ // Batch mode (--batch --raw) emits TAB-separated rows with a header line;
69
+ // NULL renders as the literal "NULL". One statement per exec (the driver
70
+ // is stateless between calls, fine for the cluster-storage dispatch
71
+ // shape). Affected-row count for a DML statement is recovered with a
72
+ // trailing `SELECT ROW_COUNT()` in the SAME exec so the same connection's
73
+ // session state is read — matching what a real mysql2 driver surfaces as
74
+ // rowCount.
75
+ function _execMysql(sqlText) {
76
+ var out;
77
+ try {
78
+ out = execFileSync("docker",
79
+ ["exec", "-i", CONTAINER,
80
+ "mysql", "-uroot", "-pblamejs_test_root", "--batch", "--raw", DB_NAME, "-e", sqlText],
81
+ { stdio: ["pipe", "pipe", "pipe"], maxBuffer: 16 * 1024 * 1024 }
82
+ ).toString("utf8");
83
+ } catch (e) {
84
+ var err = new Error(e.stderr ? e.stderr.toString("utf8") : (e.message || String(e)));
85
+ err.cause = e;
86
+ throw err;
87
+ }
88
+ return out;
89
+ }
90
+
91
+ function _parseBatch(out) {
92
+ var lines = out.split(/\r?\n/).filter(function (l) { return l.length > 0; });
93
+ if (lines.length < 1) return { rows: [], headers: [] };
94
+ var headers = lines[0].split("\t");
95
+ var rows = [];
96
+ for (var i = 1; i < lines.length; i++) {
97
+ var cells = lines[i].split("\t");
98
+ var row = {};
99
+ for (var j = 0; j < headers.length; j++) {
100
+ var v = cells[j];
101
+ row[headers[j]] = (v === "NULL" || v === undefined) ? null : v;
102
+ }
103
+ rows.push(row);
104
+ }
105
+ return { rows: rows, headers: headers };
106
+ }
107
+
108
+ // Substitute `?` placeholders with quoted literals. Every test value is
109
+ // operator-controlled (keys / numbers / JSON strings / null). MySQL is fed
110
+ // `?` (clusterStorage.placeholderize is passthrough for mysql), so this is
111
+ // the shim's bind protocol.
112
+ function _bindParams(sqlText, params) {
113
+ var i = 0;
114
+ return sqlText.replace(/\?/g, function () {
115
+ if (i >= params.length) throw new Error("placeholder/param count mismatch");
116
+ var p = params[i++];
117
+ if (p === null || p === undefined) return "NULL";
118
+ if (typeof p === "number") return String(p);
119
+ if (typeof p === "boolean") return p ? "1" : "0";
120
+ if (Buffer.isBuffer(p)) return "x'" + p.toString("hex") + "'";
121
+ return "'" + String(p).replace(/\\/g, "\\\\").replace(/'/g, "''") + "'";
122
+ });
123
+ }
124
+
125
+ function _makeDockerMysqlDriver() {
126
+ return {
127
+ connect: async function () { return { id: 1 }; },
128
+ query: async function (_client, sqlText, params) {
129
+ params = params || [];
130
+ var bound = _bindParams(sqlText, params);
131
+ var t = bound.trim();
132
+ if (/^(SELECT|SHOW|WITH)/i.test(t)) {
133
+ var sel = _parseBatch(_execMysql(bound));
134
+ return { rows: sel.rows, rowCount: sel.rows.length };
135
+ }
136
+ // DML / DDL: run it, then read ROW_COUNT() on the SAME connection
137
+ // (one exec) so the affected-row count is faithful.
138
+ var combined = _execMysql(bound + ";\nSELECT ROW_COUNT() AS `__rc`;");
139
+ var rc = _parseBatch(combined);
140
+ var n = (rc.rows[0] && rc.rows[0].__rc != null) ? Number(rc.rows[0].__rc) : 0;
141
+ // MySQL ROW_COUNT() returns -1 for statements that don't affect rows
142
+ // (e.g. CREATE). Clamp to 0 for those.
143
+ if (!Number.isFinite(n) || n < 0) n = 0;
144
+ return { rows: [], rowCount: n };
145
+ },
146
+ close: async function () { /* no-op */ },
147
+ };
148
+ }
149
+
150
+ // No-op cluster provider — we only need isClusterMode() true + a dialect.
151
+ // The framework's leader gate is not under test here (cache/nonce/
152
+ // rate-limit cluster backends don't gate on isLeader()).
153
+ function _noopProvider() {
154
+ return {
155
+ ensureSchema: async function () { /* tables created by hand below */ },
156
+ acquireLease: async function () { return { nodeId: "n", leaseId: "l", fencingToken: 1, expiresAt: Date.now() + 60000 }; },
157
+ renewLease: async function (lease) { return lease; },
158
+ releaseLease: async function () {},
159
+ currentLeader: async function () { return null; },
160
+ };
161
+ }
162
+
163
+ // ---- framework table DDL for MySQL (matches framework-schema._*DDL
164
+ // column shapes; ensureSchema is Postgres/SQLite-only so MySQL is by
165
+ // hand). Identifiers backtick-quoted, camelCase preserved. ----
166
+ var MYSQL_DDL = [
167
+ // _blamejs_cache. VARCHAR lengths kept index-safe under utf8mb4
168
+ // (4 bytes/char) so the PRIMARY KEY / composite key stay under MySQL's
169
+ // 3072-byte index limit.
170
+ "DROP TABLE IF EXISTS `_blamejs_cache`",
171
+ "CREATE TABLE `_blamejs_cache` (" +
172
+ "`cacheKey` VARCHAR(191) PRIMARY KEY, " +
173
+ "`valueJson` LONGTEXT NOT NULL, " +
174
+ "`expiresAt` BIGINT NOT NULL, " +
175
+ "`updatedAt` BIGINT NOT NULL)",
176
+ // _blamejs_cache_tags
177
+ "DROP TABLE IF EXISTS `_blamejs_cache_tags`",
178
+ "CREATE TABLE `_blamejs_cache_tags` (" +
179
+ "`cacheKey` VARCHAR(191) NOT NULL, " +
180
+ "`tag` VARCHAR(191) NOT NULL, " +
181
+ "PRIMARY KEY (`cacheKey`, `tag`))",
182
+ // Minimal audit/consent tip tables so cluster.init's boot-time rollback
183
+ // check finds "no tip row" and skips cleanly (it reads these).
184
+ "DROP TABLE IF EXISTS `_blamejs_audit_tip`",
185
+ "CREATE TABLE `_blamejs_audit_tip` (" +
186
+ "`scope` VARCHAR(64) PRIMARY KEY, " +
187
+ "`atMonotonicCounter` BIGINT NOT NULL DEFAULT 0, " +
188
+ "`rowHash` TEXT)",
189
+ "DROP TABLE IF EXISTS `_blamejs_consent_tip`",
190
+ "CREATE TABLE `_blamejs_consent_tip` (" +
191
+ "`scope` VARCHAR(64) PRIMARY KEY, " +
192
+ "`atMonotonicCounter` BIGINT NOT NULL DEFAULT 0, " +
193
+ "`rowHash` TEXT)",
194
+ "DROP TABLE IF EXISTS `_blamejs_audit_log`",
195
+ "CREATE TABLE `_blamejs_audit_log` (" +
196
+ "`monotonicCounter` BIGINT, `rowHash` TEXT)",
197
+ "DROP TABLE IF EXISTS `_blamejs_consent_log`",
198
+ "CREATE TABLE `_blamejs_consent_log` (" +
199
+ "`monotonicCounter` BIGINT, `rowHash` TEXT)",
200
+ // _blamejs_sessions — mirrors framework-schema._sessionsDDL("mysql"):
201
+ // VARCHAR key columns (index-safe under utf8mb4), BIGINT ms-epoch
202
+ // timestamps. userId/data hold the vault-sealed ciphertext at rest.
203
+ "DROP TABLE IF EXISTS `_blamejs_sessions`",
204
+ "CREATE TABLE `_blamejs_sessions` (" +
205
+ "`sidHash` VARCHAR(191) PRIMARY KEY, " +
206
+ "`userId` TEXT NOT NULL, " +
207
+ "`userIdHash` VARCHAR(191) NOT NULL, " +
208
+ "`data` TEXT, " +
209
+ "`createdAt` BIGINT NOT NULL, " +
210
+ "`expiresAt` BIGINT NOT NULL, " +
211
+ "`lastActivity` BIGINT NOT NULL)",
212
+ // _blamejs_api_encrypt_nonces — replay-protection store. nonceHash is the
213
+ // PRIMARY KEY, so the ON DUPLICATE KEY UPDATE no-op fold makes the first
214
+ // insert win (affectedRows=1) and a replay no-op (affectedRows=0).
215
+ "DROP TABLE IF EXISTS `_blamejs_api_encrypt_nonces`",
216
+ "CREATE TABLE `_blamejs_api_encrypt_nonces` (" +
217
+ "`nonceHash` VARCHAR(191) PRIMARY KEY, " +
218
+ "`expireAt` BIGINT NOT NULL)",
219
+ // _blamejs_rate_limit_counters — fixed-window counter. The PRIMARY KEY on
220
+ // `key` is what take()'s ON DUPLICATE KEY UPDATE CASE-rollover keys on.
221
+ "DROP TABLE IF EXISTS `_blamejs_rate_limit_counters`",
222
+ "CREATE TABLE `_blamejs_rate_limit_counters` (" +
223
+ "`key` VARCHAR(191) PRIMARY KEY, " +
224
+ "`windowStart` BIGINT NOT NULL, " +
225
+ "`count` BIGINT NOT NULL DEFAULT 0)",
226
+ // _blamejs_cluster_state — cluster.init's _checkVaultKeyConsistency records
227
+ // + reads this node's vault-key fingerprint here (the MySQL `ON DUPLICATE
228
+ // KEY UPDATE scope = scope` DO-NOTHING fold). The no-op provider doesn't
229
+ // create it, so it is created by hand; rotationEpoch is materialized up
230
+ // front so the runtime's idempotent ALTER is a no-op. Mirrors
231
+ // cluster-provider-db's _blamejs_cluster_state column shape.
232
+ "DROP TABLE IF EXISTS `_blamejs_cluster_state`",
233
+ "CREATE TABLE `_blamejs_cluster_state` (" +
234
+ "`scope` VARCHAR(64) PRIMARY KEY, " +
235
+ "`vaultKeyFp` TEXT NOT NULL, " +
236
+ "`recordedAt` BIGINT NOT NULL, " +
237
+ "`recordedByNode` TEXT NOT NULL, " +
238
+ "`rotationEpoch` BIGINT NOT NULL DEFAULT 0)",
239
+ ];
240
+
241
+ var DROP_ALL = [
242
+ "_blamejs_cache", "_blamejs_cache_tags", "_blamejs_audit_tip",
243
+ "_blamejs_consent_tip", "_blamejs_audit_log", "_blamejs_consent_log",
244
+ "_blamejs_sessions", "_blamejs_api_encrypt_nonces",
245
+ "_blamejs_rate_limit_counters", "_blamejs_cluster_state",
246
+ ].map(function (t) { return "DROP TABLE IF EXISTS `" + t + "`"; });
247
+
248
+ function _ddl(stmts) {
249
+ for (var i = 0; i < stmts.length; i++) _execMysql(stmts[i]);
250
+ }
251
+
252
+ // Out-of-band assertion helper: run a SELECT directly and return parsed rows.
253
+ function _selectDirect(sqlText) {
254
+ return _parseBatch(_execMysql(sqlText)).rows;
255
+ }
256
+
257
+ async function run() {
258
+ var mysqlSvc = await services.requireService("mysql");
259
+ if (!mysqlSvc.ok) throw new Error("mysql unreachable: " + mysqlSvc.reason);
260
+
261
+ _ddl(MYSQL_DDL);
262
+
263
+ // Full-framework bring-up (vault + db + cryptoField). db.init registers the
264
+ // _blamejs_sessions sealedFields (userId, data) + derived userIdHash, which
265
+ // b.session uses to seal/derive regardless of which backend the SQL routes
266
+ // to. The local SQLite db it opens is unused once cluster mode is active
267
+ // (session SQL dispatches to MySQL) but the cryptoField registry it
268
+ // populates is exactly what b.session's sealing needs.
269
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-datalayer-mysql-"));
270
+ await helpers.setupTestDb(tmpDir);
271
+
272
+ var driver = _makeDockerMysqlDriver();
273
+ b.cluster._resetForTest();
274
+ b.externalDb._resetForTest();
275
+ b.externalDb.init({
276
+ backends: {
277
+ ops: {
278
+ connect: driver.connect, query: driver.query, close: driver.close,
279
+ dialect: "mysql",
280
+ },
281
+ },
282
+ });
283
+
284
+ await b.cluster.init({
285
+ nodeId: "mysql-data-node",
286
+ role: "leader",
287
+ leaseTtl: b.constants.TIME.seconds(30),
288
+ heartbeatInterval: b.constants.TIME.seconds(10),
289
+ provider: _noopProvider(),
290
+ externalDbBackend: "ops",
291
+ dialect: "mysql",
292
+ });
293
+ softCheck("cluster is in cluster mode (state routes to MySQL)",
294
+ b.cluster.isClusterMode() === true);
295
+ softCheck("cluster dialect is mysql", b.cluster.dialect() === "mysql");
296
+
297
+ // Each backend threads clusterStorage.dialect() into every b.sql call (via
298
+ // its per-file _*SqlOpts()), so its SQL emits MySQL-shaped identifiers + the
299
+ // ON DUPLICATE KEY UPDATE upsert against the real server. A dialect bug
300
+ // surfaces as a real MySQL ERROR rather than a test artifact.
301
+ try {
302
+ await _section("cache", _testCacheCluster);
303
+ await _section("session", _testSession);
304
+ await _section("nonce", _testNonceCluster);
305
+ await _section("rate-limit", _testRateLimitCluster);
306
+ } finally {
307
+ try { await b.cluster.shutdown(); } catch (_e) {}
308
+ b.cluster._resetForTest();
309
+ try { await b.externalDb.shutdown(); } catch (_e) {}
310
+ try { await helpers.teardownTestDb(tmpDir); } catch (_e) {}
311
+ _ddl(DROP_ALL);
312
+ }
313
+
314
+ // Replay every recorded finding through the hard `check` so the file
315
+ // FAILS (and the runner reports it) when any assertion is false. All
316
+ // findings have already been printed above, so the failure message
317
+ // names the first unmet contract while the full picture is visible.
318
+ var failures = _findings.filter(function (f) { return !f.ok; });
319
+ console.log("");
320
+ console.log("[data-layer-mysql] " + (_findings.length - failures.length) + "/" +
321
+ _findings.length + " checks ok; " + failures.length + " failing");
322
+ for (var i = 0; i < _findings.length; i++) {
323
+ check(_findings[i].label, _findings[i].ok);
324
+ }
325
+ }
326
+
327
+ // Run one primitive's section; a thrown driver error becomes a single
328
+ // FAILED check carrying the first line of the MySQL error so each
329
+ // primitive's dialect bug is reported independently.
330
+ async function _section(label, fn) {
331
+ try {
332
+ await fn();
333
+ } catch (e) {
334
+ var first = ((e && e.message) || String(e)).split(/\r?\n/)
335
+ .filter(function (l) { return /ERROR \d+|error in your SQL|EXCLUDED|"/.test(l); })[0] ||
336
+ ((e && e.message) || String(e)).split(/\r?\n/)[0];
337
+ softCheck(label + "(mysql): cluster SQL executes against real MySQL " +
338
+ "(emitted with the MySQL dialect — backtick identifiers, no Postgres EXCLUDED) " +
339
+ "— DETAIL: " + first, false);
340
+ }
341
+ }
342
+
343
+ // ======================================================================
344
+ // b.cache cluster backend on real MySQL.
345
+ // get / set (tx upsert + tag rewrite) / del / has / size / clear /
346
+ // invalidateTag (whereLike prefix) / getTags / CAS update.
347
+ // ======================================================================
348
+ async function _testCacheCluster() {
349
+ var cache = b.cache.create({ backend: "cluster", namespace: "mysqlns", ttlMs: b.constants.TIME.minutes(5) });
350
+
351
+ // set + get round-trip through the real _blamejs_cache UPSERT.
352
+ await cache.set("k1", { hello: "world", n: 42 });
353
+ var got = await cache.get("k1");
354
+ softCheck("cache(mysql): set + get JSON round-trips on real MySQL",
355
+ got && got.hello === "world" && got.n === 42);
356
+
357
+ // The row physically lands keyed on "<namespace>:<key>".
358
+ var rowDirect = _selectDirect(
359
+ "SELECT `valueJson` FROM `_blamejs_cache` WHERE `cacheKey` = 'mysqlns:k1';");
360
+ softCheck("cache(mysql): row physically present under composite key",
361
+ rowDirect.length === 1 && /"hello"/.test(rowDirect[0].valueJson));
362
+
363
+ // has / del.
364
+ softCheck("cache(mysql): has returns true for a live key", (await cache.has("k1")) === true);
365
+ softCheck("cache(mysql): del removes the key", (await cache.del("k1")) === true);
366
+ softCheck("cache(mysql): get after del is undefined", (await cache.get("k1")) === undefined);
367
+
368
+ // tags: set with tags -> tag rows written in the same transaction.
369
+ await cache.set("a", "1", { tags: ["grp-x"] });
370
+ await cache.set("bk", "2", { tags: ["grp-x", "grp-y"] });
371
+ await cache.set("ck", "3", { tags: ["grp-y"] });
372
+ var tagsA = await cache.getTags("a");
373
+ softCheck("cache(mysql): getTags returns the tags written for a key",
374
+ Array.isArray(tagsA) && tagsA.indexOf("grp-x") !== -1);
375
+
376
+ // invalidateTag uses whereLike(cacheKey, prefix) + tag match. Drops every
377
+ // key carrying grp-x, scoped to this namespace; leaves grp-y-only keys.
378
+ var purged = await cache.invalidateTag("grp-x");
379
+ softCheck("cache(mysql): invalidateTag purged the grp-x keys (count >= 2)", purged >= 2);
380
+ softCheck("cache(mysql): invalidateTag dropped a + bk",
381
+ (await cache.get("a")) === undefined && (await cache.get("bk")) === undefined);
382
+ softCheck("cache(mysql): invalidateTag preserved grp-y-only key ck",
383
+ (await cache.get("ck")) === "3");
384
+
385
+ // Tag junction rows for the purged keys are gone (no orphans).
386
+ var orphanTags = _selectDirect(
387
+ "SELECT COUNT(*) AS `n` FROM `_blamejs_cache_tags` WHERE `cacheKey` = 'mysqlns:a';");
388
+ softCheck("cache(mysql): purged key's tag junction rows removed",
389
+ orphanTags.length === 1 && Number(orphanTags[0].n) === 0);
390
+
391
+ // size() counts only live, namespace-scoped rows (whereLike prefix).
392
+ var sz = await cache.size();
393
+ softCheck("cache(mysql): size() counts live namespaced rows (ck remains)", sz >= 1);
394
+
395
+ // CAS update (atomic RMW) — counter increment via the transaction +
396
+ // compare-and-set path on real MySQL.
397
+ await cache.update("counter", function (n) { return { value: (n || 0) + 1 }; });
398
+ await cache.update("counter", function (n) { return { value: (n || 0) + 1 }; });
399
+ var counterVal = await cache.get("counter");
400
+ softCheck("cache(mysql): atomic update() increments through CAS on MySQL",
401
+ counterVal === 2);
402
+
403
+ // clear() — namespace-scoped wipe via whereLike prefix.
404
+ var cleared = await cache.clear();
405
+ softCheck("cache(mysql): clear() wiped the namespace", cleared >= 1);
406
+ softCheck("cache(mysql): get after clear is undefined", (await cache.get("ck")) === undefined);
407
+
408
+ // Cross-namespace isolation: a second instance's key is untouched by the
409
+ // first's clear()/invalidateTag (prefix LIKE scoping is real on MySQL).
410
+ var other = b.cache.create({ backend: "cluster", namespace: "othermysql", ttlMs: b.constants.TIME.minutes(5) });
411
+ await other.set("shared", "other-value", { tags: ["grp-x"] });
412
+ await cache.set("shared", "first-value", { tags: ["grp-x"] });
413
+ await cache.invalidateTag("grp-x");
414
+ softCheck("cache(mysql): invalidateTag is namespace-scoped (other ns survives)",
415
+ (await other.get("shared")) === "other-value");
416
+ await other.clear();
417
+ await other.close();
418
+ await cache.close();
419
+ }
420
+
421
+ // ======================================================================
422
+ // b.session full lifecycle on real MySQL (sealed at rest).
423
+ // create / verify / count / touch / rotate / destroyAllForUser /
424
+ // destroy / purgeExpired — every statement dialect-threaded to MySQL
425
+ // (backtick identifiers), userId/data sealed via cryptoField.
426
+ // ======================================================================
427
+ async function _testSession() {
428
+ var created = await b.session.create({
429
+ userId: "user-42",
430
+ data: { roles: ["admin"], theme: "dark" },
431
+ ttlMs: b.constants.TIME.hours(8),
432
+ });
433
+ softCheck("session(mysql): create returns a sealed token + expiry",
434
+ created && typeof created.token === "string" &&
435
+ created.token.indexOf("vault:") === 0 && typeof created.expiresAt === "number");
436
+
437
+ // The row physically landed on MySQL; userId is NOT plaintext.
438
+ var rawRows = _selectDirect("SELECT `userId`, `userIdHash` FROM `_blamejs_sessions`;");
439
+ softCheck("session(mysql): a session row physically landed on MySQL",
440
+ rawRows.length === 1);
441
+ softCheck("session(mysql): userId is sealed at rest (NOT the plaintext 'user-42')",
442
+ rawRows.length === 1 && String(rawRows[0].userId).indexOf("user-42") === -1);
443
+
444
+ // verify -> unseal round-trips userId + data through MySQL.
445
+ var info = await b.session.verify(created.token);
446
+ softCheck("session(mysql): verify unseals userId from the MySQL row",
447
+ info && info.userId === "user-42");
448
+ softCheck("session(mysql): verify unseals the data payload",
449
+ info && info.data && info.data.roles && info.data.roles[0] === "admin" &&
450
+ info.data.theme === "dark");
451
+ softCheck("session(mysql): verify coerces createdAt/expiresAt to JS numbers " +
452
+ "(BIGINT-as-string would break the timeout math)",
453
+ info && typeof info.createdAt === "number" && typeof info.expiresAt === "number" &&
454
+ info.expiresAt > info.createdAt);
455
+
456
+ // count -> the live session is counted (COUNT(*) BIGINT coerced to number).
457
+ var liveCount = await b.session.count();
458
+ softCheck("session(mysql): count() returns the one live session as a JS number",
459
+ typeof liveCount === "number" && liveCount === 1);
460
+
461
+ // touch with extendBy -> bumps lastActivity + expiresAt; affectedRows>0.
462
+ var touched = await b.session.touch(created.token, { extendBy: b.constants.TIME.hours(12) });
463
+ softCheck("session(mysql): touch() updated the live row (returned true)", touched === true);
464
+ var afterTouch = await b.session.verify(created.token);
465
+ softCheck("session(mysql): touch extended expiresAt past the original",
466
+ afterTouch && afterTouch.expiresAt >= created.expiresAt);
467
+
468
+ // rotate -> new sid swapped atomically; old token no longer verifies.
469
+ var rotated = await b.session.rotate(created.token, { reason: "mfa" });
470
+ softCheck("session(mysql): rotate returns a fresh sealed token",
471
+ rotated && typeof rotated.token === "string" && rotated.token !== created.token);
472
+ softCheck("session(mysql): the OLD token no longer verifies after rotate",
473
+ (await b.session.verify(created.token)) === null);
474
+ var rotatedInfo = await b.session.verify(rotated.token);
475
+ softCheck("session(mysql): the NEW token verifies with the same userId",
476
+ rotatedInfo && rotatedInfo.userId === "user-42");
477
+
478
+ // updateData -> writes the sealed data column without rotating the sid.
479
+ var updated = await b.session.updateData(rotated.token, { roles: ["admin"], step: "mfa-done" });
480
+ softCheck("session(mysql): updateData wrote the sealed data column (returned true)",
481
+ updated === true);
482
+ var afterUpdate = await b.session.verify(rotated.token);
483
+ softCheck("session(mysql): updateData payload round-trips through the sealed MySQL column",
484
+ afterUpdate && afterUpdate.data && afterUpdate.data.step === "mfa-done");
485
+
486
+ // destroyAllForUser -> deletes via the derived userIdHash; count drops.
487
+ var revoked = await b.session.destroyAllForUser("user-42");
488
+ softCheck("session(mysql): destroyAllForUser deleted the session via userIdHash",
489
+ revoked === 1);
490
+ softCheck("session(mysql): the session no longer verifies after revoke-all",
491
+ (await b.session.verify(rotated.token)) === null);
492
+ softCheck("session(mysql): count() is 0 after revoke-all", (await b.session.count()) === 0);
493
+
494
+ // destroy single + idempotency.
495
+ var s2 = await b.session.create({ userId: "user-99", ttlMs: b.constants.TIME.hours(1) });
496
+ softCheck("session(mysql): destroy(token) returns true for a live session",
497
+ (await b.session.destroy(s2.token)) === true);
498
+ softCheck("session(mysql): destroy is idempotent (second destroy returns false)",
499
+ (await b.session.destroy(s2.token)) === false);
500
+
501
+ // purgeExpired side-effect: age a row directly, then sweep.
502
+ await b.session.create({ userId: "user-exp", ttlMs: b.constants.TIME.hours(1) });
503
+ _execMysql("UPDATE `_blamejs_sessions` SET `expiresAt` = 1 WHERE `expiresAt` > 1;");
504
+ var purged = await b.session.purgeExpired();
505
+ softCheck("session(mysql): purgeExpired removed the expired row(s) (>=1)", purged >= 1);
506
+ softCheck("session(mysql): count() is 0 after purge", (await b.session.count()) === 0);
507
+ }
508
+
509
+ // ======================================================================
510
+ // b.nonceStore cluster backend on real MySQL.
511
+ // checkAndInsert (ON DUPLICATE KEY UPDATE no-op fold = atomic
512
+ // first-seen) + replay rejection + purgeExpired. The MySQL no-op fold
513
+ // returns affectedRows=1 for a fresh insert (won) and 0 for a duplicate
514
+ // (replay), which is the wire signal checkAndInsert reads.
515
+ // ======================================================================
516
+ async function _testNonceCluster() {
517
+ var store = b.nonceStore.create({ backend: "cluster" });
518
+ var future = Date.now() + b.constants.TIME.minutes(10);
519
+
520
+ softCheck("nonce(mysql): first checkAndInsert returns true (unseen)",
521
+ (await store.checkAndInsert("nonce-aaa", future)) === true);
522
+ var n1 = _selectDirect(
523
+ "SELECT COUNT(*) AS `n` FROM `_blamejs_api_encrypt_nonces` WHERE `nonceHash` = 'nonce-aaa';");
524
+ softCheck("nonce(mysql): the nonce row physically landed",
525
+ n1.length === 1 && Number(n1[0].n) === 1);
526
+
527
+ softCheck("nonce(mysql): replay of the same nonce returns false (DUPLICATE-KEY no-op fold)",
528
+ (await store.checkAndInsert("nonce-aaa", future)) === false);
529
+ softCheck("nonce(mysql): a distinct nonce is accepted",
530
+ (await store.checkAndInsert("nonce-bbb", future)) === true);
531
+
532
+ await store.checkAndInsert("nonce-expired", Date.now() - 1000);
533
+ var purged = await store.purgeExpired();
534
+ softCheck("nonce(mysql): purgeExpired removed the expired nonce (>=1)", purged >= 1);
535
+ softCheck("nonce(mysql): a live nonce still rejects replay after purge",
536
+ (await store.checkAndInsert("nonce-aaa", future)) === false);
537
+
538
+ store.close();
539
+ }
540
+
541
+ // ======================================================================
542
+ // b.middleware.rateLimit cluster backend on real MySQL.
543
+ // take() = ON DUPLICATE KEY UPDATE with a per-column CASE conflict
544
+ // action (proposed row = VALUES(`col`), existing row = `table`.`col`)
545
+ // + a readback SELECT (MySQL has no RETURNING). The returned BIGINT
546
+ // count must coerce to a JS number so count<=limit is numeric.
547
+ // ======================================================================
548
+ async function _testRateLimitCluster() {
549
+ var backend = rateLimitModule._clusterBackend({
550
+ backend: "cluster", limit: 3, windowMs: b.constants.TIME.minutes(1),
551
+ });
552
+
553
+ var v1 = await backend.take("ratekey-1", 1);
554
+ softCheck("rate-limit(mysql): first take() is allowed against real MySQL",
555
+ v1 && v1.allowed === true);
556
+ softCheck("rate-limit(mysql): the take() verdict count math is numeric " +
557
+ "(remaining is a finite number, not NaN from a string compare)",
558
+ typeof v1.remaining === "number" && isFinite(v1.remaining) && v1.remaining === 2);
559
+
560
+ var rowAfter1 = _selectDirect(
561
+ "SELECT `count` FROM `_blamejs_rate_limit_counters` WHERE `key` = 'ratekey-1';");
562
+ softCheck("rate-limit(mysql): counter row landed with count=1",
563
+ rowAfter1.length === 1 && Number(rowAfter1[0].count) === 1);
564
+
565
+ var v2 = await backend.take("ratekey-1", 1);
566
+ var v3 = await backend.take("ratekey-1", 1);
567
+ var v4 = await backend.take("ratekey-1", 1);
568
+ softCheck("rate-limit(mysql): 2nd + 3rd allowed, 4th over the limit refused",
569
+ v2.allowed === true && v3.allowed === true && v4.allowed === false);
570
+ softCheck("rate-limit(mysql): the over-limit verdict carries a positive retryAfter",
571
+ typeof v4.retryAfter === "number" && v4.retryAfter > 0);
572
+ var rowAfter4 = _selectDirect(
573
+ "SELECT `count` FROM `_blamejs_rate_limit_counters` WHERE `key` = 'ratekey-1';");
574
+ softCheck("rate-limit(mysql): counter incremented monotonically to 4 (same-window CASE branch)",
575
+ rowAfter4.length === 1 && Number(rowAfter4[0].count) === 4);
576
+
577
+ // A window advance resets the count (the CASE conflict action's
578
+ // window-rollover branch). Force a stale window then take() again.
579
+ _execMysql("UPDATE `_blamejs_rate_limit_counters` SET `windowStart` = 0 WHERE `key` = 'ratekey-1';");
580
+ var vReset = await backend.take("ratekey-1", 1);
581
+ softCheck("rate-limit(mysql): a fresh window resets the count (CASE rollover) — allowed again",
582
+ vReset.allowed === true);
583
+ var rowAfterReset = _selectDirect(
584
+ "SELECT `count` FROM `_blamejs_rate_limit_counters` WHERE `key` = 'ratekey-1';");
585
+ softCheck("rate-limit(mysql): count reset to 1 on window advance (proposed-window CASE branch)",
586
+ rowAfterReset.length === 1 && Number(rowAfterReset[0].count) === 1);
587
+
588
+ // A distinct key is tracked independently.
589
+ var other = await backend.take("ratekey-2", 1);
590
+ softCheck("rate-limit(mysql): a distinct key is counted independently",
591
+ other.allowed === true && other.remaining === 2);
592
+
593
+ // reset(key) deletes the counter row.
594
+ await backend.reset("ratekey-1");
595
+ var afterReset = _selectDirect(
596
+ "SELECT COUNT(*) AS `n` FROM `_blamejs_rate_limit_counters` WHERE `key` = 'ratekey-1';");
597
+ softCheck("rate-limit(mysql): reset(key) deleted the counter row",
598
+ afterReset.length === 1 && Number(afterReset[0].n) === 0);
599
+
600
+ if (typeof backend.close === "function") backend.close();
601
+ }
602
+
603
+ module.exports = { run: run };
604
+
605
+ if (require.main === module) {
606
+ run().then(
607
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); process.exit(0); },
608
+ function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
609
+ );
610
+ }