@blamejs/blamejs-shop 0.0.44

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 (1220) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +17 -0
  3. package/README.md +117 -0
  4. package/SECURITY.md +139 -0
  5. package/lib/admin.js +952 -0
  6. package/lib/analytics.js +267 -0
  7. package/lib/cart.js +279 -0
  8. package/lib/catalog-import.js +344 -0
  9. package/lib/catalog.js +769 -0
  10. package/lib/checkout.js +320 -0
  11. package/lib/config.js +151 -0
  12. package/lib/customers.js +322 -0
  13. package/lib/email.js +242 -0
  14. package/lib/externaldb-d1.js +283 -0
  15. package/lib/index.js +57 -0
  16. package/lib/inventory-alerts.js +198 -0
  17. package/lib/newsletter.js +142 -0
  18. package/lib/order.js +380 -0
  19. package/lib/payment.js +318 -0
  20. package/lib/pricing.js +185 -0
  21. package/lib/r2-bridge.js +169 -0
  22. package/lib/shipping.js +185 -0
  23. package/lib/storefront.js +2160 -0
  24. package/lib/subscriptions.js +410 -0
  25. package/lib/tax.js +161 -0
  26. package/lib/theme.js +194 -0
  27. package/lib/vendor/MANIFEST.json +19 -0
  28. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +23 -0
  29. package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +34 -0
  30. package/lib/vendor/blamejs/.clusterfuzzlite/project.yaml +16 -0
  31. package/lib/vendor/blamejs/.dockerignore +45 -0
  32. package/lib/vendor/blamejs/.gitattributes +42 -0
  33. package/lib/vendor/blamejs/.github/CODEOWNERS +4 -0
  34. package/lib/vendor/blamejs/.github/FUNDING.yml +2 -0
  35. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/bug_report.md +58 -0
  36. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/config.yml +8 -0
  37. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/feature_request.md +99 -0
  38. package/lib/vendor/blamejs/.github/PULL_REQUEST_TEMPLATE.md +77 -0
  39. package/lib/vendor/blamejs/.github/dependabot.yml +37 -0
  40. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +148 -0
  41. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +107 -0
  42. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +122 -0
  43. package/lib/vendor/blamejs/.github/workflows/ci.yml +511 -0
  44. package/lib/vendor/blamejs/.github/workflows/codeql.yml +50 -0
  45. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +655 -0
  46. package/lib/vendor/blamejs/.github/workflows/release-container.yml +406 -0
  47. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +101 -0
  48. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +134 -0
  49. package/lib/vendor/blamejs/.gitignore +102 -0
  50. package/lib/vendor/blamejs/.gitleaks.toml +166 -0
  51. package/lib/vendor/blamejs/.hadolint.yaml +18 -0
  52. package/lib/vendor/blamejs/.npmrc +5 -0
  53. package/lib/vendor/blamejs/.pinact.yaml +17 -0
  54. package/lib/vendor/blamejs/ARCHITECTURE.md +158 -0
  55. package/lib/vendor/blamejs/CHANGELOG.md +1351 -0
  56. package/lib/vendor/blamejs/CODE_OF_CONDUCT.md +86 -0
  57. package/lib/vendor/blamejs/CONTRIBUTING.md +156 -0
  58. package/lib/vendor/blamejs/GOVERNANCE.md +201 -0
  59. package/lib/vendor/blamejs/LICENSE +201 -0
  60. package/lib/vendor/blamejs/LTS-CALENDAR.md +29 -0
  61. package/lib/vendor/blamejs/MIGRATING.md +29 -0
  62. package/lib/vendor/blamejs/NOTICE +81 -0
  63. package/lib/vendor/blamejs/README.md +304 -0
  64. package/lib/vendor/blamejs/SECURITY.md +432 -0
  65. package/lib/vendor/blamejs/api-snapshot.json +48709 -0
  66. package/lib/vendor/blamejs/assets/BlameJS_Logo.png +0 -0
  67. package/lib/vendor/blamejs/assets/BlameJS_Logo.svg +129 -0
  68. package/lib/vendor/blamejs/bench/README.md +77 -0
  69. package/lib/vendor/blamejs/bench/_helpers.js +70 -0
  70. package/lib/vendor/blamejs/bench/baseline.json +183 -0
  71. package/lib/vendor/blamejs/bench/crypto-hash.bench.js +19 -0
  72. package/lib/vendor/blamejs/bench/crypto-symmetric.bench.js +28 -0
  73. package/lib/vendor/blamejs/bench/run.js +140 -0
  74. package/lib/vendor/blamejs/bench/safe-json.bench.js +31 -0
  75. package/lib/vendor/blamejs/bin/blamejs.js +13 -0
  76. package/lib/vendor/blamejs/docker/caddy/Caddyfile +46 -0
  77. package/lib/vendor/blamejs/docker/coredns/Corefile +37 -0
  78. package/lib/vendor/blamejs/docker/haproxy/haproxy.cfg +52 -0
  79. package/lib/vendor/blamejs/docker/init/generate-certs.sh +118 -0
  80. package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +87 -0
  81. package/lib/vendor/blamejs/docker/mitmproxy/config.yaml +16 -0
  82. package/lib/vendor/blamejs/docker/mongo/init-tls.sh +17 -0
  83. package/lib/vendor/blamejs/docker/mysql/my.cnf +12 -0
  84. package/lib/vendor/blamejs/docker/nats/nats.conf +33 -0
  85. package/lib/vendor/blamejs/docker/postgres/init-tls.sh +17 -0
  86. package/lib/vendor/blamejs/docker/postgres/postgresql.conf +18 -0
  87. package/lib/vendor/blamejs/docker/rabbitmq/rabbitmq.conf +18 -0
  88. package/lib/vendor/blamejs/docker/redis/redis.conf +15 -0
  89. package/lib/vendor/blamejs/docker/squid/squid.conf +24 -0
  90. package/lib/vendor/blamejs/docker/syslog/syslog-ng.conf +34 -0
  91. package/lib/vendor/blamejs/docker-compose.test.yml +545 -0
  92. package/lib/vendor/blamejs/docs/cis-postgres-crosswalk.md +102 -0
  93. package/lib/vendor/blamejs/docs/cis-sqlite-equivalent.md +92 -0
  94. package/lib/vendor/blamejs/eslint.config.mjs +204 -0
  95. package/lib/vendor/blamejs/examples/wiki/Caddyfile +40 -0
  96. package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +218 -0
  97. package/lib/vendor/blamejs/examples/wiki/Dockerfile +120 -0
  98. package/lib/vendor/blamejs/examples/wiki/README.md +157 -0
  99. package/lib/vendor/blamejs/examples/wiki/cli-snapshot.json +250 -0
  100. package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +231 -0
  101. package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +166 -0
  102. package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +217 -0
  103. package/lib/vendor/blamejs/examples/wiki/lib/auto-site-entries.js +139 -0
  104. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +555 -0
  105. package/lib/vendor/blamejs/examples/wiki/lib/harvest-cli.js +507 -0
  106. package/lib/vendor/blamejs/examples/wiki/lib/harvest-env-vars.js +435 -0
  107. package/lib/vendor/blamejs/examples/wiki/lib/harvest-errors.js +282 -0
  108. package/lib/vendor/blamejs/examples/wiki/lib/harvest-vendored-deps.js +321 -0
  109. package/lib/vendor/blamejs/examples/wiki/lib/nav.js +15 -0
  110. package/lib/vendor/blamejs/examples/wiki/lib/opts-resolver.js +75 -0
  111. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +508 -0
  112. package/lib/vendor/blamejs/examples/wiki/lib/section.js +276 -0
  113. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +587 -0
  114. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +318 -0
  115. package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +122 -0
  116. package/lib/vendor/blamejs/examples/wiki/migrations/0001-pages-schema.js +74 -0
  117. package/lib/vendor/blamejs/examples/wiki/package.json +18 -0
  118. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.png +0 -0
  119. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.svg +129 -0
  120. package/lib/vendor/blamejs/examples/wiki/public/robots.txt +5 -0
  121. package/lib/vendor/blamejs/examples/wiki/public/vendor/MANIFEST.json +30 -0
  122. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.css +1 -0
  123. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.js +15 -0
  124. package/lib/vendor/blamejs/examples/wiki/public/wiki.css +1250 -0
  125. package/lib/vendor/blamejs/examples/wiki/routes/admin.js +366 -0
  126. package/lib/vendor/blamejs/examples/wiki/routes/integration.js +230 -0
  127. package/lib/vendor/blamejs/examples/wiki/routes/pages.js +266 -0
  128. package/lib/vendor/blamejs/examples/wiki/scripts/backfill-module-metadata.js +214 -0
  129. package/lib/vendor/blamejs/examples/wiki/seeders/prod/0001-default-pages.js +35 -0
  130. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/_index.js +34 -0
  131. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/api.js +76 -0
  132. package/lib/vendor/blamejs/examples/wiki/server.js +129 -0
  133. package/lib/vendor/blamejs/examples/wiki/site.config.js +197 -0
  134. package/lib/vendor/blamejs/examples/wiki/snippets/README.md +38 -0
  135. package/lib/vendor/blamejs/examples/wiki/snippets/auth/password-hash.example.js +15 -0
  136. package/lib/vendor/blamejs/examples/wiki/src/editor.js +103 -0
  137. package/lib/vendor/blamejs/examples/wiki/src/wiki.js +349 -0
  138. package/lib/vendor/blamejs/examples/wiki/test/AUDIT.md +155 -0
  139. package/lib/vendor/blamejs/examples/wiki/test/codebase-patterns.test.js +594 -0
  140. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +741 -0
  141. package/lib/vendor/blamejs/examples/wiki/test/find-missing-pages.js +254 -0
  142. package/lib/vendor/blamejs/examples/wiki/test/integration.js +391 -0
  143. package/lib/vendor/blamejs/examples/wiki/test/validate-cli-snapshot.js +379 -0
  144. package/lib/vendor/blamejs/examples/wiki/test/validate-env-snapshot.js +346 -0
  145. package/lib/vendor/blamejs/examples/wiki/test/validate-nav-coverage.js +212 -0
  146. package/lib/vendor/blamejs/examples/wiki/test/validate-site-coverage.js +252 -0
  147. package/lib/vendor/blamejs/examples/wiki/test/validate-source-comment-blocks.js +107 -0
  148. package/lib/vendor/blamejs/examples/wiki/views/_layout.html +115 -0
  149. package/lib/vendor/blamejs/examples/wiki/views/admin/api-keys.html +51 -0
  150. package/lib/vendor/blamejs/examples/wiki/views/admin/dashboard.html +22 -0
  151. package/lib/vendor/blamejs/examples/wiki/views/admin/edit.html +17 -0
  152. package/lib/vendor/blamejs/examples/wiki/views/home.html +85 -0
  153. package/lib/vendor/blamejs/examples/wiki/views/login.html +18 -0
  154. package/lib/vendor/blamejs/examples/wiki/views/page.html +5 -0
  155. package/lib/vendor/blamejs/examples/wiki/views/partials/nav.html +13 -0
  156. package/lib/vendor/blamejs/examples/wiki/views/search.html +19 -0
  157. package/lib/vendor/blamejs/examples/wiki/wiki.config.js +15 -0
  158. package/lib/vendor/blamejs/fuzz/README.md +137 -0
  159. package/lib/vendor/blamejs/fuzz/_expected.js +35 -0
  160. package/lib/vendor/blamejs/fuzz/guard-agent-registry.fuzz.js +22 -0
  161. package/lib/vendor/blamejs/fuzz/guard-csv.fuzz.js +16 -0
  162. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/01-basic.csv +3 -0
  163. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/02-formula.csv +1 -0
  164. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/03-hyperlink.csv +1 -0
  165. package/lib/vendor/blamejs/fuzz/guard-dsn.fuzz.js +22 -0
  166. package/lib/vendor/blamejs/fuzz/guard-email.fuzz.js +16 -0
  167. package/lib/vendor/blamejs/fuzz/guard-email_seed_corpus/01-basic.eml +5 -0
  168. package/lib/vendor/blamejs/fuzz/guard-envelope.fuzz.js +24 -0
  169. package/lib/vendor/blamejs/fuzz/guard-event-bus-payload.fuzz.js +24 -0
  170. package/lib/vendor/blamejs/fuzz/guard-event-bus-topic.fuzz.js +20 -0
  171. package/lib/vendor/blamejs/fuzz/guard-html.fuzz.js +16 -0
  172. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/01-basic.html +1 -0
  173. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/02-script.html +1 -0
  174. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/03-event.html +1 -0
  175. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/04-jsurl.html +1 -0
  176. package/lib/vendor/blamejs/fuzz/guard-idempotency-key.fuzz.js +20 -0
  177. package/lib/vendor/blamejs/fuzz/guard-imap-command.fuzz.js +35 -0
  178. package/lib/vendor/blamejs/fuzz/guard-jmap.fuzz.js +41 -0
  179. package/lib/vendor/blamejs/fuzz/guard-json.fuzz.js +16 -0
  180. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/01-basic.json +1 -0
  181. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/02-proto.json +1 -0
  182. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/03-dupkey.json +1 -0
  183. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/04-nan.json +1 -0
  184. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/05-bom.json +1 -0
  185. package/lib/vendor/blamejs/fuzz/guard-list-id.fuzz.js +21 -0
  186. package/lib/vendor/blamejs/fuzz/guard-list-unsubscribe.fuzz.js +25 -0
  187. package/lib/vendor/blamejs/fuzz/guard-mail-compose.fuzz.js +22 -0
  188. package/lib/vendor/blamejs/fuzz/guard-mail-move.fuzz.js +22 -0
  189. package/lib/vendor/blamejs/fuzz/guard-mail-query.fuzz.js +27 -0
  190. package/lib/vendor/blamejs/fuzz/guard-mail-reply.fuzz.js +23 -0
  191. package/lib/vendor/blamejs/fuzz/guard-mail-sieve.fuzz.js +36 -0
  192. package/lib/vendor/blamejs/fuzz/guard-managesieve-command.fuzz.js +26 -0
  193. package/lib/vendor/blamejs/fuzz/guard-markdown.fuzz.js +16 -0
  194. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/01-basic.md +2 -0
  195. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/02-jsurl.md +1 -0
  196. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/03-jsimg.md +1 -0
  197. package/lib/vendor/blamejs/fuzz/guard-message-id.fuzz.js +26 -0
  198. package/lib/vendor/blamejs/fuzz/guard-pop3-command.fuzz.js +23 -0
  199. package/lib/vendor/blamejs/fuzz/guard-posture-chain.fuzz.js +22 -0
  200. package/lib/vendor/blamejs/fuzz/guard-saga-config.fuzz.js +32 -0
  201. package/lib/vendor/blamejs/fuzz/guard-smtp-command.fuzz.js +27 -0
  202. package/lib/vendor/blamejs/fuzz/guard-snapshot-envelope.fuzz.js +22 -0
  203. package/lib/vendor/blamejs/fuzz/guard-stream-args.fuzz.js +22 -0
  204. package/lib/vendor/blamejs/fuzz/guard-svg.fuzz.js +16 -0
  205. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/01-basic.svg +1 -0
  206. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/02-script.svg +1 -0
  207. package/lib/vendor/blamejs/fuzz/guard-tenant-id.fuzz.js +20 -0
  208. package/lib/vendor/blamejs/fuzz/guard-trace-context.fuzz.js +30 -0
  209. package/lib/vendor/blamejs/fuzz/guard-xml.fuzz.js +16 -0
  210. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/01-basic.xml +1 -0
  211. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/02-xxe.xml +1 -0
  212. package/lib/vendor/blamejs/fuzz/guard-yaml.fuzz.js +16 -0
  213. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/01-basic.yaml +2 -0
  214. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/02-anchor.yaml +2 -0
  215. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/03-norway.yaml +1 -0
  216. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/04-multidoc.yaml +4 -0
  217. package/lib/vendor/blamejs/fuzz/parsers__safe-ini.fuzz.js +16 -0
  218. package/lib/vendor/blamejs/fuzz/parsers__safe-ini_seed_corpus/01-basic.ini +2 -0
  219. package/lib/vendor/blamejs/fuzz/parsers__safe-toml.fuzz.js +16 -0
  220. package/lib/vendor/blamejs/fuzz/parsers__safe-toml_seed_corpus/01-basic.toml +4 -0
  221. package/lib/vendor/blamejs/fuzz/parsers__safe-xml.fuzz.js +16 -0
  222. package/lib/vendor/blamejs/fuzz/parsers__safe-xml_seed_corpus/01-basic.xml +1 -0
  223. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml.fuzz.js +16 -0
  224. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml_seed_corpus/01-basic.yaml +4 -0
  225. package/lib/vendor/blamejs/fuzz/safe-decompress.fuzz.js +49 -0
  226. package/lib/vendor/blamejs/fuzz/safe-dns.fuzz.js +29 -0
  227. package/lib/vendor/blamejs/fuzz/safe-ical.fuzz.js +16 -0
  228. package/lib/vendor/blamejs/fuzz/safe-icap.fuzz.js +42 -0
  229. package/lib/vendor/blamejs/fuzz/safe-json.fuzz.js +25 -0
  230. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/01-object.txt +1 -0
  231. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/02-array.txt +1 -0
  232. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/03-string.txt +1 -0
  233. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/04-proto.txt +1 -0
  234. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/05-deep.txt +1 -0
  235. package/lib/vendor/blamejs/fuzz/safe-jsonpath.fuzz.js +16 -0
  236. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/01-basic.txt +1 -0
  237. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/02-filter.txt +1 -0
  238. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/03-deepscan.txt +1 -0
  239. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/04-slice.txt +1 -0
  240. package/lib/vendor/blamejs/fuzz/safe-mime.fuzz.js +27 -0
  241. package/lib/vendor/blamejs/fuzz/safe-mount-info.fuzz.js +33 -0
  242. package/lib/vendor/blamejs/fuzz/safe-sieve.fuzz.js +28 -0
  243. package/lib/vendor/blamejs/fuzz/safe-smtp.fuzz.js +64 -0
  244. package/lib/vendor/blamejs/fuzz/safe-url.fuzz.js +16 -0
  245. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/01-basic.txt +1 -0
  246. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/02-userinfo.txt +1 -0
  247. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/03-dangerous.txt +1 -0
  248. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/04-data.txt +1 -0
  249. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/05-ipv6.txt +1 -0
  250. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/06-idn.txt +1 -0
  251. package/lib/vendor/blamejs/fuzz/safe-vcard.fuzz.js +16 -0
  252. package/lib/vendor/blamejs/index.js +678 -0
  253. package/lib/vendor/blamejs/keys/release-pqc-pub.json +7 -0
  254. package/lib/vendor/blamejs/lib/_test/crypto-fixtures.js +67 -0
  255. package/lib/vendor/blamejs/lib/a2a-tasks.js +598 -0
  256. package/lib/vendor/blamejs/lib/a2a.js +407 -0
  257. package/lib/vendor/blamejs/lib/acme.js +1448 -0
  258. package/lib/vendor/blamejs/lib/agent-audit.js +45 -0
  259. package/lib/vendor/blamejs/lib/agent-event-bus.js +382 -0
  260. package/lib/vendor/blamejs/lib/agent-idempotency.js +497 -0
  261. package/lib/vendor/blamejs/lib/agent-orchestrator.js +717 -0
  262. package/lib/vendor/blamejs/lib/agent-posture-chain.js +366 -0
  263. package/lib/vendor/blamejs/lib/agent-saga.js +321 -0
  264. package/lib/vendor/blamejs/lib/agent-snapshot.js +676 -0
  265. package/lib/vendor/blamejs/lib/agent-stream.js +269 -0
  266. package/lib/vendor/blamejs/lib/agent-tenant.js +632 -0
  267. package/lib/vendor/blamejs/lib/agent-trace.js +281 -0
  268. package/lib/vendor/blamejs/lib/ai-adverse-decision.js +184 -0
  269. package/lib/vendor/blamejs/lib/ai-content-detect.js +268 -0
  270. package/lib/vendor/blamejs/lib/ai-input.js +201 -0
  271. package/lib/vendor/blamejs/lib/ai-model-manifest.js +363 -0
  272. package/lib/vendor/blamejs/lib/ai-pref.js +340 -0
  273. package/lib/vendor/blamejs/lib/api-key.js +721 -0
  274. package/lib/vendor/blamejs/lib/api-snapshot.js +458 -0
  275. package/lib/vendor/blamejs/lib/app-shutdown.js +557 -0
  276. package/lib/vendor/blamejs/lib/app.js +365 -0
  277. package/lib/vendor/blamejs/lib/archive.js +547 -0
  278. package/lib/vendor/blamejs/lib/arg-parser.js +697 -0
  279. package/lib/vendor/blamejs/lib/argon2-builtin.js +173 -0
  280. package/lib/vendor/blamejs/lib/asn1-der.js +424 -0
  281. package/lib/vendor/blamejs/lib/asyncapi-bindings.js +160 -0
  282. package/lib/vendor/blamejs/lib/asyncapi-traits.js +143 -0
  283. package/lib/vendor/blamejs/lib/asyncapi.js +575 -0
  284. package/lib/vendor/blamejs/lib/atomic-file.js +1023 -0
  285. package/lib/vendor/blamejs/lib/audit-chain.js +266 -0
  286. package/lib/vendor/blamejs/lib/audit-daily-review.js +389 -0
  287. package/lib/vendor/blamejs/lib/audit-sign.js +751 -0
  288. package/lib/vendor/blamejs/lib/audit-tools.js +1113 -0
  289. package/lib/vendor/blamejs/lib/audit.js +1671 -0
  290. package/lib/vendor/blamejs/lib/auth/aal.js +169 -0
  291. package/lib/vendor/blamejs/lib/auth/access-lock.js +220 -0
  292. package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +265 -0
  293. package/lib/vendor/blamejs/lib/auth/ato-kill-switch.js +112 -0
  294. package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +111 -0
  295. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +573 -0
  296. package/lib/vendor/blamejs/lib/auth/ciba.js +637 -0
  297. package/lib/vendor/blamejs/lib/auth/dpop.js +516 -0
  298. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +306 -0
  299. package/lib/vendor/blamejs/lib/auth/fal.js +229 -0
  300. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +681 -0
  301. package/lib/vendor/blamejs/lib/auth/jwt-external.js +519 -0
  302. package/lib/vendor/blamejs/lib/auth/jwt.js +430 -0
  303. package/lib/vendor/blamejs/lib/auth/lockout.js +449 -0
  304. package/lib/vendor/blamejs/lib/auth/oauth.js +2141 -0
  305. package/lib/vendor/blamejs/lib/auth/oid4vci.js +657 -0
  306. package/lib/vendor/blamejs/lib/auth/oid4vp.js +531 -0
  307. package/lib/vendor/blamejs/lib/auth/openid-federation.js +600 -0
  308. package/lib/vendor/blamejs/lib/auth/passkey.js +676 -0
  309. package/lib/vendor/blamejs/lib/auth/password.js +693 -0
  310. package/lib/vendor/blamejs/lib/auth/saml.js +2109 -0
  311. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-disclosure.js +95 -0
  312. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +225 -0
  313. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +197 -0
  314. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +728 -0
  315. package/lib/vendor/blamejs/lib/auth/status-list.js +272 -0
  316. package/lib/vendor/blamejs/lib/auth/step-up-policy.js +335 -0
  317. package/lib/vendor/blamejs/lib/auth/step-up.js +454 -0
  318. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +505 -0
  319. package/lib/vendor/blamejs/lib/auth-header.js +148 -0
  320. package/lib/vendor/blamejs/lib/backup/bundle.js +265 -0
  321. package/lib/vendor/blamejs/lib/backup/crypto.js +176 -0
  322. package/lib/vendor/blamejs/lib/backup/index.js +1001 -0
  323. package/lib/vendor/blamejs/lib/backup/manifest.js +443 -0
  324. package/lib/vendor/blamejs/lib/boot-gates.js +174 -0
  325. package/lib/vendor/blamejs/lib/breach-deadline.js +272 -0
  326. package/lib/vendor/blamejs/lib/break-glass.js +1753 -0
  327. package/lib/vendor/blamejs/lib/budr.js +205 -0
  328. package/lib/vendor/blamejs/lib/bundler.js +461 -0
  329. package/lib/vendor/blamejs/lib/cache-redis.js +256 -0
  330. package/lib/vendor/blamejs/lib/cache-status.js +288 -0
  331. package/lib/vendor/blamejs/lib/cache.js +1331 -0
  332. package/lib/vendor/blamejs/lib/calendar.js +1240 -0
  333. package/lib/vendor/blamejs/lib/canonical-json.js +143 -0
  334. package/lib/vendor/blamejs/lib/cdn-cache-control.js +473 -0
  335. package/lib/vendor/blamejs/lib/cert.js +763 -0
  336. package/lib/vendor/blamejs/lib/chain-writer.js +259 -0
  337. package/lib/vendor/blamejs/lib/circuit-breaker.js +101 -0
  338. package/lib/vendor/blamejs/lib/cli-helpers.js +237 -0
  339. package/lib/vendor/blamejs/lib/cli.js +2328 -0
  340. package/lib/vendor/blamejs/lib/client-hints.js +318 -0
  341. package/lib/vendor/blamejs/lib/cloud-events.js +277 -0
  342. package/lib/vendor/blamejs/lib/cluster-provider-db.js +317 -0
  343. package/lib/vendor/blamejs/lib/cluster-storage.js +351 -0
  344. package/lib/vendor/blamejs/lib/cluster.js +1017 -0
  345. package/lib/vendor/blamejs/lib/cms-codec.js +826 -0
  346. package/lib/vendor/blamejs/lib/codepoint-class.js +262 -0
  347. package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +190 -0
  348. package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +205 -0
  349. package/lib/vendor/blamejs/lib/compliance-ai-act-risk.js +189 -0
  350. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +200 -0
  351. package/lib/vendor/blamejs/lib/compliance-ai-act.js +821 -0
  352. package/lib/vendor/blamejs/lib/compliance-eaa.js +204 -0
  353. package/lib/vendor/blamejs/lib/compliance-sanctions-aliases.js +167 -0
  354. package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +206 -0
  355. package/lib/vendor/blamejs/lib/compliance-sanctions-fuzzy.js +297 -0
  356. package/lib/vendor/blamejs/lib/compliance-sanctions.js +569 -0
  357. package/lib/vendor/blamejs/lib/compliance.js +1558 -0
  358. package/lib/vendor/blamejs/lib/config-drift.js +426 -0
  359. package/lib/vendor/blamejs/lib/config.js +446 -0
  360. package/lib/vendor/blamejs/lib/consent.js +369 -0
  361. package/lib/vendor/blamejs/lib/constants.js +209 -0
  362. package/lib/vendor/blamejs/lib/content-credentials.js +704 -0
  363. package/lib/vendor/blamejs/lib/cookies.js +560 -0
  364. package/lib/vendor/blamejs/lib/cra-report.js +299 -0
  365. package/lib/vendor/blamejs/lib/credential-hash.js +394 -0
  366. package/lib/vendor/blamejs/lib/crypto-field.js +1017 -0
  367. package/lib/vendor/blamejs/lib/crypto-hpke-pq.js +187 -0
  368. package/lib/vendor/blamejs/lib/crypto-hpke.js +256 -0
  369. package/lib/vendor/blamejs/lib/crypto.js +1908 -0
  370. package/lib/vendor/blamejs/lib/csp.js +271 -0
  371. package/lib/vendor/blamejs/lib/csv.js +418 -0
  372. package/lib/vendor/blamejs/lib/daemon.js +481 -0
  373. package/lib/vendor/blamejs/lib/dark-patterns.js +488 -0
  374. package/lib/vendor/blamejs/lib/data-act.js +328 -0
  375. package/lib/vendor/blamejs/lib/db-collection.js +587 -0
  376. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +267 -0
  377. package/lib/vendor/blamejs/lib/db-declare-view.js +420 -0
  378. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +333 -0
  379. package/lib/vendor/blamejs/lib/db-query.js +802 -0
  380. package/lib/vendor/blamejs/lib/db-role-context.js +50 -0
  381. package/lib/vendor/blamejs/lib/db-schema.js +322 -0
  382. package/lib/vendor/blamejs/lib/db.js +3111 -0
  383. package/lib/vendor/blamejs/lib/dbsc.js +299 -0
  384. package/lib/vendor/blamejs/lib/ddl-change-control.js +523 -0
  385. package/lib/vendor/blamejs/lib/deprecate.js +377 -0
  386. package/lib/vendor/blamejs/lib/dev.js +405 -0
  387. package/lib/vendor/blamejs/lib/dora.js +402 -0
  388. package/lib/vendor/blamejs/lib/dr-runbook.js +368 -0
  389. package/lib/vendor/blamejs/lib/dsr.js +1188 -0
  390. package/lib/vendor/blamejs/lib/dual-control.js +526 -0
  391. package/lib/vendor/blamejs/lib/early-hints.js +212 -0
  392. package/lib/vendor/blamejs/lib/error-page.js +420 -0
  393. package/lib/vendor/blamejs/lib/events.js +214 -0
  394. package/lib/vendor/blamejs/lib/external-db-migrate.js +659 -0
  395. package/lib/vendor/blamejs/lib/external-db.js +1877 -0
  396. package/lib/vendor/blamejs/lib/fapi2.js +394 -0
  397. package/lib/vendor/blamejs/lib/fda-21cfr11.js +395 -0
  398. package/lib/vendor/blamejs/lib/fdx.js +370 -0
  399. package/lib/vendor/blamejs/lib/fedcm.js +264 -0
  400. package/lib/vendor/blamejs/lib/file-type.js +360 -0
  401. package/lib/vendor/blamejs/lib/file-upload.js +1256 -0
  402. package/lib/vendor/blamejs/lib/flag-cache.js +136 -0
  403. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +135 -0
  404. package/lib/vendor/blamejs/lib/flag-providers.js +279 -0
  405. package/lib/vendor/blamejs/lib/flag-targeting.js +210 -0
  406. package/lib/vendor/blamejs/lib/flag.js +346 -0
  407. package/lib/vendor/blamejs/lib/forms.js +525 -0
  408. package/lib/vendor/blamejs/lib/framework-error.js +724 -0
  409. package/lib/vendor/blamejs/lib/framework-schema.js +845 -0
  410. package/lib/vendor/blamejs/lib/framework-sha1-hibp.js +34 -0
  411. package/lib/vendor/blamejs/lib/fsm.js +469 -0
  412. package/lib/vendor/blamejs/lib/gate-contract.js +1661 -0
  413. package/lib/vendor/blamejs/lib/gdpr-ropa.js +261 -0
  414. package/lib/vendor/blamejs/lib/graphql-federation.js +234 -0
  415. package/lib/vendor/blamejs/lib/guard-agent-registry.js +179 -0
  416. package/lib/vendor/blamejs/lib/guard-all.js +555 -0
  417. package/lib/vendor/blamejs/lib/guard-archive.js +901 -0
  418. package/lib/vendor/blamejs/lib/guard-auth.js +451 -0
  419. package/lib/vendor/blamejs/lib/guard-cidr.js +676 -0
  420. package/lib/vendor/blamejs/lib/guard-csv.js +1176 -0
  421. package/lib/vendor/blamejs/lib/guard-domain.js +814 -0
  422. package/lib/vendor/blamejs/lib/guard-dsn.js +382 -0
  423. package/lib/vendor/blamejs/lib/guard-email.js +951 -0
  424. package/lib/vendor/blamejs/lib/guard-envelope.js +294 -0
  425. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +217 -0
  426. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +150 -0
  427. package/lib/vendor/blamejs/lib/guard-filename.js +956 -0
  428. package/lib/vendor/blamejs/lib/guard-graphql.js +731 -0
  429. package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +164 -0
  430. package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +144 -0
  431. package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +154 -0
  432. package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +44 -0
  433. package/lib/vendor/blamejs/lib/guard-html-wcag.js +470 -0
  434. package/lib/vendor/blamejs/lib/guard-html.js +1209 -0
  435. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +151 -0
  436. package/lib/vendor/blamejs/lib/guard-image.js +584 -0
  437. package/lib/vendor/blamejs/lib/guard-imap-command.js +337 -0
  438. package/lib/vendor/blamejs/lib/guard-jmap.js +321 -0
  439. package/lib/vendor/blamejs/lib/guard-json.js +935 -0
  440. package/lib/vendor/blamejs/lib/guard-jsonpath.js +512 -0
  441. package/lib/vendor/blamejs/lib/guard-jwt.js +772 -0
  442. package/lib/vendor/blamejs/lib/guard-list-id.js +318 -0
  443. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +412 -0
  444. package/lib/vendor/blamejs/lib/guard-mail-compose.js +282 -0
  445. package/lib/vendor/blamejs/lib/guard-mail-move.js +202 -0
  446. package/lib/vendor/blamejs/lib/guard-mail-query.js +310 -0
  447. package/lib/vendor/blamejs/lib/guard-mail-reply.js +172 -0
  448. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +207 -0
  449. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +566 -0
  450. package/lib/vendor/blamejs/lib/guard-markdown.js +768 -0
  451. package/lib/vendor/blamejs/lib/guard-message-id.js +267 -0
  452. package/lib/vendor/blamejs/lib/guard-mime.js +609 -0
  453. package/lib/vendor/blamejs/lib/guard-oauth.js +650 -0
  454. package/lib/vendor/blamejs/lib/guard-pdf.js +569 -0
  455. package/lib/vendor/blamejs/lib/guard-pop3-command.js +317 -0
  456. package/lib/vendor/blamejs/lib/guard-posture-chain.js +201 -0
  457. package/lib/vendor/blamejs/lib/guard-regex.js +632 -0
  458. package/lib/vendor/blamejs/lib/guard-saga-config.js +157 -0
  459. package/lib/vendor/blamejs/lib/guard-shell.js +522 -0
  460. package/lib/vendor/blamejs/lib/guard-smtp-command.js +594 -0
  461. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +168 -0
  462. package/lib/vendor/blamejs/lib/guard-stream-args.js +166 -0
  463. package/lib/vendor/blamejs/lib/guard-svg.js +1163 -0
  464. package/lib/vendor/blamejs/lib/guard-template.js +490 -0
  465. package/lib/vendor/blamejs/lib/guard-tenant-id.js +138 -0
  466. package/lib/vendor/blamejs/lib/guard-time.js +586 -0
  467. package/lib/vendor/blamejs/lib/guard-trace-context.js +172 -0
  468. package/lib/vendor/blamejs/lib/guard-uuid.js +548 -0
  469. package/lib/vendor/blamejs/lib/guard-xml.js +666 -0
  470. package/lib/vendor/blamejs/lib/guard-yaml.js +726 -0
  471. package/lib/vendor/blamejs/lib/hal.js +125 -0
  472. package/lib/vendor/blamejs/lib/handlers.js +350 -0
  473. package/lib/vendor/blamejs/lib/honeytoken.js +168 -0
  474. package/lib/vendor/blamejs/lib/html-balance.js +347 -0
  475. package/lib/vendor/blamejs/lib/http-client-cache.js +923 -0
  476. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +519 -0
  477. package/lib/vendor/blamejs/lib/http-client.js +2152 -0
  478. package/lib/vendor/blamejs/lib/http-message-signature.js +589 -0
  479. package/lib/vendor/blamejs/lib/http2-teardown.js +34 -0
  480. package/lib/vendor/blamejs/lib/i18n-messageformat.js +398 -0
  481. package/lib/vendor/blamejs/lib/i18n.js +931 -0
  482. package/lib/vendor/blamejs/lib/iab-mspa.js +257 -0
  483. package/lib/vendor/blamejs/lib/iab-tcf.js +461 -0
  484. package/lib/vendor/blamejs/lib/importmap-integrity.js +90 -0
  485. package/lib/vendor/blamejs/lib/inbox.js +435 -0
  486. package/lib/vendor/blamejs/lib/incident-report.js +314 -0
  487. package/lib/vendor/blamejs/lib/ip-utils.js +102 -0
  488. package/lib/vendor/blamejs/lib/jobs.js +185 -0
  489. package/lib/vendor/blamejs/lib/jose-jwe-experimental.js +228 -0
  490. package/lib/vendor/blamejs/lib/jsonapi.js +230 -0
  491. package/lib/vendor/blamejs/lib/keychain.js +865 -0
  492. package/lib/vendor/blamejs/lib/lazy-require.js +48 -0
  493. package/lib/vendor/blamejs/lib/legal-hold.js +374 -0
  494. package/lib/vendor/blamejs/lib/local-db-thin.js +321 -0
  495. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +369 -0
  496. package/lib/vendor/blamejs/lib/log-stream-local.js +146 -0
  497. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +410 -0
  498. package/lib/vendor/blamejs/lib/log-stream-otlp.js +286 -0
  499. package/lib/vendor/blamejs/lib/log-stream-syslog.js +310 -0
  500. package/lib/vendor/blamejs/lib/log-stream-webhook.js +199 -0
  501. package/lib/vendor/blamejs/lib/log-stream.js +584 -0
  502. package/lib/vendor/blamejs/lib/log.js +625 -0
  503. package/lib/vendor/blamejs/lib/lro.js +200 -0
  504. package/lib/vendor/blamejs/lib/mail-agent.js +786 -0
  505. package/lib/vendor/blamejs/lib/mail-arc-sign.js +417 -0
  506. package/lib/vendor/blamejs/lib/mail-arf.js +343 -0
  507. package/lib/vendor/blamejs/lib/mail-auth.js +2144 -0
  508. package/lib/vendor/blamejs/lib/mail-bimi.js +1047 -0
  509. package/lib/vendor/blamejs/lib/mail-bounce.js +955 -0
  510. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1286 -0
  511. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +789 -0
  512. package/lib/vendor/blamejs/lib/mail-crypto.js +108 -0
  513. package/lib/vendor/blamejs/lib/mail-dav.js +1224 -0
  514. package/lib/vendor/blamejs/lib/mail-deploy.js +1119 -0
  515. package/lib/vendor/blamejs/lib/mail-dkim.js +1250 -0
  516. package/lib/vendor/blamejs/lib/mail-greylist.js +448 -0
  517. package/lib/vendor/blamejs/lib/mail-helo.js +473 -0
  518. package/lib/vendor/blamejs/lib/mail-journal.js +435 -0
  519. package/lib/vendor/blamejs/lib/mail-mdn.js +424 -0
  520. package/lib/vendor/blamejs/lib/mail-rbl.js +392 -0
  521. package/lib/vendor/blamejs/lib/mail-require-tls.js +198 -0
  522. package/lib/vendor/blamejs/lib/mail-scan.js +502 -0
  523. package/lib/vendor/blamejs/lib/mail-send-deliver.js +629 -0
  524. package/lib/vendor/blamejs/lib/mail-server-imap.js +1858 -0
  525. package/lib/vendor/blamejs/lib/mail-server-jmap.js +1565 -0
  526. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +908 -0
  527. package/lib/vendor/blamejs/lib/mail-server-mx.js +969 -0
  528. package/lib/vendor/blamejs/lib/mail-server-pop3.js +915 -0
  529. package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +315 -0
  530. package/lib/vendor/blamejs/lib/mail-server-registry.js +378 -0
  531. package/lib/vendor/blamejs/lib/mail-server-submission.js +1396 -0
  532. package/lib/vendor/blamejs/lib/mail-server-tls.js +445 -0
  533. package/lib/vendor/blamejs/lib/mail-sieve.js +557 -0
  534. package/lib/vendor/blamejs/lib/mail-spam-score.js +284 -0
  535. package/lib/vendor/blamejs/lib/mail-srs.js +248 -0
  536. package/lib/vendor/blamejs/lib/mail-store-fts.js +394 -0
  537. package/lib/vendor/blamejs/lib/mail-store.js +929 -0
  538. package/lib/vendor/blamejs/lib/mail-unsubscribe.js +400 -0
  539. package/lib/vendor/blamejs/lib/mail.js +1971 -0
  540. package/lib/vendor/blamejs/lib/mcp-tool-registry.js +473 -0
  541. package/lib/vendor/blamejs/lib/mcp.js +950 -0
  542. package/lib/vendor/blamejs/lib/metrics.js +1503 -0
  543. package/lib/vendor/blamejs/lib/middleware/age-gate.js +177 -0
  544. package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +203 -0
  545. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +981 -0
  546. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +137 -0
  547. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +171 -0
  548. package/lib/vendor/blamejs/lib/middleware/attach-user.js +220 -0
  549. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +293 -0
  550. package/lib/vendor/blamejs/lib/middleware/body-parser.js +1519 -0
  551. package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +183 -0
  552. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +217 -0
  553. package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +122 -0
  554. package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +355 -0
  555. package/lib/vendor/blamejs/lib/middleware/compression.js +489 -0
  556. package/lib/vendor/blamejs/lib/middleware/cookies.js +130 -0
  557. package/lib/vendor/blamejs/lib/middleware/cors.js +386 -0
  558. package/lib/vendor/blamejs/lib/middleware/csp-nonce.js +388 -0
  559. package/lib/vendor/blamejs/lib/middleware/csp-report.js +167 -0
  560. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +499 -0
  561. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +243 -0
  562. package/lib/vendor/blamejs/lib/middleware/db-role-for.js +304 -0
  563. package/lib/vendor/blamejs/lib/middleware/dpop.js +402 -0
  564. package/lib/vendor/blamejs/lib/middleware/error-handler.js +69 -0
  565. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +168 -0
  566. package/lib/vendor/blamejs/lib/middleware/flag-context.js +110 -0
  567. package/lib/vendor/blamejs/lib/middleware/gpc.js +153 -0
  568. package/lib/vendor/blamejs/lib/middleware/headers.js +242 -0
  569. package/lib/vendor/blamejs/lib/middleware/health.js +438 -0
  570. package/lib/vendor/blamejs/lib/middleware/host-allowlist.js +189 -0
  571. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +964 -0
  572. package/lib/vendor/blamejs/lib/middleware/index.js +183 -0
  573. package/lib/vendor/blamejs/lib/middleware/nel.js +214 -0
  574. package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +237 -0
  575. package/lib/vendor/blamejs/lib/middleware/no-cache.js +106 -0
  576. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +177 -0
  577. package/lib/vendor/blamejs/lib/middleware/protected-resource-metadata.js +277 -0
  578. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +556 -0
  579. package/lib/vendor/blamejs/lib/middleware/request-id.js +79 -0
  580. package/lib/vendor/blamejs/lib/middleware/request-log.js +205 -0
  581. package/lib/vendor/blamejs/lib/middleware/require-aal.js +138 -0
  582. package/lib/vendor/blamejs/lib/middleware/require-auth.js +144 -0
  583. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +290 -0
  584. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +113 -0
  585. package/lib/vendor/blamejs/lib/middleware/require-methods.js +97 -0
  586. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +212 -0
  587. package/lib/vendor/blamejs/lib/middleware/require-step-up.js +226 -0
  588. package/lib/vendor/blamejs/lib/middleware/scim-server.js +375 -0
  589. package/lib/vendor/blamejs/lib/middleware/security-headers.js +285 -0
  590. package/lib/vendor/blamejs/lib/middleware/security-txt.js +170 -0
  591. package/lib/vendor/blamejs/lib/middleware/span-http-server.js +280 -0
  592. package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +323 -0
  593. package/lib/vendor/blamejs/lib/middleware/sse.js +200 -0
  594. package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +167 -0
  595. package/lib/vendor/blamejs/lib/middleware/trace-propagate.js +148 -0
  596. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +749 -0
  597. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +164 -0
  598. package/lib/vendor/blamejs/lib/migration-files.js +37 -0
  599. package/lib/vendor/blamejs/lib/migrations.js +385 -0
  600. package/lib/vendor/blamejs/lib/mime-parse.js +198 -0
  601. package/lib/vendor/blamejs/lib/money.js +699 -0
  602. package/lib/vendor/blamejs/lib/mtls-ca.js +572 -0
  603. package/lib/vendor/blamejs/lib/mtls-engine-default.js +501 -0
  604. package/lib/vendor/blamejs/lib/network-byte-quota.js +308 -0
  605. package/lib/vendor/blamejs/lib/network-dns-resolver.js +533 -0
  606. package/lib/vendor/blamejs/lib/network-dns.js +1930 -0
  607. package/lib/vendor/blamejs/lib/network-heartbeat.js +425 -0
  608. package/lib/vendor/blamejs/lib/network-nts.js +574 -0
  609. package/lib/vendor/blamejs/lib/network-proxy.js +265 -0
  610. package/lib/vendor/blamejs/lib/network-smtp-policy.js +836 -0
  611. package/lib/vendor/blamejs/lib/network-tls.js +3126 -0
  612. package/lib/vendor/blamejs/lib/network.js +346 -0
  613. package/lib/vendor/blamejs/lib/nis2-report.js +181 -0
  614. package/lib/vendor/blamejs/lib/nist-crosswalk.js +293 -0
  615. package/lib/vendor/blamejs/lib/nonce-store.js +177 -0
  616. package/lib/vendor/blamejs/lib/notify.js +683 -0
  617. package/lib/vendor/blamejs/lib/ntp-check.js +458 -0
  618. package/lib/vendor/blamejs/lib/numeric-bounds.js +111 -0
  619. package/lib/vendor/blamejs/lib/numeric-checks.js +40 -0
  620. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +349 -0
  621. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +488 -0
  622. package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +351 -0
  623. package/lib/vendor/blamejs/lib/object-store/gcs.js +515 -0
  624. package/lib/vendor/blamejs/lib/object-store/http-put.js +153 -0
  625. package/lib/vendor/blamejs/lib/object-store/http-request.js +38 -0
  626. package/lib/vendor/blamejs/lib/object-store/index.js +197 -0
  627. package/lib/vendor/blamejs/lib/object-store/local.js +163 -0
  628. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +1133 -0
  629. package/lib/vendor/blamejs/lib/object-store/sigv4.js +957 -0
  630. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +420 -0
  631. package/lib/vendor/blamejs/lib/observability-tracer.js +395 -0
  632. package/lib/vendor/blamejs/lib/observability.js +720 -0
  633. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +248 -0
  634. package/lib/vendor/blamejs/lib/openapi-schema-walk.js +192 -0
  635. package/lib/vendor/blamejs/lib/openapi-security.js +169 -0
  636. package/lib/vendor/blamejs/lib/openapi-yaml.js +154 -0
  637. package/lib/vendor/blamejs/lib/openapi.js +489 -0
  638. package/lib/vendor/blamejs/lib/otel-export.js +278 -0
  639. package/lib/vendor/blamejs/lib/outbox.js +547 -0
  640. package/lib/vendor/blamejs/lib/pagination.js +542 -0
  641. package/lib/vendor/blamejs/lib/parsers/index.js +91 -0
  642. package/lib/vendor/blamejs/lib/parsers/safe-env.js +642 -0
  643. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +293 -0
  644. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +784 -0
  645. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +390 -0
  646. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +1015 -0
  647. package/lib/vendor/blamejs/lib/permissions.js +793 -0
  648. package/lib/vendor/blamejs/lib/pick.js +105 -0
  649. package/lib/vendor/blamejs/lib/pqc-agent.js +351 -0
  650. package/lib/vendor/blamejs/lib/pqc-gate.js +279 -0
  651. package/lib/vendor/blamejs/lib/pqc-software.js +271 -0
  652. package/lib/vendor/blamejs/lib/problem-details.js +482 -0
  653. package/lib/vendor/blamejs/lib/process-spawn.js +196 -0
  654. package/lib/vendor/blamejs/lib/promise-pool.js +162 -0
  655. package/lib/vendor/blamejs/lib/protobuf-encoder.js +190 -0
  656. package/lib/vendor/blamejs/lib/protocol-dispatcher.js +161 -0
  657. package/lib/vendor/blamejs/lib/public-suffix.js +403 -0
  658. package/lib/vendor/blamejs/lib/pubsub-cluster.js +154 -0
  659. package/lib/vendor/blamejs/lib/pubsub-redis.js +167 -0
  660. package/lib/vendor/blamejs/lib/pubsub.js +463 -0
  661. package/lib/vendor/blamejs/lib/queue-local.js +476 -0
  662. package/lib/vendor/blamejs/lib/queue-redis.js +745 -0
  663. package/lib/vendor/blamejs/lib/queue-sqs.js +319 -0
  664. package/lib/vendor/blamejs/lib/queue.js +1016 -0
  665. package/lib/vendor/blamejs/lib/redact.js +1007 -0
  666. package/lib/vendor/blamejs/lib/redis-client.js +520 -0
  667. package/lib/vendor/blamejs/lib/render.js +285 -0
  668. package/lib/vendor/blamejs/lib/request-helpers.js +767 -0
  669. package/lib/vendor/blamejs/lib/resource-access-lock.js +116 -0
  670. package/lib/vendor/blamejs/lib/restore-bundle.js +340 -0
  671. package/lib/vendor/blamejs/lib/restore-rollback.js +365 -0
  672. package/lib/vendor/blamejs/lib/restore.js +409 -0
  673. package/lib/vendor/blamejs/lib/retention.js +640 -0
  674. package/lib/vendor/blamejs/lib/retry.js +523 -0
  675. package/lib/vendor/blamejs/lib/router.js +1289 -0
  676. package/lib/vendor/blamejs/lib/safe-async.js +1184 -0
  677. package/lib/vendor/blamejs/lib/safe-buffer.js +562 -0
  678. package/lib/vendor/blamejs/lib/safe-decompress.js +297 -0
  679. package/lib/vendor/blamejs/lib/safe-dns.js +665 -0
  680. package/lib/vendor/blamejs/lib/safe-ical.js +634 -0
  681. package/lib/vendor/blamejs/lib/safe-icap.js +502 -0
  682. package/lib/vendor/blamejs/lib/safe-json.js +946 -0
  683. package/lib/vendor/blamejs/lib/safe-jsonpath.js +285 -0
  684. package/lib/vendor/blamejs/lib/safe-mime.js +831 -0
  685. package/lib/vendor/blamejs/lib/safe-mount-info.js +306 -0
  686. package/lib/vendor/blamejs/lib/safe-path.js +254 -0
  687. package/lib/vendor/blamejs/lib/safe-redirect.js +106 -0
  688. package/lib/vendor/blamejs/lib/safe-schema.js +1810 -0
  689. package/lib/vendor/blamejs/lib/safe-sieve.js +684 -0
  690. package/lib/vendor/blamejs/lib/safe-smtp.js +185 -0
  691. package/lib/vendor/blamejs/lib/safe-sql.js +363 -0
  692. package/lib/vendor/blamejs/lib/safe-url.js +428 -0
  693. package/lib/vendor/blamejs/lib/safe-vcard.js +473 -0
  694. package/lib/vendor/blamejs/lib/sandbox-worker.js +135 -0
  695. package/lib/vendor/blamejs/lib/sandbox.js +358 -0
  696. package/lib/vendor/blamejs/lib/scheduler.js +827 -0
  697. package/lib/vendor/blamejs/lib/sd-notify.js +269 -0
  698. package/lib/vendor/blamejs/lib/sec-cyber.js +214 -0
  699. package/lib/vendor/blamejs/lib/security-assert.js +395 -0
  700. package/lib/vendor/blamejs/lib/seeders.js +620 -0
  701. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +309 -0
  702. package/lib/vendor/blamejs/lib/self-update.js +804 -0
  703. package/lib/vendor/blamejs/lib/server-timing.js +174 -0
  704. package/lib/vendor/blamejs/lib/session-device-binding.js +431 -0
  705. package/lib/vendor/blamejs/lib/session-stores.js +138 -0
  706. package/lib/vendor/blamejs/lib/session.js +1162 -0
  707. package/lib/vendor/blamejs/lib/slug.js +381 -0
  708. package/lib/vendor/blamejs/lib/sse.js +349 -0
  709. package/lib/vendor/blamejs/lib/ssrf-guard.js +792 -0
  710. package/lib/vendor/blamejs/lib/standard-webhooks.js +183 -0
  711. package/lib/vendor/blamejs/lib/static.js +1249 -0
  712. package/lib/vendor/blamejs/lib/storage.js +1272 -0
  713. package/lib/vendor/blamejs/lib/stream-throttle.js +235 -0
  714. package/lib/vendor/blamejs/lib/structured-fields.js +244 -0
  715. package/lib/vendor/blamejs/lib/subject.js +667 -0
  716. package/lib/vendor/blamejs/lib/tcpa-10dlc.js +175 -0
  717. package/lib/vendor/blamejs/lib/template.js +931 -0
  718. package/lib/vendor/blamejs/lib/tenant-quota.js +545 -0
  719. package/lib/vendor/blamejs/lib/test-harness.js +275 -0
  720. package/lib/vendor/blamejs/lib/testing.js +1185 -0
  721. package/lib/vendor/blamejs/lib/time.js +578 -0
  722. package/lib/vendor/blamejs/lib/tls-exporter.js +239 -0
  723. package/lib/vendor/blamejs/lib/totp.js +318 -0
  724. package/lib/vendor/blamejs/lib/tracing.js +546 -0
  725. package/lib/vendor/blamejs/lib/uuid.js +207 -0
  726. package/lib/vendor/blamejs/lib/validate-opts.js +381 -0
  727. package/lib/vendor/blamejs/lib/vault/index.js +638 -0
  728. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +311 -0
  729. package/lib/vendor/blamejs/lib/vault/passphrase-source.js +198 -0
  730. package/lib/vendor/blamejs/lib/vault/rotate.js +803 -0
  731. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +471 -0
  732. package/lib/vendor/blamejs/lib/vault/wrap.js +296 -0
  733. package/lib/vendor/blamejs/lib/vault-aad.js +259 -0
  734. package/lib/vendor/blamejs/lib/vendor/.vendor-data-pubkey +4 -0
  735. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +161 -0
  736. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.data.js +68 -0
  737. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.pem +33 -0
  738. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.data.js +1325 -0
  739. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.txt +10002 -0
  740. package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +9 -0
  741. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +18 -0
  742. package/lib/vendor/blamejs/lib/vendor/pki.cjs +181 -0
  743. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +16382 -0
  744. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5881 -0
  745. package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +328 -0
  746. package/lib/vendor/blamejs/lib/vendor/vendor-data-pubkey.js +16 -0
  747. package/lib/vendor/blamejs/lib/vendor-data.js +520 -0
  748. package/lib/vendor/blamejs/lib/vex.js +630 -0
  749. package/lib/vendor/blamejs/lib/watcher.js +608 -0
  750. package/lib/vendor/blamejs/lib/web-push-vapid.js +322 -0
  751. package/lib/vendor/blamejs/lib/webhook.js +977 -0
  752. package/lib/vendor/blamejs/lib/websocket-channels.js +327 -0
  753. package/lib/vendor/blamejs/lib/websocket.js +1561 -0
  754. package/lib/vendor/blamejs/lib/wiki-concepts.js +338 -0
  755. package/lib/vendor/blamejs/lib/worker-pool.js +464 -0
  756. package/lib/vendor/blamejs/lib/ws-client.js +978 -0
  757. package/lib/vendor/blamejs/lib/xml-c14n.js +506 -0
  758. package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +164 -0
  759. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +19 -0
  760. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/README.md +88 -0
  761. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/build.sh +26 -0
  762. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/project.yaml +28 -0
  763. package/lib/vendor/blamejs/package.json +81 -0
  764. package/lib/vendor/blamejs/release-notes/v0.0.x.json +310 -0
  765. package/lib/vendor/blamejs/release-notes/v0.1.x.json +1798 -0
  766. package/lib/vendor/blamejs/release-notes/v0.10.x.json +1288 -0
  767. package/lib/vendor/blamejs/release-notes/v0.11.x.json +2551 -0
  768. package/lib/vendor/blamejs/release-notes/v0.12.0.json +64 -0
  769. package/lib/vendor/blamejs/release-notes/v0.12.1.json +32 -0
  770. package/lib/vendor/blamejs/release-notes/v0.12.2.json +45 -0
  771. package/lib/vendor/blamejs/release-notes/v0.2.x.json +706 -0
  772. package/lib/vendor/blamejs/release-notes/v0.3.x.json +786 -0
  773. package/lib/vendor/blamejs/release-notes/v0.4.x.json +588 -0
  774. package/lib/vendor/blamejs/release-notes/v0.5.x.json +390 -0
  775. package/lib/vendor/blamejs/release-notes/v0.6.x.json +1947 -0
  776. package/lib/vendor/blamejs/release-notes/v0.7.x.json +3811 -0
  777. package/lib/vendor/blamejs/release-notes/v0.8.x.json +3318 -0
  778. package/lib/vendor/blamejs/release-notes/v0.9.x.json +2257 -0
  779. package/lib/vendor/blamejs/scripts/build-vendored-sbom.js +325 -0
  780. package/lib/vendor/blamejs/scripts/check-api-snapshot.js +62 -0
  781. package/lib/vendor/blamejs/scripts/check-changelog-extract.js +108 -0
  782. package/lib/vendor/blamejs/scripts/check-pack-against-gitignore.js +83 -0
  783. package/lib/vendor/blamejs/scripts/check-services.js +483 -0
  784. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +349 -0
  785. package/lib/vendor/blamejs/scripts/consolidate-release-notes.js +216 -0
  786. package/lib/vendor/blamejs/scripts/gen-migrating.js +275 -0
  787. package/lib/vendor/blamejs/scripts/generate-changelog-entry.js +577 -0
  788. package/lib/vendor/blamejs/scripts/generate-release-signing-key.js +79 -0
  789. package/lib/vendor/blamejs/scripts/publish-dep-confusion-placeholder.sh +101 -0
  790. package/lib/vendor/blamejs/scripts/refresh-api-snapshot.js +31 -0
  791. package/lib/vendor/blamejs/scripts/refresh-vendor-manifest.js +132 -0
  792. package/lib/vendor/blamejs/scripts/release.js +652 -0
  793. package/lib/vendor/blamejs/scripts/sha3-digest.js +62 -0
  794. package/lib/vendor/blamejs/scripts/sign-release-artifact.js +92 -0
  795. package/lib/vendor/blamejs/scripts/test-integration.js +181 -0
  796. package/lib/vendor/blamejs/scripts/test-wiki-integration.js +126 -0
  797. package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +77 -0
  798. package/lib/vendor/blamejs/scripts/vendor-data-gen.js +186 -0
  799. package/lib/vendor/blamejs/scripts/vendor-data-keygen.js +101 -0
  800. package/lib/vendor/blamejs/scripts/vendor-update.sh +278 -0
  801. package/lib/vendor/blamejs/test/00-primitives.js +19075 -0
  802. package/lib/vendor/blamejs/test/10-state.js +622 -0
  803. package/lib/vendor/blamejs/test/20-db.js +561 -0
  804. package/lib/vendor/blamejs/test/30-chain.js +2110 -0
  805. package/lib/vendor/blamejs/test/40-consumers.js +2453 -0
  806. package/lib/vendor/blamejs/test/50-integration.js +486 -0
  807. package/lib/vendor/blamejs/test/_helpers.js +10 -0
  808. package/lib/vendor/blamejs/test/_smoke-worker.js +69 -0
  809. package/lib/vendor/blamejs/test/fixtures/exploit-corpus/corpus.json +368 -0
  810. package/lib/vendor/blamejs/test/fixtures/http-client-stream-payload.txt +2 -0
  811. package/lib/vendor/blamejs/test/fixtures/worker-pool/echo.js +52 -0
  812. package/lib/vendor/blamejs/test/helpers/_codebase-shingle-worker.js +24 -0
  813. package/lib/vendor/blamejs/test/helpers/_codebase-shingle.js +203 -0
  814. package/lib/vendor/blamejs/test/helpers/_shape-match.js +513 -0
  815. package/lib/vendor/blamejs/test/helpers/check.js +36 -0
  816. package/lib/vendor/blamejs/test/helpers/cluster.js +70 -0
  817. package/lib/vendor/blamejs/test/helpers/db.js +143 -0
  818. package/lib/vendor/blamejs/test/helpers/drivers.js +207 -0
  819. package/lib/vendor/blamejs/test/helpers/fs-watch.js +101 -0
  820. package/lib/vendor/blamejs/test/helpers/http.js +14 -0
  821. package/lib/vendor/blamejs/test/helpers/index.js +93 -0
  822. package/lib/vendor/blamejs/test/helpers/json-round-trip.js +120 -0
  823. package/lib/vendor/blamejs/test/helpers/mocks.js +20 -0
  824. package/lib/vendor/blamejs/test/helpers/otel.js +13 -0
  825. package/lib/vendor/blamejs/test/helpers/services.js +380 -0
  826. package/lib/vendor/blamejs/test/helpers/wait.js +206 -0
  827. package/lib/vendor/blamejs/test/integration/cache.test.js +235 -0
  828. package/lib/vendor/blamejs/test/integration/cluster-provider-mysql.test.js +174 -0
  829. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +611 -0
  830. package/lib/vendor/blamejs/test/integration/http-client.test.js +129 -0
  831. package/lib/vendor/blamejs/test/integration/log-stream.test.js +219 -0
  832. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +181 -0
  833. package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +152 -0
  834. package/lib/vendor/blamejs/test/integration/mail-smtp.test.js +161 -0
  835. package/lib/vendor/blamejs/test/integration/mtls-ca.test.js +289 -0
  836. package/lib/vendor/blamejs/test/integration/network-dns.test.js +123 -0
  837. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +101 -0
  838. package/lib/vendor/blamejs/test/integration/ntp-check.test.js +89 -0
  839. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +403 -0
  840. package/lib/vendor/blamejs/test/integration/pqc-pkcs8-forward-compat.test.js +271 -0
  841. package/lib/vendor/blamejs/test/integration/pubsub.test.js +137 -0
  842. package/lib/vendor/blamejs/test/integration/queue-redis.test.js +352 -0
  843. package/lib/vendor/blamejs/test/integration/redis-client-tls.test.js +96 -0
  844. package/lib/vendor/blamejs/test/integration/ssrf-guard.test.js +98 -0
  845. package/lib/vendor/blamejs/test/integration/websocket-permessage-deflate.test.js +261 -0
  846. package/lib/vendor/blamejs/test/integration/ws-client-roundtrip.test.js +230 -0
  847. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +211 -0
  848. package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +59 -0
  849. package/lib/vendor/blamejs/test/layer-0-primitives/access-lock.test.js +136 -0
  850. package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +219 -0
  851. package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +69 -0
  852. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +266 -0
  853. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +262 -0
  854. package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +390 -0
  855. package/lib/vendor/blamejs/test/layer-0-primitives/agent-posture-chain.test.js +174 -0
  856. package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +279 -0
  857. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +322 -0
  858. package/lib/vendor/blamejs/test/layer-0-primitives/agent-stream.test.js +227 -0
  859. package/lib/vendor/blamejs/test/layer-0-primitives/agent-tenant.test.js +302 -0
  860. package/lib/vendor/blamejs/test/layer-0-primitives/agent-trace.test.js +150 -0
  861. package/lib/vendor/blamejs/test/layer-0-primitives/ai-adverse-decision.test.js +44 -0
  862. package/lib/vendor/blamejs/test/layer-0-primitives/ai-content-detect.test.js +150 -0
  863. package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +50 -0
  864. package/lib/vendor/blamejs/test/layer-0-primitives/ai-model-manifest.test.js +96 -0
  865. package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +76 -0
  866. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +1080 -0
  867. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +311 -0
  868. package/lib/vendor/blamejs/test/layer-0-primitives/archive-zip-stream.test.js +291 -0
  869. package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +140 -0
  870. package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +267 -0
  871. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +108 -0
  872. package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +929 -0
  873. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-conflict-path.test.js +80 -0
  874. package/lib/vendor/blamejs/test/layer-0-primitives/audit-cve-defensive.test.js +176 -0
  875. package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +132 -0
  876. package/lib/vendor/blamejs/test/layer-0-primitives/audit-export-cadf.test.js +97 -0
  877. package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +141 -0
  878. package/lib/vendor/blamejs/test/layer-0-primitives/audit-segregation.test.js +115 -0
  879. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +163 -0
  880. package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +246 -0
  881. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge-verifier.test.js +485 -0
  882. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +331 -0
  883. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +352 -0
  884. package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +572 -0
  885. package/lib/vendor/blamejs/test/layer-0-primitives/auth-password-audit.test.js +61 -0
  886. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-bucket-ops.test.js +258 -0
  887. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +105 -0
  888. package/lib/vendor/blamejs/test/layer-0-primitives/backup-worker.test.js +34 -0
  889. package/lib/vendor/blamejs/test/layer-0-primitives/bearer-auth.test.js +107 -0
  890. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +131 -0
  891. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +118 -0
  892. package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +85 -0
  893. package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +38 -0
  894. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +861 -0
  895. package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +55 -0
  896. package/lib/vendor/blamejs/test/layer-0-primitives/bundler-engine.test.js +209 -0
  897. package/lib/vendor/blamejs/test/layer-0-primitives/cache-status.test.js +129 -0
  898. package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +871 -0
  899. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +891 -0
  900. package/lib/vendor/blamejs/test/layer-0-primitives/canonical-json-jcs.test.js +43 -0
  901. package/lib/vendor/blamejs/test/layer-0-primitives/cdn-cache-control.test.js +243 -0
  902. package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +550 -0
  903. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +107 -0
  904. package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +147 -0
  905. package/lib/vendor/blamejs/test/layer-0-primitives/cli-audit-verify-chain.test.js +104 -0
  906. package/lib/vendor/blamejs/test/layer-0-primitives/cli-backup.test.js +135 -0
  907. package/lib/vendor/blamejs/test/layer-0-primitives/cli-config-drift.test.js +67 -0
  908. package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +75 -0
  909. package/lib/vendor/blamejs/test/layer-0-primitives/cli-file-type.test.js +98 -0
  910. package/lib/vendor/blamejs/test/layer-0-primitives/cli-helpers.test.js +145 -0
  911. package/lib/vendor/blamejs/test/layer-0-primitives/cli-mtls.test.js +133 -0
  912. package/lib/vendor/blamejs/test/layer-0-primitives/cli-password.test.js +97 -0
  913. package/lib/vendor/blamejs/test/layer-0-primitives/cli-restore.test.js +160 -0
  914. package/lib/vendor/blamejs/test/layer-0-primitives/cli-retention.test.js +84 -0
  915. package/lib/vendor/blamejs/test/layer-0-primitives/cli-security.test.js +69 -0
  916. package/lib/vendor/blamejs/test/layer-0-primitives/cli-vault.test.js +142 -0
  917. package/lib/vendor/blamejs/test/layer-0-primitives/client-hints.test.js +133 -0
  918. package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +237 -0
  919. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +9600 -0
  920. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +575 -0
  921. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-cascade.test.js +89 -0
  922. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eaa.test.js +36 -0
  923. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +712 -0
  924. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +278 -0
  925. package/lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js +97 -0
  926. package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +424 -0
  927. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +94 -0
  928. package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +357 -0
  929. package/lib/vendor/blamejs/test/layer-0-primitives/cra-report.test.js +31 -0
  930. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +226 -0
  931. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +86 -0
  932. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-envelope.test.js +85 -0
  933. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +193 -0
  934. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +98 -0
  935. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke-pq.test.js +132 -0
  936. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke.test.js +155 -0
  937. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-mlkem768-x25519.test.js +129 -0
  938. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-namespace-hash.test.js +0 -0
  939. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-random-int.test.js +72 -0
  940. package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +96 -0
  941. package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +401 -0
  942. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +34 -0
  943. package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +180 -0
  944. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +210 -0
  945. package/lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js +153 -0
  946. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +66 -0
  947. package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +74 -0
  948. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection-extensions.test.js +226 -0
  949. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection.test.js +136 -0
  950. package/lib/vendor/blamejs/test/layer-0-primitives/db-init-extensions.test.js +165 -0
  951. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +150 -0
  952. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-extensions.test.js +191 -0
  953. package/lib/vendor/blamejs/test/layer-0-primitives/db-role-for.test.js +228 -0
  954. package/lib/vendor/blamejs/test/layer-0-primitives/db-vacuum.test.js +55 -0
  955. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +89 -0
  956. package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +184 -0
  957. package/lib/vendor/blamejs/test/layer-0-primitives/declare-row-policy.test.js +203 -0
  958. package/lib/vendor/blamejs/test/layer-0-primitives/declare-view.test.js +303 -0
  959. package/lib/vendor/blamejs/test/layer-0-primitives/dns-dnssec-algorithm.test.js +163 -0
  960. package/lib/vendor/blamejs/test/layer-0-primitives/dns-null-mx.test.js +39 -0
  961. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +165 -0
  962. package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +59 -0
  963. package/lib/vendor/blamejs/test/layer-0-primitives/dsr-state-rules.test.js +55 -0
  964. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +786 -0
  965. package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +105 -0
  966. package/lib/vendor/blamejs/test/layer-0-primitives/early-hints.test.js +147 -0
  967. package/lib/vendor/blamejs/test/layer-0-primitives/events.test.js +105 -0
  968. package/lib/vendor/blamejs/test/layer-0-primitives/exploit-replay.test.js +243 -0
  969. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +181 -0
  970. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +190 -0
  971. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-routing.test.js +531 -0
  972. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +118 -0
  973. package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +89 -0
  974. package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +156 -0
  975. package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +79 -0
  976. package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +216 -0
  977. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +434 -0
  978. package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +432 -0
  979. package/lib/vendor/blamejs/test/layer-0-primitives/file-type.test.js +81 -0
  980. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +887 -0
  981. package/lib/vendor/blamejs/test/layer-0-primitives/forensic-snapshot.test.js +51 -0
  982. package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +375 -0
  983. package/lib/vendor/blamejs/test/layer-0-primitives/gcs-bucket-ops.test.js +321 -0
  984. package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +41 -0
  985. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +32 -0
  986. package/lib/vendor/blamejs/test/layer-0-primitives/guard-agent-registry.test.js +87 -0
  987. package/lib/vendor/blamejs/test/layer-0-primitives/guard-all.test.js +328 -0
  988. package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +339 -0
  989. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +694 -0
  990. package/lib/vendor/blamejs/test/layer-0-primitives/guard-dsn.test.js +296 -0
  991. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +234 -0
  992. package/lib/vendor/blamejs/test/layer-0-primitives/guard-envelope.test.js +192 -0
  993. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-payload.test.js +89 -0
  994. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-topic.test.js +71 -0
  995. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +386 -0
  996. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +859 -0
  997. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +357 -0
  998. package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +92 -0
  999. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  1000. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +174 -0
  1001. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +317 -0
  1002. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-id.test.js +199 -0
  1003. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-unsubscribe.test.js +214 -0
  1004. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-compose.test.js +111 -0
  1005. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-move.test.js +110 -0
  1006. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-query.test.js +112 -0
  1007. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-reply.test.js +86 -0
  1008. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-sieve.test.js +92 -0
  1009. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +301 -0
  1010. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +265 -0
  1011. package/lib/vendor/blamejs/test/layer-0-primitives/guard-message-id.test.js +0 -0
  1012. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +161 -0
  1013. package/lib/vendor/blamejs/test/layer-0-primitives/guard-posture-chain.test.js +100 -0
  1014. package/lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js +79 -0
  1015. package/lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js +269 -0
  1016. package/lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js +89 -0
  1017. package/lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js +78 -0
  1018. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +288 -0
  1019. package/lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js +69 -0
  1020. package/lib/vendor/blamejs/test/layer-0-primitives/guard-trace-context.test.js +102 -0
  1021. package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +202 -0
  1022. package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +203 -0
  1023. package/lib/vendor/blamejs/test/layer-0-primitives/hal.test.js +51 -0
  1024. package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +50 -0
  1025. package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +37 -0
  1026. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +692 -0
  1027. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +280 -0
  1028. package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +225 -0
  1029. package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +203 -0
  1030. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +991 -0
  1031. package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +63 -0
  1032. package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +73 -0
  1033. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +612 -0
  1034. package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +56 -0
  1035. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +166 -0
  1036. package/lib/vendor/blamejs/test/layer-0-primitives/incident-report.test.js +29 -0
  1037. package/lib/vendor/blamejs/test/layer-0-primitives/jose-jwe-experimental.test.js +121 -0
  1038. package/lib/vendor/blamejs/test/layer-0-primitives/json-api.test.js +58 -0
  1039. package/lib/vendor/blamejs/test/layer-0-primitives/json-round-trip-helper.test.js +110 -0
  1040. package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +159 -0
  1041. package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
  1042. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +118 -0
  1043. package/lib/vendor/blamejs/test/layer-0-primitives/local-db-thin.test.js +150 -0
  1044. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-cloudwatch.test.js +489 -0
  1045. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp-grpc.test.js +207 -0
  1046. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp.test.js +283 -0
  1047. package/lib/vendor/blamejs/test/layer-0-primitives/lro.test.js +65 -0
  1048. package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +417 -0
  1049. package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +208 -0
  1050. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +910 -0
  1051. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +502 -0
  1052. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +680 -0
  1053. package/lib/vendor/blamejs/test/layer-0-primitives/mail-canspam.test.js +128 -0
  1054. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +149 -0
  1055. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +323 -0
  1056. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +297 -0
  1057. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +514 -0
  1058. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +369 -0
  1059. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +199 -0
  1060. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +627 -0
  1061. package/lib/vendor/blamejs/test/layer-0-primitives/mail-feedback-id.test.js +56 -0
  1062. package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +217 -0
  1063. package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +283 -0
  1064. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +217 -0
  1065. package/lib/vendor/blamejs/test/layer-0-primitives/mail-mdn.test.js +334 -0
  1066. package/lib/vendor/blamejs/test/layer-0-primitives/mail-rbl.test.js +271 -0
  1067. package/lib/vendor/blamejs/test/layer-0-primitives/mail-require-tls.test.js +128 -0
  1068. package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +215 -0
  1069. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +336 -0
  1070. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +732 -0
  1071. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +840 -0
  1072. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +130 -0
  1073. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +285 -0
  1074. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +74 -0
  1075. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +112 -0
  1076. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +229 -0
  1077. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +394 -0
  1078. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +147 -0
  1079. package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +151 -0
  1080. package/lib/vendor/blamejs/test/layer-0-primitives/mail-spam-score.test.js +204 -0
  1081. package/lib/vendor/blamejs/test/layer-0-primitives/mail-srs.test.js +152 -0
  1082. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +279 -0
  1083. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +323 -0
  1084. package/lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js +165 -0
  1085. package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +439 -0
  1086. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js +202 -0
  1087. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +155 -0
  1088. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js +112 -0
  1089. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +224 -0
  1090. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js +278 -0
  1091. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +376 -0
  1092. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js +89 -0
  1093. package/lib/vendor/blamejs/test/layer-0-primitives/nel.test.js +200 -0
  1094. package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +106 -0
  1095. package/lib/vendor/blamejs/test/layer-0-primitives/network-byte-quota.test.js +133 -0
  1096. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +372 -0
  1097. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +635 -0
  1098. package/lib/vendor/blamejs/test/layer-0-primitives/network-heartbeat-passive.test.js +128 -0
  1099. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-build-options.test.js +130 -0
  1100. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-ct-inclusion.test.js +179 -0
  1101. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +447 -0
  1102. package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +369 -0
  1103. package/lib/vendor/blamejs/test/layer-0-primitives/nis2-report.test.js +21 -0
  1104. package/lib/vendor/blamejs/test/layer-0-primitives/nist-crosswalk.test.js +42 -0
  1105. package/lib/vendor/blamejs/test/layer-0-primitives/no-cache.test.js +98 -0
  1106. package/lib/vendor/blamejs/test/layer-0-primitives/notify.test.js +707 -0
  1107. package/lib/vendor/blamejs/test/layer-0-primitives/numeric-bounds.test.js +142 -0
  1108. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +72 -0
  1109. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +597 -0
  1110. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +190 -0
  1111. package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +877 -0
  1112. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +257 -0
  1113. package/lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js +522 -0
  1114. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +216 -0
  1115. package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +324 -0
  1116. package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +546 -0
  1117. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +153 -0
  1118. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-software.test.js +94 -0
  1119. package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +195 -0
  1120. package/lib/vendor/blamejs/test/layer-0-primitives/process-spawn.test.js +62 -0
  1121. package/lib/vendor/blamejs/test/layer-0-primitives/promise-pool.test.js +93 -0
  1122. package/lib/vendor/blamejs/test/layer-0-primitives/protected-resource-metadata.test.js +68 -0
  1123. package/lib/vendor/blamejs/test/layer-0-primitives/protobuf-encoder.test.js +138 -0
  1124. package/lib/vendor/blamejs/test/layer-0-primitives/protocol-dispatcher.test.js +174 -0
  1125. package/lib/vendor/blamejs/test/layer-0-primitives/public-suffix.test.js +197 -0
  1126. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +232 -0
  1127. package/lib/vendor/blamejs/test/layer-0-primitives/queue-dlq-extend-lease.test.js +178 -0
  1128. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +322 -0
  1129. package/lib/vendor/blamejs/test/layer-0-primitives/queue-priority-rate-progress.test.js +266 -0
  1130. package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +300 -0
  1131. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +338 -0
  1132. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-registry.test.js +75 -0
  1133. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +246 -0
  1134. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +130 -0
  1135. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +335 -0
  1136. package/lib/vendor/blamejs/test/layer-0-primitives/request-log.test.js +170 -0
  1137. package/lib/vendor/blamejs/test/layer-0-primitives/require-auth-cache-control.test.js +93 -0
  1138. package/lib/vendor/blamejs/test/layer-0-primitives/require-mtls.test.js +34 -0
  1139. package/lib/vendor/blamejs/test/layer-0-primitives/resource-access-lock.test.js +52 -0
  1140. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +67 -0
  1141. package/lib/vendor/blamejs/test/layer-0-primitives/retry.test.js +535 -0
  1142. package/lib/vendor/blamejs/test/layer-0-primitives/router-cross-origin-redirect.test.js +0 -0
  1143. package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +128 -0
  1144. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +163 -0
  1145. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-parallel.test.js +170 -0
  1146. package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +248 -0
  1147. package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +451 -0
  1148. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +289 -0
  1149. package/lib/vendor/blamejs/test/layer-0-primitives/safe-icap.test.js +206 -0
  1150. package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +104 -0
  1151. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +339 -0
  1152. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mount-info.test.js +180 -0
  1153. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +78 -0
  1154. package/lib/vendor/blamejs/test/layer-0-primitives/safe-sieve.test.js +123 -0
  1155. package/lib/vendor/blamejs/test/layer-0-primitives/safe-smtp.test.js +95 -0
  1156. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-idn-homograph.test.js +77 -0
  1157. package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +257 -0
  1158. package/lib/vendor/blamejs/test/layer-0-primitives/saml-slo.test.js +249 -0
  1159. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +228 -0
  1160. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +238 -0
  1161. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +92 -0
  1162. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +700 -0
  1163. package/lib/vendor/blamejs/test/layer-0-primitives/sd-notify.test.js +67 -0
  1164. package/lib/vendor/blamejs/test/layer-0-primitives/sec-cyber.test.js +85 -0
  1165. package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +107 -0
  1166. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +175 -0
  1167. package/lib/vendor/blamejs/test/layer-0-primitives/seeders.test.js +816 -0
  1168. package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier.test.js +168 -0
  1169. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +302 -0
  1170. package/lib/vendor/blamejs/test/layer-0-primitives/server-timing.test.js +93 -0
  1171. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +247 -0
  1172. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +295 -0
  1173. package/lib/vendor/blamejs/test/layer-0-primitives/shape-match.test.js +142 -0
  1174. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +952 -0
  1175. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-multipart-sse.test.js +441 -0
  1176. package/lib/vendor/blamejs/test/layer-0-primitives/slug.test.js +330 -0
  1177. package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +233 -0
  1178. package/lib/vendor/blamejs/test/layer-0-primitives/source-comment-blocks.test.js +105 -0
  1179. package/lib/vendor/blamejs/test/layer-0-primitives/speculation-rules.test.js +319 -0
  1180. package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +148 -0
  1181. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +283 -0
  1182. package/lib/vendor/blamejs/test/layer-0-primitives/standard-webhooks.test.js +67 -0
  1183. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +266 -0
  1184. package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +487 -0
  1185. package/lib/vendor/blamejs/test/layer-0-primitives/storage-chunk-scratch.test.js +0 -0
  1186. package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +773 -0
  1187. package/lib/vendor/blamejs/test/layer-0-primitives/stream-throttle.test.js +173 -0
  1188. package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +180 -0
  1189. package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +66 -0
  1190. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +89 -0
  1191. package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +571 -0
  1192. package/lib/vendor/blamejs/test/layer-0-primitives/test-harness.test.js +190 -0
  1193. package/lib/vendor/blamejs/test/layer-0-primitives/testing-request.test.js +119 -0
  1194. package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +522 -0
  1195. package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +151 -0
  1196. package/lib/vendor/blamejs/test/layer-0-primitives/tls-exporter.test.js +168 -0
  1197. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-ct.test.js +275 -0
  1198. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-verify.test.js +105 -0
  1199. package/lib/vendor/blamejs/test/layer-0-primitives/tls-pinset-drift.test.js +35 -0
  1200. package/lib/vendor/blamejs/test/layer-0-primitives/tls-preferred-groups.test.js +81 -0
  1201. package/lib/vendor/blamejs/test/layer-0-primitives/tracing.test.js +280 -0
  1202. package/lib/vendor/blamejs/test/layer-0-primitives/uuid.test.js +93 -0
  1203. package/lib/vendor/blamejs/test/layer-0-primitives/vault-aad.test.js +277 -0
  1204. package/lib/vendor/blamejs/test/layer-0-primitives/vault-seal-pem-file.test.js +252 -0
  1205. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-data.test.js +149 -0
  1206. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-manifest.test.js +92 -0
  1207. package/lib/vendor/blamejs/test/layer-0-primitives/vex.test.js +661 -0
  1208. package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +308 -0
  1209. package/lib/vendor/blamejs/test/layer-0-primitives/web-push-vapid.test.js +144 -0
  1210. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +674 -0
  1211. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +360 -0
  1212. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool.test.js +302 -0
  1213. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +349 -0
  1214. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +717 -0
  1215. package/lib/vendor/blamejs/test/layer-5-integration/bundler-output.test.js +444 -0
  1216. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +597 -0
  1217. package/lib/vendor/blamejs/test/layer-5-integration/security-chaos.test.js +308 -0
  1218. package/lib/vendor/blamejs/test/smoke.js +431 -0
  1219. package/lib/webhooks.js +305 -0
  1220. package/package.json +43 -0
@@ -0,0 +1,3111 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.db
4
+ * @featured true
5
+ * @nav Data
6
+ * @title Db
7
+ *
8
+ * @intro
9
+ * Database core — SQLite (node:sqlite) wrapped in encrypted-at-rest
10
+ * storage, sealed-column field-level crypto, append-only audit-chain
11
+ * integration, declarative schema reconcile, and run-once
12
+ * migrations. Default at-rest posture is `encrypted`: the live `.db`
13
+ * lives in tmpfs (/dev/shm), is decrypted from `<dataDir>/db.enc` at
14
+ * boot, periodically re-encrypted every five minutes, and re-
15
+ * encrypted again at shutdown. The DB encryption key is sealed by
16
+ * `b.vault` at `<dataDir>/db.key.enc`. Operators who want a plain
17
+ * on-disk SQLite file pass `atRest: "plain"` and accept a boot
18
+ * warning — sealed columns still protect PII, but schema and row
19
+ * counts are visible to a forensic disk image.
20
+ *
21
+ * Beyond the storage shell, the module owns the framework's data
22
+ * contract: `audit_log` / `consent_log` / `audit_checkpoints` and
23
+ * the `_blamejs_*` reserved tables are provisioned before any
24
+ * operator schema reconciles, append-only triggers refuse
25
+ * UPDATE/DELETE on the chain tables, and boot refuses to continue
26
+ * on chain breakage, checkpoint signature failure, audit-log
27
+ * rollback, or PRAGMA integrity_check corruption. WORM
28
+ * declarations (`declareWorm`) and dual-control gates
29
+ * (`declareRequireDualControl`) layer SEC 17a-4(f) / FINRA 4511 /
30
+ * 21 CFR Part 11 §11.10(c) record-preservation invariants on
31
+ * operator tables.
32
+ *
33
+ * The query surface is `db.from(table)` (chainable), `db.prepare`
34
+ * (LRU-cached node:sqlite Statement), `db.stream` (object-mode
35
+ * Readable for million-row exports with auto-unseal), and
36
+ * `db.transaction` (BEGIN/COMMIT/ROLLBACK around a callback).
37
+ * Postgres-only declarative migrations (`declareView` /
38
+ * `declareRowPolicy`) emit migration-shape objects consumed by
39
+ * `b.externalDb.migrate`.
40
+ *
41
+ * @card
42
+ * Database core — SQLite (node:sqlite) wrapped in encrypted-at-rest storage, sealed-column field-level crypto, append-only audit-chain integration, declarative schema reconcile, and run-once migrations.
43
+ */
44
+ var nodeFs = require("node:fs");
45
+ var nodePath = require("node:path");
46
+ var { DatabaseSync } = require("node:sqlite");
47
+ var { Readable } = require("node:stream");
48
+ var atomicFile = require("./atomic-file");
49
+ var audit = require("./audit");
50
+ var auditSign = require("./audit-sign");
51
+ var cluster = require("./cluster");
52
+ var csv = require("./csv");
53
+ var events = require("./events");
54
+ var consent = require("./consent");
55
+ var C = require("./constants");
56
+ var { generateToken, generateBytes, encryptPacked, decryptPacked, sha3Hash } = require("./crypto");
57
+ var cryptoField = require("./crypto-field");
58
+ var dbDeclareRowPolicy = require("./db-declare-row-policy");
59
+ var dbDeclareView = require("./db-declare-view");
60
+ var { Query } = require("./db-query");
61
+ var dbSchema = require("./db-schema");
62
+ var { defineClass } = require("./framework-error");
63
+ var { boot } = require("./log");
64
+ var lazyRequire = require("./lazy-require");
65
+ var observability = require("./observability");
66
+ var ntpCheck = lazyRequire(function () { return require("./ntp-check"); });
67
+ var safeAsync = require("./safe-async");
68
+ var safeEnv = require("./parsers/safe-env");
69
+ var safeJson = require("./safe-json");
70
+ var safeSql = require("./safe-sql");
71
+ var validateOpts = require("./validate-opts");
72
+ var vault = require("./vault");
73
+
74
+ var DbError = defineClass("DbError", { alwaysPermanent: true });
75
+ var WormViolationError = require("./framework-error").WormViolationError;
76
+ var _wormErr = WormViolationError.factory;
77
+
78
+ // Lazy: compliance and dual-control read state at runtime; both are
79
+ // non-load-time deps so a top-of-file require would not cycle, but
80
+ // they're only needed on declareWorm / declareRequireDualControl /
81
+ // eraseHard. Lazy keeps the load graph minimal.
82
+ var compliance = lazyRequire(function () { return require("./compliance"); });
83
+
84
+ // Postures that REQUIRE row-level WORM on operator-named business-
85
+ // record tables. Audit_log / consent_log / audit_checkpoints are
86
+ // already WORM-by-default; this set covers operator tables.
87
+ // sec-17a-4 — SEC Rule 17a-4(f) broker-dealer record preservation
88
+ // finra-4511 — FINRA Rule 4511 books-and-records
89
+ // fda-21cfr11 — 21 CFR Part 11 §11.10(c) protect record integrity
90
+ var WORM_POSTURES = Object.freeze(["sec-17a-4", "finra-4511", "fda-21cfr11"]);
91
+ var _dbErr = DbError.factory;
92
+
93
+ // Lazy: cluster-storage's _localDb pulls db back in, so eager require
94
+ // would deadlock the load order. cluster-storage is only used on the
95
+ // purge-audit-chain external-db nodePath, which always runs after init.
96
+ var clusterStorage = lazyRequire(function () { return require("./cluster-storage"); });
97
+
98
+ // Lazy refs for the test-reset cascade. Each module requires db.js
99
+ // directly or transitively (audit/consent/subject/session/etc. all
100
+ // own a sealed-column slice that depends on db.from), so eager
101
+ // requires here would cycle on load. The cascade runs only when a
102
+ // test explicitly resets db, so paying the resolve cost lazily is
103
+ // the correct tradeoff.
104
+ var _resetAudit = lazyRequire(function () { return require("./audit"); });
105
+ var _resetConsent = lazyRequire(function () { return require("./consent"); });
106
+ var _resetSubject = lazyRequire(function () { return require("./subject"); });
107
+ var _resetSession = lazyRequire(function () { return require("./session"); });
108
+ var _resetStorage = lazyRequire(function () { return require("./storage"); });
109
+ var _resetAuditSign = lazyRequire(function () { return require("./audit-sign"); });
110
+ var _resetQueue = lazyRequire(function () { return require("./queue"); });
111
+ var _resetBreakGlass = lazyRequire(function () { return require("./break-glass"); });
112
+ var _resetLogStream = lazyRequire(function () { return require("./log-stream"); });
113
+ var _resetRedact = lazyRequire(function () { return require("./redact"); });
114
+ var _resetExternalDb = lazyRequire(function () { return require("./external-db"); });
115
+
116
+ var AUDIT_TIP_SCHEMA = {
117
+ type: "object",
118
+ required: ["atMonotonicCounter"],
119
+ properties: {
120
+ atMonotonicCounter: { type: "number" },
121
+ rowHash: { type: "string" },
122
+ signedAt: { type: "string" },
123
+ },
124
+ };
125
+
126
+ var runSql = dbSchema.runSql;
127
+
128
+ // Module-local state, populated by init()
129
+ var database = null; // the SQLite handle
130
+ var dbPath = null; // plaintext DB file path (tmpfs in encrypted mode, dataDir/db in plain mode)
131
+ var encPath = null; // encrypted-at-rest path (null in plain mode)
132
+ var encKey = null; // DB encryption key buffer (null in plain mode)
133
+ var encTimer = null; // periodic encrypt interval handle
134
+ var atRest = null; // 'encrypted' or 'plain'
135
+ var dataDir = null;
136
+ var initialized = false;
137
+ var dataResidency = null; // operator's declared region config (validated by storage backends)
138
+ var subjectTables = []; // [{ name, subjectField, personalDataCategories }] — for subject.export/erase
139
+ var tableMetadata = {}; // table name → metadata snapshot (PK/FK/sealed/derived) for getTableMetadata
140
+ // D-M5 — streamLimit ceiling. db.stream() / Query.stream() consult this
141
+ // (overridden per-call via opts.streamLimit). Default cap matches a
142
+ // generous-but-bounded 1M rows so an accidentally-unbounded export
143
+ // surfaces a thrown error instead of OOM. v0.7.67's maxRowsPerQuery
144
+ // bounds .all() / .first() — this is its streaming counterpart.
145
+ var streamLimit = C.BYTES.bytes(1000000); // allow:raw-byte-literal — row-count ceiling, not bytes
146
+
147
+ // ---- Framework-baked tables ----
148
+ //
149
+ // audit_log + consent_log + _blamejs_subject_restrictions + _blamejs_subject_erasures
150
+ // are provisioned by the framework before app schema reconciles. Apps cannot
151
+ // opt out, override, or rename them. An app schema entry colliding with any of
152
+ // these names is refused at init.
153
+ var RESERVED_TABLE_NAMES = new Set([
154
+ "audit_log",
155
+ "audit_checkpoints",
156
+ "consent_log",
157
+ "_blamejs_subject_restrictions",
158
+ "_blamejs_subject_erasures",
159
+ "_blamejs_sessions",
160
+ "_blamejs_jobs",
161
+ "_blamejs_migrations",
162
+ "_blamejs_counters",
163
+ "_blamejs_audit_purge_anchor",
164
+ "_blamejs_scheduler_ticks",
165
+ "_blamejs_rate_limit_counters",
166
+ "_blamejs_pubsub_messages",
167
+ "_blamejs_api_encrypt_nonces",
168
+ "_blamejs_api_keys",
169
+ "_blamejs_cache",
170
+ "_blamejs_seeders",
171
+ "_blamejs_seeders_lock",
172
+ "_blamejs_break_glass_policies",
173
+ "_blamejs_break_glass_grants",
174
+ ]);
175
+
176
+ var FRAMEWORK_SCHEMA = [
177
+ {
178
+ name: "audit_log",
179
+ columns: {
180
+ _id: "TEXT PRIMARY KEY",
181
+ recordedAt: "INTEGER NOT NULL",
182
+ monotonicCounter: "INTEGER NOT NULL",
183
+ actorUserId: "TEXT",
184
+ actorUserIdHash: "TEXT",
185
+ actorIp: "TEXT",
186
+ actorUserAgent: "TEXT",
187
+ actorSessionId: "TEXT",
188
+ action: "TEXT NOT NULL",
189
+ resourceKind: "TEXT",
190
+ resourceId: "TEXT",
191
+ resourceIdHash: "TEXT",
192
+ outcome: "TEXT NOT NULL",
193
+ reason: "TEXT",
194
+ metadata: "TEXT",
195
+ requestId: "TEXT",
196
+ prevHash: "TEXT NOT NULL",
197
+ rowHash: "TEXT NOT NULL",
198
+ nonce: "BLOB NOT NULL",
199
+ fencingToken: "INTEGER NOT NULL DEFAULT 0",
200
+ },
201
+ indexes: [
202
+ "actorUserIdHash", "resourceIdHash", "recordedAt", "action",
203
+ { name: "idx_audit_monotonic", columns: "monotonicCounter", unique: true },
204
+ ],
205
+ sealedFields: ["actorUserId", "actorIp", "actorUserAgent", "actorSessionId", "resourceId", "reason", "metadata"],
206
+ derivedHashes: {
207
+ actorUserIdHash: { from: "actorUserId" },
208
+ resourceIdHash: { from: "resourceId" },
209
+ },
210
+ },
211
+ {
212
+ name: "consent_log",
213
+ columns: {
214
+ _id: "TEXT PRIMARY KEY",
215
+ recordedAt: "INTEGER NOT NULL",
216
+ monotonicCounter: "INTEGER NOT NULL",
217
+ subjectId: "TEXT NOT NULL",
218
+ subjectIdHash: "TEXT NOT NULL",
219
+ purpose: "TEXT NOT NULL",
220
+ lawfulBasis: "TEXT NOT NULL",
221
+ action: "TEXT NOT NULL",
222
+ scope: "TEXT",
223
+ channel: "TEXT NOT NULL",
224
+ evidenceRef: "TEXT",
225
+ prevHash: "TEXT NOT NULL",
226
+ rowHash: "TEXT NOT NULL",
227
+ nonce: "BLOB NOT NULL",
228
+ fencingToken: "INTEGER NOT NULL DEFAULT 0",
229
+ },
230
+ indexes: [
231
+ "subjectIdHash", "recordedAt", "purpose",
232
+ { name: "idx_consent_monotonic", columns: "monotonicCounter", unique: true },
233
+ ],
234
+ sealedFields: ["subjectId", "scope", "evidenceRef"],
235
+ derivedHashes: {
236
+ subjectIdHash: { from: "subjectId" },
237
+ },
238
+ },
239
+ {
240
+ name: "_blamejs_subject_restrictions",
241
+ columns: {
242
+ subjectIdHash: "TEXT PRIMARY KEY",
243
+ since: "INTEGER NOT NULL",
244
+ reason: "TEXT",
245
+ },
246
+ sealedFields: ["reason"],
247
+ },
248
+ {
249
+ name: "_blamejs_subject_erasures",
250
+ columns: {
251
+ subjectIdHash: "TEXT PRIMARY KEY",
252
+ erasedAt: "INTEGER NOT NULL",
253
+ },
254
+ },
255
+ {
256
+ // Subject-level legal hold registry. Operators register a hold
257
+ // via b.legalHold.place(subjectId, ...) — b.subject.erase and
258
+ // b.retention consult b.legalHold.isHeld(subjectId) before
259
+ // accepting any deletion. Per FRCP Rule 26/37(e), GDPR Art
260
+ // 17(3)(e), SEC Rule 17a-4, HIPAA §164.530(j)(2).
261
+ name: "_blamejs_legal_hold",
262
+ columns: {
263
+ subjectIdHash: "TEXT PRIMARY KEY",
264
+ placedAt: "INTEGER NOT NULL",
265
+ placedBy: "TEXT",
266
+ reason: "TEXT NOT NULL",
267
+ custodian: "TEXT",
268
+ citation: "TEXT",
269
+ retainUntil: "INTEGER",
270
+ },
271
+ indexes: ["placedAt"],
272
+ },
273
+ {
274
+ // Per-row crypto-erasure key registry — F-RTBF-3 per-row keys.
275
+ // Each entry holds a sealed wrapped K_row keyed by (table,
276
+ // rowId). b.subject.eraseHard deletes the entry, leaving WAL /
277
+ // replica residuals undecryptable.
278
+ name: "_blamejs_per_row_keys",
279
+ columns: {
280
+ tableName: "TEXT NOT NULL",
281
+ rowId: "TEXT NOT NULL",
282
+ wrappedKey: "BLOB NOT NULL",
283
+ createdAt: "INTEGER NOT NULL",
284
+ },
285
+ primaryKey: ["tableName", "rowId"],
286
+ indexes: [],
287
+ },
288
+ {
289
+ // Operator-declared WORM (write-once-read-many) registry. Each
290
+ // entry pairs an operator-named table with the posture that
291
+ // demanded the WORM declaration; boot-time assertions iterate
292
+ // this registry to verify triggers are installed under the
293
+ // current b.compliance.current() posture.
294
+ name: "_blamejs_worm_tables",
295
+ columns: {
296
+ tableName: "TEXT PRIMARY KEY",
297
+ posture: "TEXT",
298
+ declaredAt: "INTEGER NOT NULL",
299
+ },
300
+ },
301
+ {
302
+ // Operator-declared dual-control gate registry. b.db.delete /
303
+ // b.subject.erase / b.audit.purge consult this table on
304
+ // destructive ops; under the named posture the framework refuses
305
+ // execution unless the caller passes a consumed dual-control
306
+ // grant.
307
+ name: "_blamejs_dual_control_gates",
308
+ columns: {
309
+ tableName: "TEXT PRIMARY KEY",
310
+ posture: "TEXT",
311
+ m: "INTEGER NOT NULL",
312
+ n: "INTEGER NOT NULL",
313
+ declaredAt:"INTEGER NOT NULL",
314
+ },
315
+ },
316
+ {
317
+ name: "audit_checkpoints",
318
+ columns: {
319
+ _id: "TEXT PRIMARY KEY",
320
+ createdAt: "INTEGER NOT NULL",
321
+ atMonotonicCounter: "INTEGER NOT NULL",
322
+ atRowHash: "TEXT NOT NULL",
323
+ signature: "BLOB NOT NULL",
324
+ publicKeyFingerprint: "TEXT NOT NULL",
325
+ fencingToken: "INTEGER NOT NULL DEFAULT 0",
326
+ },
327
+ indexes: [
328
+ "createdAt",
329
+ { name: "idx_chkpt_counter", columns: "atMonotonicCounter", unique: true },
330
+ ],
331
+ sealedFields: [],
332
+ },
333
+ {
334
+ name: "_blamejs_audit_purge_anchor",
335
+ columns: {
336
+ // CHECK constraint: scope is one of the framework's audit-
337
+ // chain anchor scopes (`audit` / `consent`). Pre-v0.8.37 a
338
+ // typo silently created a parallel anchor; the chain verifier
339
+ // walked the wrong anchor and missed tampering. The CHECK
340
+ // refuses unknown scope strings at INSERT time.
341
+ scope: "TEXT PRIMARY KEY CHECK (scope IN ('audit', 'consent'))",
342
+ lastPurgedCounter: "INTEGER NOT NULL",
343
+ lastPurgedRowHash: "TEXT NOT NULL",
344
+ archiveBundleId: "TEXT NOT NULL",
345
+ purgedAt: "INTEGER NOT NULL",
346
+ },
347
+ sealedFields: [],
348
+ },
349
+ {
350
+ // Scheduler exactly-once-globally claim table. Each fire claims a
351
+ // (name, scheduledAtUnix) row before dispatching; UNIQUE on the
352
+ // composite tickKey (name + ":" + scheduledAtUnix) means a concurrent
353
+ // leader's INSERT loses with a constraint violation, and that node
354
+ // skips the tick. Closes the once-globally gap during cluster
355
+ // leader hand-offs where two leaders briefly coexist.
356
+ name: "_blamejs_scheduler_ticks",
357
+ columns: {
358
+ tickKey: "TEXT PRIMARY KEY",
359
+ name: "TEXT NOT NULL",
360
+ scheduledAtUnix: "INTEGER NOT NULL",
361
+ claimedAtUnix: "INTEGER NOT NULL",
362
+ claimedBy: "TEXT",
363
+ },
364
+ indexes: ["scheduledAtUnix"],
365
+ sealedFields: [],
366
+ },
367
+ {
368
+ // _blamejs_rate_limit_counters — fixed-window counter table for
369
+ // the cluster-shared rate-limit backend. One row per (key); the
370
+ // count rolls over atomically when the windowStart advances. Used
371
+ // by lib/middleware/rate-limit.js when scope: 'cluster' is set.
372
+ name: "_blamejs_rate_limit_counters",
373
+ columns: {
374
+ key: "TEXT PRIMARY KEY",
375
+ windowStart: "INTEGER NOT NULL",
376
+ count: "INTEGER NOT NULL DEFAULT 0",
377
+ },
378
+ indexes: ["windowStart"],
379
+ sealedFields: [],
380
+ },
381
+ {
382
+ // _blamejs_pubsub_messages — cluster fan-out for `b.pubsub` (the
383
+ // generalization of the previous WebSocket-specific table). Any
384
+ // pubsub instance using the `cluster` backend writes a row on
385
+ // publish; other nodes poll for new ids and dispatch to their
386
+ // local subscribers. Rows older than the configured retention
387
+ // window are pruned by the backend on a rate-limited basis.
388
+ name: "_blamejs_pubsub_messages",
389
+ columns: {
390
+ id: "INTEGER PRIMARY KEY AUTOINCREMENT",
391
+ topic: "TEXT NOT NULL",
392
+ payload: "TEXT NOT NULL",
393
+ publishedAt: "INTEGER NOT NULL",
394
+ publishedBy: "TEXT NOT NULL",
395
+ },
396
+ indexes: ["publishedAt"],
397
+ sealedFields: [],
398
+ },
399
+ {
400
+ // _blamejs_api_encrypt_nonces — replay-protection store for the
401
+ // api-encrypt middleware. The middleware hashes the client-supplied
402
+ // nonce via SHA3 before insert so a leaked DB / table dump never
403
+ // exposes the original 16-byte client nonces. Hashing is
404
+ // deterministic so the PRIMARY KEY conflict is what catches a
405
+ // replay attempt within the replay window.
406
+ name: "_blamejs_api_encrypt_nonces",
407
+ columns: {
408
+ nonceHash: "TEXT PRIMARY KEY",
409
+ expireAt: "INTEGER NOT NULL",
410
+ },
411
+ indexes: ["expireAt"],
412
+ sealedFields: [],
413
+ },
414
+ {
415
+ name: "_blamejs_sessions",
416
+ columns: {
417
+ sidHash: "TEXT PRIMARY KEY",
418
+ userId: "TEXT NOT NULL",
419
+ userIdHash: "TEXT NOT NULL",
420
+ data: "TEXT",
421
+ createdAt: "INTEGER NOT NULL",
422
+ expiresAt: "INTEGER NOT NULL",
423
+ lastActivity: "INTEGER NOT NULL",
424
+ },
425
+ indexes: ["userIdHash", "expiresAt"],
426
+ sealedFields: ["userId", "data"],
427
+ derivedHashes: { userIdHash: { from: "userId" } },
428
+ },
429
+ {
430
+ // _blamejs_api_keys — operator-facing API-key registry. Sealed
431
+ // columns: ownerId / scopes / metadata. The secret never lands
432
+ // here — only its SHA3-512 hash, constant-time-compared on
433
+ // verify. Same dual-storage pattern as sessions: this row mirrors
434
+ // the cluster-mode DDL in framework-schema.js so cluster-storage
435
+ // can route to either backend transparently.
436
+ name: "_blamejs_api_keys",
437
+ columns: {
438
+ id: "TEXT PRIMARY KEY",
439
+ namespace: "TEXT NOT NULL",
440
+ ownerId: "TEXT NOT NULL",
441
+ ownerIdHash: "TEXT NOT NULL",
442
+ secretHash: "TEXT NOT NULL",
443
+ // secondarySecretHash + secondaryExpiresAt support graceful key
444
+ // rotation: when rotate({ gracePeriodMs }) is called the old hash
445
+ // is preserved here and the new hash takes the primary slot. Both
446
+ // verify successfully until secondaryExpiresAt, then the old slot
447
+ // is implicitly retired.
448
+ secondarySecretHash: "TEXT",
449
+ secondaryExpiresAt: "INTEGER",
450
+ scopes: "TEXT",
451
+ metadata: "TEXT",
452
+ createdAt: "INTEGER NOT NULL",
453
+ expiresAt: "INTEGER",
454
+ revokedAt: "INTEGER",
455
+ lastUsedAt: "INTEGER",
456
+ prefix: "TEXT NOT NULL",
457
+ },
458
+ indexes: [
459
+ "ownerIdHash",
460
+ { name: "idx_api_keys_namespace_owner", columns: ["namespace", "ownerIdHash"] },
461
+ "expiresAt",
462
+ ],
463
+ sealedFields: ["ownerId", "scopes", "metadata"],
464
+ derivedHashes: { ownerIdHash: { from: "ownerId" } },
465
+ },
466
+ {
467
+ name: "_blamejs_jobs",
468
+ columns: {
469
+ _id: "TEXT PRIMARY KEY",
470
+ queueName: "TEXT NOT NULL",
471
+ payload: "TEXT",
472
+ status: "TEXT NOT NULL",
473
+ enqueuedAt: "INTEGER NOT NULL",
474
+ availableAt: "INTEGER NOT NULL",
475
+ leasedAt: "INTEGER",
476
+ leaseExpiresAt: "INTEGER",
477
+ attempts: "INTEGER NOT NULL DEFAULT 0",
478
+ maxAttempts: "INTEGER NOT NULL DEFAULT 5",
479
+ lastError: "TEXT",
480
+ finishedAt: "INTEGER",
481
+ traceId: "TEXT",
482
+ classification: "TEXT",
483
+ priority: "INTEGER NOT NULL DEFAULT 0",
484
+ // Repeat-in-queue: cron-shaped recurring jobs re-enqueue themselves
485
+ // after each successful completion. NULL = one-shot (no repeat).
486
+ repeatCron: "TEXT",
487
+ repeatTimezone: "TEXT",
488
+ // Flows: parent-child job graphs with dependency edges.
489
+ // flowId groups jobs in the same flow; dependsOn is a JSON array
490
+ // of jobIds this row waits for; flowChildName is the human-readable
491
+ // label inside the flow used by dependsOn resolution.
492
+ flowId: "TEXT",
493
+ flowChildName: "TEXT",
494
+ dependsOn: "TEXT",
495
+ },
496
+ indexes: [
497
+ { name: "idx_jobs_lease", columns: ["queueName", "status", "availableAt"] },
498
+ { name: "idx_jobs_priority", columns: ["queueName", "status", "priority", "availableAt"] },
499
+ { name: "idx_jobs_flow", columns: ["flowId"] },
500
+ "leaseExpiresAt",
501
+ "finishedAt",
502
+ ],
503
+ sealedFields: ["payload", "lastError"],
504
+ },
505
+ {
506
+ // _blamejs_cache — operator-facing cache primitive's cluster backend
507
+ // (lib/cache.js). Mirrors the cluster-mode DDL in framework-schema.js.
508
+ // PRIMARY KEY is the composite "<namespace>:<key>"; valueJson is
509
+ // JSON-serialized; expiresAt is unix-ms (Number.MAX_SAFE_INTEGER for
510
+ // never-expiring entries). Not sealed: cache values are operator-
511
+ // chosen application data, the operator decides what's worth storing.
512
+ name: "_blamejs_cache",
513
+ columns: {
514
+ cacheKey: "TEXT PRIMARY KEY",
515
+ valueJson: "TEXT NOT NULL",
516
+ expiresAt: "INTEGER NOT NULL",
517
+ updatedAt: "INTEGER NOT NULL",
518
+ },
519
+ indexes: ["expiresAt"],
520
+ sealedFields: [],
521
+ },
522
+ {
523
+ // _blamejs_cache_tags — junction table for tag→cacheKey lookup
524
+ // backing b.cache.invalidateTag(t) on the cluster backend. Composite
525
+ // PK (cacheKey, tag) lets one cacheKey carry many tags; index on
526
+ // tag makes invalidation a single indexed scan. Cleared together
527
+ // with the matching _blamejs_cache rows on del / clear / sweep.
528
+ name: "_blamejs_cache_tags",
529
+ columns: {
530
+ cacheKey: "TEXT NOT NULL",
531
+ tag: "TEXT NOT NULL",
532
+ },
533
+ primaryKey: ["cacheKey", "tag"],
534
+ indexes: ["tag"],
535
+ sealedFields: [],
536
+ },
537
+ {
538
+ // _blamejs_seeders — registry of applied seed files for the
539
+ // b.seeders primitive (lib/seeders.js). Composite PK (env, name)
540
+ // means the same filename can apply per env (dev fixtures don't
541
+ // collide with prod fixtures by name). rerunnable=1 entries get
542
+ // their appliedAt updated in place on every run; non-rerunnable
543
+ // entries are insert-once.
544
+ name: "_blamejs_seeders",
545
+ columns: {
546
+ env: "TEXT NOT NULL",
547
+ name: "TEXT NOT NULL",
548
+ description: "TEXT",
549
+ appliedAt: "TEXT NOT NULL",
550
+ rerunnable: "INTEGER NOT NULL DEFAULT 0",
551
+ },
552
+ primaryKey: ["env", "name"],
553
+ indexes: [],
554
+ sealedFields: [],
555
+ },
556
+ {
557
+ // _blamejs_seeders_lock — single-row advisory lock for the seeders
558
+ // runner. Same shape as _blamejs_migrations_lock (CHECK constraint
559
+ // on scope='lock' enforces single row). Two processes calling
560
+ // `seed run` against the same DB race on this PK; loser sees a
561
+ // clear "lock held" error.
562
+ name: "_blamejs_seeders_lock",
563
+ columns: {
564
+ scope: "TEXT PRIMARY KEY CHECK (scope = 'lock')",
565
+ lockedAt: "INTEGER NOT NULL",
566
+ lockedBy: "TEXT NOT NULL",
567
+ },
568
+ sealedFields: [],
569
+ },
570
+ {
571
+ // _blamejs_break_glass_policies — column-level break-glass policy
572
+ // registry. One row per (table) declares which columns are
573
+ // glass-locked and what the operator's grant rules are. Sealed
574
+ // columns hold the column-list, factor-list, and bypass config so
575
+ // policy contents aren't browsable in cleartext.
576
+ name: "_blamejs_break_glass_policies",
577
+ columns: {
578
+ tableName: "TEXT PRIMARY KEY",
579
+ columnsJson: "TEXT NOT NULL",
580
+ factorsJson: "TEXT NOT NULL",
581
+ cryptographic: "INTEGER NOT NULL DEFAULT 0",
582
+ grantTtlMs: "INTEGER NOT NULL",
583
+ maxRowsPerGrant: "INTEGER NOT NULL DEFAULT 1",
584
+ reasonRequired: "INTEGER NOT NULL DEFAULT 1",
585
+ reasonMinLength: "INTEGER NOT NULL DEFAULT 12",
586
+ pinIp: "INTEGER NOT NULL DEFAULT 1",
587
+ sessionPin: "INTEGER NOT NULL DEFAULT 1",
588
+ onLockedAccess: "TEXT NOT NULL DEFAULT 'throw'",
589
+ requireScope: "TEXT",
590
+ serviceAccountBypassJson: "TEXT",
591
+ dekSealed: "TEXT",
592
+ auditReasonStorage: "TEXT NOT NULL DEFAULT 'cleartext'",
593
+ updatedAt: "INTEGER NOT NULL",
594
+ },
595
+ indexes: [],
596
+ sealedFields: ["columnsJson", "factorsJson", "serviceAccountBypassJson"],
597
+ },
598
+ {
599
+ // _blamejs_break_glass_grants — issued grants. Each successful
600
+ // step-up creates one row; each row read decrements rowsRemaining.
601
+ // Default maxRowsPerGrant=1 enforces "row by row" auth per the
602
+ // operator-confirmed shape (each row access = its own grant).
603
+ // Sealed columns hold reason + scopeColumns so audit-readable
604
+ // metadata doesn't leak in cleartext.
605
+ name: "_blamejs_break_glass_grants",
606
+ columns: {
607
+ _id: "TEXT PRIMARY KEY",
608
+ issuedToActorId: "TEXT NOT NULL",
609
+ issuedToActorHash: "TEXT NOT NULL",
610
+ factorType: "TEXT NOT NULL",
611
+ reasonSealed: "TEXT",
612
+ scopeTable: "TEXT NOT NULL",
613
+ scopeColumnsJson: "TEXT NOT NULL",
614
+ issuedAt: "INTEGER NOT NULL",
615
+ expiresAt: "INTEGER NOT NULL",
616
+ maxRowsPerGrant: "INTEGER NOT NULL",
617
+ rowsConsumed: "INTEGER NOT NULL DEFAULT 0",
618
+ revokedAt: "INTEGER",
619
+ sessionId: "TEXT",
620
+ ip: "TEXT",
621
+ kwGrantHalf: "TEXT",
622
+ },
623
+ indexes: [
624
+ { name: "idx_bg_grants_actor", columns: ["issuedToActorHash"] },
625
+ { name: "idx_bg_grants_table", columns: ["scopeTable"] },
626
+ "expiresAt",
627
+ "revokedAt",
628
+ ],
629
+ derivedHashes: { issuedToActorHash: { from: "issuedToActorId" } },
630
+ sealedFields: ["reasonSealed", "scopeColumnsJson", "kwGrantHalf"],
631
+ },
632
+ ];
633
+
634
+ var log = boot("db");
635
+
636
+ // ---- Tmpfs detection ----
637
+
638
+ function resolveTmpDir(optsTmpDir) {
639
+ if (optsTmpDir) return optsTmpDir;
640
+ var envTmp = safeEnv.readVar("BLAMEJS_TMPDIR");
641
+ if (envTmp) return envTmp;
642
+ if (nodeFs.existsSync("/dev/shm")) return "/dev/shm";
643
+ return null;
644
+ }
645
+
646
+ // ---- DB encryption key management ----
647
+
648
+ function loadOrCreateDbKey(dataDirPath, keyPathOverride) {
649
+ // Operator opt: `opts.dbKeyPath` — useful when the encryption key
650
+ // needs to live outside `dataDir` (e.g. a separate volume mounted
651
+ // from a KMS-fronted secret store). Default places it next to the
652
+ // encrypted DB so backup capture is one-tarball.
653
+ var keyPath = keyPathOverride || nodePath.join(dataDirPath, "db.key.enc");
654
+ if (nodeFs.existsSync(keyPath)) {
655
+ var sealed = atomicFile.readSync(keyPath, { encoding: "utf8" }).trim();
656
+ var b64 = vault.unseal(sealed);
657
+ if (!b64) {
658
+ throw _dbErr("db/key-unseal-empty",
659
+ "FATAL: db.key.enc unseal returned empty — vault may not be initialized or key file corrupted");
660
+ }
661
+ return Buffer.from(b64, "base64");
662
+ }
663
+ // First run — generate, seal, persist (atomic)
664
+ var raw = generateBytes(C.BYTES.bytes(32));
665
+ // allow:seal-without-aad — whole-file DB encryption key, not a row column
666
+ var sealedKey = vault.seal(raw.toString("base64"));
667
+ atomicFile.writeSync(keyPath, sealedKey, { fileMode: 0o600 });
668
+ log("generated DB encryption key at " + keyPath);
669
+ return raw;
670
+ }
671
+
672
+ function decryptToTmp() {
673
+ if (!encPath || !nodeFs.existsSync(encPath)) return;
674
+ // If a plaintext file already exists in tmpfs from a prior process, prefer
675
+ // the newer mtime (crash recovery — operator's most recent state wins).
676
+ if (nodeFs.existsSync(dbPath)) {
677
+ var plainStat = nodeFs.statSync(dbPath);
678
+ var encStat = nodeFs.statSync(encPath);
679
+ if (plainStat.mtimeMs > encStat.mtimeMs && plainStat.size > 0) {
680
+ log("plaintext is newer than encrypted — keeping plaintext (crash recovery)");
681
+ return;
682
+ }
683
+ }
684
+ var packed = nodeFs.readFileSync(encPath);
685
+ if (packed.length < 26) return; // too short to be a valid envelope
686
+ // AAD binds the envelope to this deployment's data dir so two
687
+ // installs sharing the same operator passphrase can't swap each
688
+ // other's db.enc files. Backwards-compat: if the AAD-bound decrypt
689
+ // fails, retry without AAD for envelopes written by pre-AAD
690
+ // versions (one-release transition window).
691
+ var aad = _dbEncAad(dataDir);
692
+ try {
693
+ atomicFile.writeSync(dbPath, decryptPacked(packed, encKey, aad));
694
+ } catch (_e) {
695
+ atomicFile.writeSync(dbPath, decryptPacked(packed, encKey));
696
+ }
697
+ }
698
+
699
+ function _dbEncAad(dir) {
700
+ return Buffer.from("blamejs.db-enc.v1\0" + (dir || ""), "utf8");
701
+ }
702
+
703
+ function encryptToDisk() {
704
+ if (!encPath) return;
705
+ // Force WAL checkpoint so the .db file holds all committed transactions.
706
+ try { runSql(database, "PRAGMA wal_checkpoint(TRUNCATE)"); } catch (_e) { /* best effort */ }
707
+ if (!nodeFs.existsSync(dbPath)) return;
708
+ atomicFile.writeSync(encPath, encryptPacked(nodeFs.readFileSync(dbPath), encKey, _dbEncAad(dataDir)));
709
+ }
710
+
711
+ /**
712
+ * @primitive b.db.snapshot
713
+ * @signature b.db.snapshot()
714
+ * @since 0.8.58
715
+ * @status stable
716
+ * @related b.db.flushToDisk, b.backup
717
+ *
718
+ * In-memory encrypted snapshot — same envelope shape that
719
+ * `flushToDisk` writes, just held in memory. Operators capturing a
720
+ * backup mid-flight (`b.backup` wrapping a hot DB) get a Buffer they
721
+ * can stream onward to object storage without touching the on-disk
722
+ * encPath. Forces a WAL checkpoint first so the snapshot reflects
723
+ * committed state, not pre-WAL pages.
724
+ *
725
+ * Under `atRest: 'plain'` returns the raw plaintext SQLite file as a
726
+ * Buffer (no envelope), since there's no encryption key to apply —
727
+ * operators wanting an encrypted snapshot under plain mode wrap with
728
+ * their own `b.crypto.encryptPacked` at the call site.
729
+ *
730
+ * @example
731
+ * var b = require("@blamejs/core");
732
+ * var snap = b.db.snapshot();
733
+ * await b.objectStore.put("backups/" + Date.now() + ".enc", snap);
734
+ */
735
+ function snapshot() {
736
+ _requireInit();
737
+ // WAL checkpoint flushes committed transactions into the main DB file
738
+ // so the snapshot reflects the current logical state, not just the
739
+ // pre-WAL pages.
740
+ try { runSql(database, "PRAGMA wal_checkpoint(TRUNCATE)"); } catch (_e) { /* best effort */ }
741
+ if (!nodeFs.existsSync(dbPath)) {
742
+ throw _dbErr("db/snapshot-no-source",
743
+ "snapshot: plaintext DB at " + dbPath + " is missing — did init complete?");
744
+ }
745
+ var plain = nodeFs.readFileSync(dbPath);
746
+ if (!encPath || !encKey) {
747
+ // atRest: 'plain' — return the raw bytes. Operators wanting an
748
+ // encrypted snapshot under plain mode wrap with their own
749
+ // b.crypto.encryptPacked at the call site.
750
+ return plain;
751
+ }
752
+ return encryptPacked(plain, encKey, _dbEncAad(dataDir));
753
+ }
754
+
755
+ // Remove the plaintext DB + WAL/SHM sidecar files. On Windows these can't be
756
+ // unlinked while the SQLite handle is open, so this MUST be called after
757
+ // database.close().
758
+ function removePlaintextFiles() {
759
+ if (!dbPath) return;
760
+ try { nodeFs.unlinkSync(dbPath); } catch (_e) { /* cleanup */ }
761
+ try { nodeFs.unlinkSync(dbPath + "-wal"); } catch (_e) { /* cleanup */ }
762
+ try { nodeFs.unlinkSync(dbPath + "-shm"); } catch (_e) { /* cleanup */ }
763
+ }
764
+
765
+ // Clean up stale plaintext DB files left by previously-crashed processes.
766
+ // Anything matching blamejs-*.db that isn't our current process's file is
767
+ // stale (no other process should write to /dev/shm with our prefix).
768
+ function cleanStaleTmpDbs(tmpDir) {
769
+ var entries = atomicFile.listDir(tmpDir, {
770
+ filter: function (name) { return name.startsWith("blamejs-") && name.endsWith(".db"); },
771
+ });
772
+ for (var i = 0; i < entries.length; i++) {
773
+ var full = entries[i].fullPath;
774
+ if (full === dbPath) continue;
775
+ try { nodeFs.unlinkSync(full); } catch (_e) { /* concurrent cleanup */ }
776
+ try { nodeFs.unlinkSync(full + "-wal"); } catch (_e) { /* may not exist */ }
777
+ try { nodeFs.unlinkSync(full + "-shm"); } catch (_e) { /* may not exist */ }
778
+ }
779
+ }
780
+
781
+ // ---- Init dispatch ----
782
+
783
+ /**
784
+ * @primitive b.db.init
785
+ * @signature b.db.init(opts)
786
+ * @since 0.1.0
787
+ * @status stable
788
+ * @related b.db.close, b.db.from, b.db.declareWorm
789
+ *
790
+ * Boot the database. Provisions the framework-baked tables
791
+ * (`audit_log` / `consent_log` / `audit_checkpoints` /
792
+ * `_blamejs_*`), reconciles the operator schema, installs append-
793
+ * only triggers on chain tables, runs any pending file-based
794
+ * migrations, verifies the audit + consent chains end-to-end,
795
+ * verifies every audit checkpoint signature, runs PRAGMA
796
+ * integrity_check, performs a rollback-detection check against
797
+ * `audit.tip`, and runs a best-effort SNTP boot drift check. Refuses
798
+ * to boot on any chain breakage, signature mismatch, or rollback —
799
+ * compliance posture demands fail-closed at the earliest signal.
800
+ *
801
+ * @opts
802
+ * dataDir: string, // required — where db.enc + db.key.enc live
803
+ * schema: Array, // required — [{ name, columns, indexes, sealedFields, derivedHashes, foreignKeys, primaryKey, subjectField, personalDataCategories }, ...]
804
+ * atRest: "encrypted"|"plain", // default "encrypted"
805
+ * tmpDir: string, // override the encrypted-mode tmpfs path (default /dev/shm or BLAMEJS_TMPDIR)
806
+ * migrationDir: string, // optional — path to ./migrations/ (run-once each)
807
+ * streamLimit: number, // default 1_000_000 — db.stream row ceiling
808
+ * skipBootIntegrityCheck: boolean, // default false — skip PRAGMA integrity_check
809
+ * skipIntegrityCheck: boolean, // default false — alias
810
+ * auditSigning: { mode, algorithm }, // default { mode: "wrapped" }
811
+ * ntpServers: string[], // override NTP server list
812
+ * ntpTimeoutMs: number, // override NTP timeout
813
+ * dataResidency: object, // operator's region declaration
814
+ *
815
+ * @example
816
+ * var b = require("blamejs");
817
+ * await b.db.init({
818
+ * dataDir: "/var/lib/myapp",
819
+ * atRest: "encrypted",
820
+ * schema: [
821
+ * {
822
+ * name: "orders",
823
+ * columns: {
824
+ * _id: "TEXT PRIMARY KEY",
825
+ * customerId: "TEXT NOT NULL",
826
+ * totalCents: "INTEGER NOT NULL",
827
+ * note: "TEXT",
828
+ * createdAt: "INTEGER NOT NULL",
829
+ * },
830
+ * indexes: ["customerId"],
831
+ * sealedFields: ["note"],
832
+ * derivedHashes: { customerIdHash: { from: "customerId" } },
833
+ * subjectField: "customerId",
834
+ * },
835
+ * ],
836
+ * });
837
+ */
838
+ async function init(opts) {
839
+ if (initialized) return;
840
+ // Drop any prepared-statement cache leftover from a prior init/close
841
+ // cycle — Statement handles attached to a finalized DB throw on use.
842
+ _prepareCache.clear();
843
+ if (!opts || !opts.dataDir) {
844
+ throw new DbError("db/bad-init", "db.init({ dataDir }) is required");
845
+ }
846
+ if (!Array.isArray(opts.schema)) {
847
+ throw new DbError("db/bad-init",
848
+ "db.init({ schema }) must be an array of table definitions");
849
+ }
850
+
851
+ atRest = (opts.atRest || "encrypted").toLowerCase();
852
+ if (atRest !== "encrypted" && atRest !== "plain") {
853
+ throw new DbError("db/bad-at-rest",
854
+ "db.init: atRest must be 'encrypted' or 'plain', got: " + opts.atRest);
855
+ }
856
+ // D-M5 — operator-tunable streamLimit ceiling. Throw at config-time
857
+ // on bad shape so a typo surfaces at boot rather than as an
858
+ // unbounded stream at first export.
859
+ if (opts.streamLimit !== undefined) {
860
+ if (typeof opts.streamLimit !== "number" || !isFinite(opts.streamLimit) ||
861
+ opts.streamLimit <= 0 || Math.floor(opts.streamLimit) !== opts.streamLimit) {
862
+ throw new DbError("db/bad-init",
863
+ "db.init: streamLimit must be a positive finite integer; got " +
864
+ JSON.stringify(opts.streamLimit));
865
+ }
866
+ streamLimit = opts.streamLimit;
867
+ }
868
+ dataDir = opts.dataDir;
869
+ if (!nodeFs.existsSync(dataDir)) nodeFs.mkdirSync(dataDir, { recursive: true });
870
+
871
+ if (atRest === "encrypted") {
872
+ var tmpDir = resolveTmpDir(opts.tmpDir);
873
+ if (!tmpDir) {
874
+ throw _dbErr("db/no-tmpfs",
875
+ "FATAL: atRest: 'encrypted' (default) requires tmpfs but none was found. " +
876
+ "Provide opts.tmpDir or set BLAMEJS_TMPDIR, or pass atRest: 'plain' (with warning).");
877
+ }
878
+ if (!nodeFs.existsSync(tmpDir)) nodeFs.mkdirSync(tmpDir, { recursive: true });
879
+
880
+ // D-H7 — if the resolved tmpDir is NOT actually tmpfs, the
881
+ // plaintext DB file lives on persistent storage. statvfs/statfs
882
+ // isn't in stable Node, but on Linux we can check that tmpDir
883
+ // resolves under /dev/shm or /run/shm as a heuristic. On other
884
+ // platforms we warn that the operator must verify tmpfs binding
885
+ // out-of-band.
886
+ if (process.platform === "linux") {
887
+ var realTmp = "";
888
+ try { realTmp = nodeFs.realpathSync(tmpDir); } catch (_e) { /* stat best-effort */ }
889
+ if (realTmp.indexOf("/dev/shm") !== 0 && realTmp.indexOf("/run/shm") !== 0 &&
890
+ realTmp.indexOf("/run/user/") !== 0 && realTmp.indexOf("/tmp") !== 0) {
891
+ log.warn("WARNING: db.init: tmpDir '" + tmpDir + "' (real: '" + realTmp +
892
+ "') does not resolve under /dev/shm /run/shm /run/user /tmp — verify it is " +
893
+ "actually a tmpfs mount. A persistent-disk tmpDir leaks plaintext into backup " +
894
+ "snapshots, replication, and forensic disk images.");
895
+ }
896
+ }
897
+
898
+ // Operator overrides for the encrypted-DB on-disk nodePath. `opts.encryptedDbPath`
899
+ // takes a fully-qualified nodePath; `opts.encryptedDbName` overrides
900
+ // just the basename under `dataDir` (default "db.enc"). Helps when
901
+ // multiple framework-shaped instances share a dataDir.
902
+ encPath = opts.encryptedDbPath ||
903
+ nodePath.join(dataDir, opts.encryptedDbName || "db.enc");
904
+ dbPath = nodePath.join(tmpDir, "blamejs-" + generateToken(C.BYTES.bytes(16)) + ".db");
905
+ encKey = loadOrCreateDbKey(dataDir, opts.dbKeyPath);
906
+
907
+ cleanStaleTmpDbs(tmpDir);
908
+ decryptToTmp();
909
+ } else {
910
+ // plain mode
911
+ log.warn("WARNING: atRest: 'plain' — DB structure and row counts visible on disk.");
912
+ log.warn(" Field-level encryption (sealedFields) still protects sealed columns,");
913
+ log.warn(" but the simpler at-rest model is opt-out only. Default is 'encrypted'.");
914
+ dbPath = nodePath.join(dataDir, "blamejs.db");
915
+ encPath = null;
916
+ encKey = null;
917
+ }
918
+
919
+ // Open the database
920
+ database = new DatabaseSync(dbPath);
921
+
922
+ // Performance pragmas
923
+ runSql(database, "PRAGMA journal_mode=WAL");
924
+ runSql(database, "PRAGMA synchronous=NORMAL");
925
+ runSql(database, "PRAGMA cache_size=-8000");
926
+ runSql(database, "PRAGMA temp_store=MEMORY");
927
+ runSql(database, "PRAGMA busy_timeout=5000");
928
+ runSql(database, "PRAGMA mmap_size=268435456");
929
+ runSql(database, "PRAGMA auto_vacuum=INCREMENTAL");
930
+ // Foreign-key enforcement is OFF by default in SQLite. Turn it ON so
931
+ // structured `foreignKeys` declarations actually constrain writes.
932
+ runSql(database, "PRAGMA foreign_keys=ON");
933
+
934
+ // PRAGMA secure_delete=ON — SQLite normally just unlinks rows from
935
+ // the B-tree; the underlying page bytes survive on disk until a new
936
+ // write reuses the slot. With secure_delete=ON, freed pages are
937
+ // overwritten with zeros so a forensic recovery against the file
938
+ // can't reconstruct deleted rows. The cost is one extra write per
939
+ // delete, which the framework's audit-and-DSR-erase path already
940
+ // dominates with audit-chain emissions and cascade fan-out.
941
+ runSql(database, "PRAGMA secure_delete=ON");
942
+ // PRAGMA trusted_schema=OFF — refuses to call functions / virtual-
943
+ // table modules referenced from a malicious shadow schema. Defends
944
+ // the CVE-2018-8740 family where an attacker who can write to the
945
+ // database file (backups, logs, restore-from-untrusted) plants
946
+ // schema entries that fire on next access.
947
+ try { runSql(database, "PRAGMA trusted_schema=OFF"); } catch (_e) { /* sqlite < 3.31 */ }
948
+ // PRAGMA cell_size_check=ON — refuses pages with corrupted cell
949
+ // sizes at parse time rather than crashing later. Cheap defense
950
+ // against malformed-page attacks.
951
+ try { runSql(database, "PRAGMA cell_size_check=ON"); } catch (_e) { /* sqlite < 3.26 */ }
952
+ // node:sqlite does not expose loadExtension at all — extensions must
953
+ // be statically linked into the runtime. The framework's surface is
954
+ // therefore implicitly extension-free; no runtime defense is needed
955
+ // beyond the trusted_schema + cell_size_check PRAGMAs above.
956
+
957
+ // Boot-time integrity check — refuse to boot on B-tree corruption.
958
+ // SQLite normally surfaces corruption only when a query stumbles on
959
+ // a bad page; that's a "first failure during request handling"
960
+ // surface, not a clean fail-closed boot. integrity_check is fast on
961
+ // the freshly-decrypted-into-tmpfs file (<1 second on a typical
962
+ // multi-MB DB) and the result is "ok" or a list of issues.
963
+ if (opts.skipBootIntegrityCheck !== true) {
964
+ var ic = database.prepare("PRAGMA integrity_check").all();
965
+ var icIssues = ic.map(function (r) { return r && r.integrity_check; })
966
+ .filter(function (s) { return s && s !== "ok"; });
967
+ if (icIssues.length > 0) {
968
+ throw new DbError("db/integrity-check-failed",
969
+ "PRAGMA integrity_check at boot reported " + icIssues.length +
970
+ " issue(s): " + icIssues.slice(0, 3).join("; "));
971
+ }
972
+ }
973
+
974
+ // PRAGMA integrity_check — refuse boot on B-tree corruption (per
975
+ // audit-batch finding). SQLite returns "ok" for a healthy database;
976
+ // any other result means corruption. Catching it at boot beats
977
+ // stumbling on it later in a query that hits the bad page. Skip
978
+ // when opts.skipIntegrityCheck is set (e.g. tmpfs-only fixtures).
979
+ if (opts.skipIntegrityCheck !== true) {
980
+ var integrityRows = [];
981
+ try {
982
+ // .all-style read; runSql is for statements without rows.
983
+ integrityRows = database.prepare("PRAGMA integrity_check").all();
984
+ } catch (e) {
985
+ throw new DbError("db/integrity-check-failed",
986
+ "PRAGMA integrity_check failed at boot: " + ((e && e.message) || String(e)));
987
+ }
988
+ if (integrityRows.length !== 1 ||
989
+ !integrityRows[0] || integrityRows[0].integrity_check !== "ok") {
990
+ throw new DbError("db/integrity-check-failed",
991
+ "PRAGMA integrity_check reported corruption: " +
992
+ JSON.stringify(integrityRows));
993
+ }
994
+ }
995
+
996
+ // Refuse app schema entries that collide with framework-reserved names.
997
+ // Pre-v0.8.18 this was an exact-match Set; an app could ship
998
+ // `_blamejs_audit_log_archive` (or similar prefix-collision) and the
999
+ // framework would silently provision it next to the reserved
1000
+ // namespace, allowing a row-by-row look-alike attack against audit
1001
+ // archive tooling.
1002
+ // Under `frameworkTables: false` the framework's own audit_log /
1003
+ // consent_log are NOT provisioned, so an operator naming a table
1004
+ // `audit_log` (or `consent_log`) doesn't collide. The `_blamejs_*`
1005
+ // prefix stays reserved unconditionally — those names are
1006
+ // hard-claimed by other framework primitives (sessions, jobs,
1007
+ // migrations, rate-limit-counters, …) which still get provisioned
1008
+ // by their respective subsystems.
1009
+ var frameworkTablesEarly = opts.frameworkTables !== false;
1010
+ var FRAMEWORK_NAMED_RESERVED = frameworkTablesEarly
1011
+ ? RESERVED_TABLE_NAMES
1012
+ : new Set(); // empty — fall back to the prefix check only
1013
+ for (var ri = 0; ri < opts.schema.length; ri++) {
1014
+ var appName = opts.schema[ri].name;
1015
+ if (FRAMEWORK_NAMED_RESERVED.has(appName) ||
1016
+ (typeof appName === "string" && appName.indexOf("_blamejs_") === 0)) {
1017
+ throw new DbError("db/reserved-table-name",
1018
+ "table name '" + appName + "' is reserved by the framework. " +
1019
+ "Pick a different name (the framework provisions audit_log, consent_log, " +
1020
+ "and any '_blamejs_*'-prefixed tables automatically). " +
1021
+ "Pass opts.frameworkTables: false to skip provisioning audit_log/consent_log " +
1022
+ "when the host application owns its own audit chain.");
1023
+ }
1024
+ }
1025
+
1026
+ // Track subject schema for subject.export/erase walks
1027
+ subjectTables = [];
1028
+ for (var si = 0; si < opts.schema.length; si++) {
1029
+ var st = opts.schema[si];
1030
+ if (st.subjectField) {
1031
+ // Validate personalDataCategories shape + audit-emit on
1032
+ // unknown vocabulary. Pre-v0.8.37 this was a free-form JSON
1033
+ // blob; a typo silently dropped the column from subject-export
1034
+ // / erase walks. The framework checks the value is a string
1035
+ // (catches null / number / object typos) and emits a warning
1036
+ // audit when the category is outside the GDPR Art 9 + general
1037
+ // vocabulary so operators can audit-trail their custom labels.
1038
+ if (st.personalDataCategories) {
1039
+ if (typeof st.personalDataCategories !== "object" || Array.isArray(st.personalDataCategories)) {
1040
+ throw new DbError("db/bad-personal-data-categories",
1041
+ "table '" + st.name + "': personalDataCategories must be an object mapping field name → category");
1042
+ }
1043
+ var FRAMEWORK_CATEGORY_VOCAB = [
1044
+ "name", "email", "phone", "address", "ip", "id-document",
1045
+ "biometric", "health", "genetic", "sexual-orientation",
1046
+ "racial-or-ethnic-origin", "political-opinion", "religious-belief",
1047
+ "trade-union-membership", "criminal-record",
1048
+ "financial", "location", "behavioral", "device-id",
1049
+ "child-data", "education", "employment", "operator-defined",
1050
+ ];
1051
+ Object.keys(st.personalDataCategories).forEach(function (field) {
1052
+ var cat = st.personalDataCategories[field];
1053
+ if (typeof cat !== "string" || cat.length === 0) {
1054
+ throw new DbError("db/bad-personal-data-category",
1055
+ "table '" + st.name + "' field '" + field +
1056
+ "': category must be a non-empty string");
1057
+ }
1058
+ if (FRAMEWORK_CATEGORY_VOCAB.indexOf(cat) === -1) {
1059
+ // Unknown — emit a one-time audit per (table,field,category)
1060
+ // tuple so operators see typos in their categorical
1061
+ // taxonomy. Lazy require to avoid circular load (audit
1062
+ // imports db for chain hashing).
1063
+ try {
1064
+ var auditMod = require("./audit"); // allow:inline-require — circular-load defense (audit imports db)
1065
+ auditMod.safeEmit({
1066
+ action: "db.personal_data_category_unknown",
1067
+ outcome: "success",
1068
+ metadata: {
1069
+ severity: "warning",
1070
+ table: st.name,
1071
+ field: field,
1072
+ category: cat,
1073
+ vocabHint: "use one of: " + FRAMEWORK_CATEGORY_VOCAB.join(", ") +
1074
+ " (or operator-defined for genuinely-custom)",
1075
+ },
1076
+ });
1077
+ } catch (_e) { /* drop-silent */ }
1078
+ }
1079
+ });
1080
+ }
1081
+ subjectTables.push({
1082
+ name: st.name,
1083
+ subjectField: st.subjectField,
1084
+ personalDataCategories: st.personalDataCategories || {},
1085
+ });
1086
+ }
1087
+ }
1088
+
1089
+ // Operator opt-out for the framework's own tables + audit/consent
1090
+ // chain machinery + WORM assertion + audit-signing bootstrap. Set
1091
+ // `frameworkTables: false` when the host application maintains its
1092
+ // own audit/consent semantics and just wants the framework's
1093
+ // primitives (vault / db / cryptoField / etc.) without the bundled
1094
+ // chain tables. When OFF, every framework-table-dependent step
1095
+ // below is a no-op. Append-only triggers are scoped to the
1096
+ // framework tables only, so they're skipped too.
1097
+ //
1098
+ // `auditSigning: false` is a finer-grained gate — keep the
1099
+ // framework tables but skip the audit-signing-key bootstrap (HS-
1100
+ // shape deployments that already manage their own signing key).
1101
+ //
1102
+ // Defaults match v0.8.57 behavior: both ON.
1103
+ var frameworkTablesEnabled = opts.frameworkTables !== false;
1104
+ var auditSigningEnabled = opts.auditSigning !== false;
1105
+
1106
+ // Build the full schema = framework-baked tables + app tables.
1107
+ // Framework tables come FIRST so audit_log/consent_log exist before any
1108
+ // app migration can reference them. When `frameworkTables: false`,
1109
+ // skip the concat so the operator's own `audit_log` (or whatever
1110
+ // shape) doesn't collide with the framework's.
1111
+ var fullSchema = frameworkTablesEnabled
1112
+ ? FRAMEWORK_SCHEMA.concat(opts.schema)
1113
+ : opts.schema.slice();
1114
+
1115
+ // Register schema with field-crypto + capture table metadata snapshot
1116
+ // (framework tables included so getTableMetadata covers everything).
1117
+ tableMetadata = {};
1118
+ for (var i = 0; i < fullSchema.length; i++) {
1119
+ var t = fullSchema[i];
1120
+ cryptoField.registerTable(t.name, {
1121
+ sealedFields: t.sealedFields,
1122
+ derivedHashes: t.derivedHashes,
1123
+ hashNamespaces: t.hashNamespaces,
1124
+ });
1125
+ tableMetadata[t.name] = {
1126
+ primaryKey: _normalizePk(t),
1127
+ foreignKeys: Array.isArray(t.foreignKeys) ? t.foreignKeys.slice() : [],
1128
+ columns: Object.assign({}, t.columns),
1129
+ indexes: Array.isArray(t.indexes) ? t.indexes.slice() : [],
1130
+ sealedFields: Array.isArray(t.sealedFields) ? t.sealedFields.slice() : [],
1131
+ derivedHashes: Object.assign({}, t.derivedHashes || {}),
1132
+ subjectField: t.subjectField || null,
1133
+ personalDataCategories: Object.assign({}, t.personalDataCategories || {}),
1134
+ };
1135
+ }
1136
+
1137
+ // Declarative schema reconcile (framework + app tables)
1138
+ dbSchema.reconcile(database, fullSchema);
1139
+
1140
+ // Append-only enforcement on audit_log + consent_log via SQLite triggers.
1141
+ // Apps cannot UPDATE or DELETE these tables; the framework's audit.record /
1142
+ // consent.grant only INSERT. This is a SQL-level guard against bug-induced
1143
+ // or malicious tampering — independent of the API surface's discipline.
1144
+ // Operator-driven retention purge (when implemented) must drop these
1145
+ // triggers explicitly inside a transaction, perform the purge, and
1146
+ // recreate them. Skipped under `frameworkTables: false`.
1147
+ if (frameworkTablesEnabled) _installAppendOnlyTriggers(database);
1148
+
1149
+ // Imperative migrations (run once each, in order)
1150
+ if (opts.migrationDir) {
1151
+ var result = dbSchema.runMigrations(database, opts.migrationDir);
1152
+ if (result.applied.length > 0) {
1153
+ log("applied " + result.applied.length + " migration(s): " + result.applied.join(", "));
1154
+ }
1155
+ }
1156
+
1157
+ // dataResidency — operator's declared region. Registered here for
1158
+ // downstream backends (storage, mail, log destinations) to validate
1159
+ // against; backends opt in by reading this value via getDataResidency().
1160
+ dataResidency = opts.dataResidency || null;
1161
+
1162
+ // Mark initialized BEFORE the chain verify so audit/consent.verify() can
1163
+ // call db.prepare() through the public surface. If verify fails, we
1164
+ // process.exit() — initialized state is moot at that point.
1165
+ initialized = true;
1166
+
1167
+ // ---- Refuse-to-boot on chain break ----
1168
+ // Verify both the audit and consent chains end-to-end. A broken chain
1169
+ // means tamper-evidence has been compromised — the framework refuses
1170
+ // to continue under any circumstances. Recovery is operator-driven
1171
+ // (restore from backup or manual chain rebuild); the framework only
1172
+ // detects-and-fails. Skipped under `frameworkTables: false` (the
1173
+ // framework's audit_log / consent_log don't exist for an operator
1174
+ // running their own audit subsystem).
1175
+ if (frameworkTablesEnabled) {
1176
+ var auditResult = await audit.verify();
1177
+ if (!auditResult.ok) {
1178
+ // Fire the breach event BEFORE throwing so operator listeners
1179
+ // get a chance at sync I/O (file flag, console alert) before
1180
+ // init unwinds.
1181
+ events.emit(events.EVENTS.AUDIT_CHAIN_BREAK, { table: "audit_log", result: auditResult });
1182
+ throw _dbErr("db/audit-chain-break",
1183
+ "FATAL: audit_log chain integrity broken at row " + auditResult.breakAt +
1184
+ " (" + auditResult.reason + "); break row _id: " + auditResult.breakRowId +
1185
+ "; expected: " + auditResult.expected + "; actual: " + auditResult.actual +
1186
+ ". Refusing to boot. Compliance requires that any tamper-detection signal halt service. " +
1187
+ "Recovery is manual: restore from backup, or rebuild the audit chain from a verified earlier snapshot.");
1188
+ }
1189
+ var consentResult = await consent.verify();
1190
+ if (!consentResult.ok) {
1191
+ events.emit(events.EVENTS.AUDIT_CHAIN_BREAK, { table: "consent_log", result: consentResult });
1192
+ throw _dbErr("db/consent-chain-break",
1193
+ "FATAL: consent_log chain integrity broken at row " + consentResult.breakAt +
1194
+ " (" + consentResult.reason + "); break row _id: " + consentResult.breakRowId +
1195
+ ". Refusing to boot.");
1196
+ }
1197
+ log("audit chain ok (" + auditResult.rowsVerified + " rows), consent chain ok (" + consentResult.rowsVerified + " rows)");
1198
+ }
1199
+
1200
+ // ---- Rollback detection (audit.tip sidecar) ----
1201
+ // The framework writes <dataDir>/audit.tip on each checkpoint. At boot we
1202
+ // compare current MAX(monotonicCounter) to the recorded tip. If current
1203
+ // is BELOW tip — the DB was rolled back to an older snapshot. Refuse boot.
1204
+ _checkRollback(dataDir);
1205
+
1206
+ // ---- F-RET-2 — WORM posture assertion ----
1207
+ // Under sec-17a-4 / finra-4511 / fda-21cfr11 postures the operator
1208
+ // MUST have declared row-level WORM on at least one business-record
1209
+ // table. Refuse boot otherwise so missing-declaration drift is
1210
+ // surfaced at start-up, not on the first delete.
1211
+ // Skipped under `frameworkTables: false` — WORM declarations are
1212
+ // an operator-side concern when the framework isn't owning audit.
1213
+ if (frameworkTablesEnabled) {
1214
+ try { _assertWormUnderPosture(); }
1215
+ catch (e) {
1216
+ // The assertion throws under regulated postures; let it
1217
+ // propagate. Outside regulated postures it's a no-op.
1218
+ throw e;
1219
+ }
1220
+ }
1221
+
1222
+ // ---- Audit-signing key + checkpoint subsystem ----
1223
+ // Default mode 'wrapped' (passphrase-required, separate from vault). Apps
1224
+ // that want a quick-start dev path can pass auditSigning: { mode: 'plaintext' }
1225
+ // — same warning pattern as vault.
1226
+ // opts.auditSigning.algorithm picks the keypair algorithm at first-run
1227
+ // generation. Default = SLH-DSA-SHAKE-256f (matches the framework's
1228
+ // SHAKE-family hash posture); ML-DSA-87 is the throughput-focused
1229
+ // opt-in. Existing key files take their algorithm from disk; this
1230
+ // option only matters on first generation.
1231
+ // Operator opt-out via `auditSigning: false` skips the signing
1232
+ // bootstrap entirely. Also implicitly skipped when frameworkTables
1233
+ // are off (no audit_log to sign checkpoints over).
1234
+ if (auditSigningEnabled && frameworkTablesEnabled) {
1235
+ var auditSigningMode = (opts.auditSigning && opts.auditSigning.mode)
1236
+ ? opts.auditSigning.mode
1237
+ : safeEnv.readVar("BLAMEJS_AUDIT_SIGNING_MODE", {
1238
+ default: "wrapped",
1239
+ enum: ["wrapped", "plaintext"],
1240
+ });
1241
+ var auditSigningAlg = opts.auditSigning && opts.auditSigning.algorithm
1242
+ ? opts.auditSigning.algorithm
1243
+ : null;
1244
+ await auditSign.init({
1245
+ dataDir: dataDir,
1246
+ mode: auditSigningMode,
1247
+ algorithm: auditSigningAlg || undefined,
1248
+ });
1249
+ }
1250
+
1251
+ // Verify all existing checkpoint signatures (defense against
1252
+ // signature forgery attempt + key-rotation gone wrong). Refuse to
1253
+ // boot on failure. Skipped under `frameworkTables: false` /
1254
+ // `auditSigning: false`.
1255
+ if (frameworkTablesEnabled && auditSigningEnabled) {
1256
+ var ckptResult = await audit.verifyCheckpoints();
1257
+ if (!ckptResult.ok) {
1258
+ events.emit(events.EVENTS.AUDIT_CHECKPOINT_BREAK, { result: ckptResult });
1259
+ throw _dbErr("db/audit-checkpoint-break",
1260
+ "FATAL: audit checkpoint verification failed at row " +
1261
+ ckptResult.breakAt + " (" + ckptResult.reason + "); checkpoint _id: " +
1262
+ ckptResult.checkpointId + ". Refusing to boot. Either the audit-signing key " +
1263
+ "was rotated without retaining the prior pubkey, or a forged checkpoint was inserted.");
1264
+ }
1265
+ log("audit checkpoints ok (" + ckptResult.checkpointsVerified + " signed)");
1266
+
1267
+ // Anchor a fresh checkpoint at boot if there's any new audit
1268
+ // activity since the last checkpoint (else no-op).
1269
+ await audit.checkpoint({ skipIfUnchanged: true });
1270
+ }
1271
+
1272
+ // ---- NTP drift check ----
1273
+ // Best-effort; unreachable NTP doesn't fail boot, but >= 1hr drift does
1274
+ // (unless BLAMEJS_NTP_STRICT=0 / BLAMEJS_SKIP_NTP_CHECK=1).
1275
+ await _runNtpBootCheck(opts);
1276
+
1277
+ // Start periodic encrypt timer (encrypted mode only)
1278
+ if (atRest === "encrypted") {
1279
+ encTimer = safeAsync.repeating(function () {
1280
+ try { encryptToDisk(); } catch (e) {
1281
+ log.error("periodic encrypt failed: " + e.message);
1282
+ }
1283
+ }, C.TIME.minutes(5), { name: "db-periodic-encrypt" });
1284
+
1285
+ // Final encrypt on process exit. We don't try to unlink the plaintext
1286
+ // here — the SQLite handle may still be open, and the OS reclaims tmpfs
1287
+ // on reboot anyway. close() does the orderly shutdown.
1288
+ process.on("exit", function () {
1289
+ try { encryptToDisk(); } catch (_e) { /* exit handler — silent */ }
1290
+ });
1291
+ }
1292
+
1293
+ log("ready (mode: " + atRest + ", path: " + dbPath + ")");
1294
+ }
1295
+
1296
+ // ---- Public API ----
1297
+
1298
+ /**
1299
+ * @primitive b.db.from
1300
+ * @signature b.db.from(tableName)
1301
+ * @since 0.1.0
1302
+ * @status stable
1303
+ * @related b.db.prepare, b.db.transaction, b.db.stream
1304
+ *
1305
+ * Open a chainable Query against a registered table. Sealed columns
1306
+ * auto-encrypt on insert/update and auto-decrypt on read; derived-
1307
+ * hash columns auto-populate from their source field on insert.
1308
+ * Identifier safety, parameter binding, row-policy gates, and
1309
+ * audit-emission are wired into the chain so operator code never
1310
+ * concatenates SQL by hand.
1311
+ *
1312
+ * @example
1313
+ * var b = require("blamejs");
1314
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1315
+ * { name: "orders",
1316
+ * columns: { _id: "TEXT PRIMARY KEY", customerId: "TEXT NOT NULL", totalCents: "INTEGER NOT NULL" },
1317
+ * sealedFields: ["customerId"] },
1318
+ * ] });
1319
+ *
1320
+ * b.db.from("orders").insert({
1321
+ * _id: b.uuid.v7(), customerId: "cust_123", totalCents: 4999,
1322
+ * });
1323
+ *
1324
+ * var rows = b.db.from("orders").where({ customerId: "cust_123" }).all();
1325
+ * rows.length;
1326
+ * // → 1
1327
+ */
1328
+ function from(tableName) {
1329
+ _requireInit();
1330
+ return new Query(database, tableName);
1331
+ }
1332
+
1333
+ // D-M6 — bounded prepared-statement cache for SQLite. Long-running
1334
+ // daemons with diverse query shapes accumulate node:sqlite Statement
1335
+ // handles indefinitely; the LRU here caps at PREPARE_CACHE_MAX (256)
1336
+ // distinct SQL strings and finalizes the oldest when over. Reuse of
1337
+ // the same SQL string returns the cached Statement (the canonical
1338
+ // node:sqlite-style win); previously this was ad-hoc and operators
1339
+ // re-preparing in a hot path leaked fds.
1340
+ var PREPARE_CACHE_MAX = 256; // allow:raw-byte-literal — distinct-statement cache cap
1341
+ var _prepareCache = new Map(); // sql → Statement (insertion order = LRU)
1342
+
1343
+ /**
1344
+ * @primitive b.db.prepare
1345
+ * @signature b.db.prepare(sql)
1346
+ * @since 0.1.0
1347
+ * @status stable
1348
+ * @related b.db.from, b.db.runSql, b.db.stream
1349
+ *
1350
+ * Raw-escape-hatch wrapper around `node:sqlite`'s `Statement`
1351
+ * preparation, with an LRU cache keyed by SQL string (cap 256
1352
+ * distinct shapes). Reuse of the same SQL returns the cached
1353
+ * Statement so a hot path doesn't churn file descriptors. Use
1354
+ * `b.db.from(table)` for the typical chainable surface; `prepare` is
1355
+ * for the rare cases where the chainable Query doesn't cover the
1356
+ * shape.
1357
+ *
1358
+ * @example
1359
+ * var b = require("blamejs");
1360
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1361
+ * { name: "orders",
1362
+ * columns: { _id: "TEXT PRIMARY KEY", totalCents: "INTEGER NOT NULL" } },
1363
+ * ] });
1364
+ *
1365
+ * var stmt = b.db.prepare("SELECT SUM(totalCents) AS total FROM orders");
1366
+ * var row = stmt.get();
1367
+ * typeof row.total;
1368
+ * // → "object"
1369
+ */
1370
+ function prepare(sql) {
1371
+ _requireInit();
1372
+ if (_prepareCache.has(sql)) {
1373
+ var hit = _prepareCache.get(sql);
1374
+ // Refresh LRU position by reinserting.
1375
+ _prepareCache.delete(sql);
1376
+ _prepareCache.set(sql, hit);
1377
+ return hit;
1378
+ }
1379
+ var stmt = database.prepare(sql);
1380
+ _prepareCache.set(sql, stmt);
1381
+ if (_prepareCache.size > PREPARE_CACHE_MAX) {
1382
+ var oldestKey = _prepareCache.keys().next().value;
1383
+ _prepareCache.delete(oldestKey);
1384
+ }
1385
+ return stmt;
1386
+ }
1387
+
1388
+ /**
1389
+ * @primitive b.db.stream
1390
+ * @signature b.db.stream(sql)
1391
+ * @since 0.4.0
1392
+ * @status stable
1393
+ * @related b.db.from, b.db.prepare, b.db.exportCsv
1394
+ *
1395
+ * Object-mode `Readable` that yields rows as `node:sqlite`'s
1396
+ * `iterate()` produces them. Unlike `.all()`, the engine never
1397
+ * materializes the full result set, so audit exports, backup table
1398
+ * dumps, and million-row reports finish without OOM pressure.
1399
+ * Variadic: positional parameter bindings come after `sql`; an
1400
+ * optional final plain-object argument carries `opts.table` (enables
1401
+ * sealed-column auto-unseal) and `opts.streamLimit` (per-call row
1402
+ * ceiling override). Default ceiling is the module-level
1403
+ * `streamLimit` (1_000_000); the stream destroys with a
1404
+ * `db/stream-limit-exceeded` error past the cap rather than
1405
+ * accumulating unboundedly.
1406
+ *
1407
+ * @example
1408
+ * var b = require("blamejs");
1409
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1410
+ * { name: "events",
1411
+ * columns: { _id: "TEXT PRIMARY KEY", payload: "TEXT" },
1412
+ * sealedFields: ["payload"] },
1413
+ * ] });
1414
+ *
1415
+ * var count = 0;
1416
+ * var s = b.db.stream("SELECT * FROM events", { table: "events" });
1417
+ * await new Promise(function (resolve, reject) {
1418
+ * s.on("data", function (_row) { count += 1; });
1419
+ * s.on("end", resolve);
1420
+ * s.on("error", reject);
1421
+ * });
1422
+ * count >= 0;
1423
+ * // → true
1424
+ */
1425
+ function stream(sql) {
1426
+ _requireInit();
1427
+ var opts = null;
1428
+ var params;
1429
+ // Last arg may be a plain {table?} options object; everything else
1430
+ // is a SQL parameter binding. node:sqlite accepts numbers, strings,
1431
+ // bigints, Buffers, and null — plain objects can only be opts.
1432
+ var args = Array.prototype.slice.call(arguments, 1);
1433
+ if (args.length > 0) {
1434
+ var last = args[args.length - 1];
1435
+ var isOptsShape = last !== null && typeof last === "object" &&
1436
+ !Buffer.isBuffer(last) && !Array.isArray(last) &&
1437
+ typeof last.length !== "number"; // exclude TypedArray-shapes
1438
+ if (isOptsShape) {
1439
+ opts = last;
1440
+ params = args.slice(0, -1);
1441
+ } else {
1442
+ params = args;
1443
+ }
1444
+ } else {
1445
+ params = [];
1446
+ }
1447
+ var table = opts && typeof opts.table === "string" ? opts.table : null;
1448
+ var unseal = table ? cryptoField : null;
1449
+
1450
+ // D-M5 — streamLimit ceiling. Per-call opts.streamLimit overrides
1451
+ // the module-level default; bad shape throws at call time so the
1452
+ // typo surfaces instead of an unbounded stream.
1453
+ var perCallLimit = streamLimit;
1454
+ if (opts && opts.streamLimit !== undefined) {
1455
+ if (typeof opts.streamLimit !== "number" || !isFinite(opts.streamLimit) ||
1456
+ opts.streamLimit <= 0 || Math.floor(opts.streamLimit) !== opts.streamLimit) {
1457
+ throw new DbError("db/bad-stream-limit",
1458
+ "db.stream: opts.streamLimit must be a positive finite integer; got " +
1459
+ JSON.stringify(opts.streamLimit));
1460
+ }
1461
+ perCallLimit = opts.streamLimit;
1462
+ }
1463
+
1464
+ var stmt;
1465
+ var iter;
1466
+ try {
1467
+ stmt = database.prepare(sql);
1468
+ iter = stmt.iterate.apply(stmt, params);
1469
+ } catch (e) {
1470
+ var r = new Readable({ objectMode: true, read: function () {} });
1471
+ setImmediate(function () { r.destroy(e); });
1472
+ return r;
1473
+ }
1474
+ var emitted = 0;
1475
+ return new Readable({
1476
+ objectMode: true,
1477
+ read: function () {
1478
+ try {
1479
+ if (emitted >= perCallLimit) {
1480
+ this.destroy(new DbError("db/stream-limit-exceeded",
1481
+ "db.stream: emitted " + emitted + " rows, exceeding streamLimit " +
1482
+ perCallLimit + ". Pass opts.streamLimit higher OR raise via " +
1483
+ "db.init({ streamLimit }) after auditing the export path."));
1484
+ return;
1485
+ }
1486
+ var step = iter.next();
1487
+ if (step.done) { this.push(null); return; }
1488
+ emitted += 1;
1489
+ var row = step.value;
1490
+ this.push(unseal ? unseal.unsealRow(table, row) : row);
1491
+ } catch (e) {
1492
+ this.destroy(e);
1493
+ }
1494
+ },
1495
+ });
1496
+ }
1497
+
1498
+ // DDL_RE — case-insensitive prefix match for the eight statement
1499
+ // shapes that MUTATE schema. Audited individually so a forensic
1500
+ // review can reconstruct schema evolution from the chain alone (D-M1).
1501
+ var DDL_RE = /^\s*(CREATE|DROP|ALTER|TRUNCATE|RENAME|ATTACH|DETACH|REINDEX)\b/i;
1502
+
1503
+ // D-L7 — slow-query observability buckets for the local SQLite nodePath.
1504
+ // Highest matched bucket wins so the per-query emit is single-shot;
1505
+ // operators dashboard on the `bucket` label.
1506
+ var _SLOW_QUERY_BUCKETS_LOCAL = Object.freeze([
1507
+ { ms: C.TIME.seconds(30), label: "30s" },
1508
+ { ms: C.TIME.seconds(5), label: "5s" },
1509
+ { ms: C.TIME.seconds(1), label: "1s" },
1510
+ ]);
1511
+ var _STATEMENT_CLASS_RE_LOCAL = /^\s*(?:\/\*[\s\S]*?\*\/\s*|--[^\n]*\n\s*)*([A-Za-z]+)/;
1512
+ function _classifyStatementLocal(sql) {
1513
+ if (typeof sql !== "string" || sql.length === 0) return "UNKNOWN";
1514
+ var m = _STATEMENT_CLASS_RE_LOCAL.exec(sql);
1515
+ return m ? m[1].toUpperCase() : "UNKNOWN";
1516
+ }
1517
+ function _reportSlowSqlite(durationMs, statement) {
1518
+ if (typeof durationMs !== "number" || !isFinite(durationMs)) return;
1519
+ for (var i = 0; i < _SLOW_QUERY_BUCKETS_LOCAL.length; i++) {
1520
+ var bucket = _SLOW_QUERY_BUCKETS_LOCAL[i];
1521
+ if (durationMs >= bucket.ms) {
1522
+ try {
1523
+ observability.event("db.query.slow", durationMs, {
1524
+ backend: "sqlite",
1525
+ bucket: bucket.label,
1526
+ statementClass: _classifyStatementLocal(statement),
1527
+ "db.statement": String(statement || "").slice(0, 256), // allow:raw-byte-literal — log-truncation length, not bytes
1528
+ });
1529
+ } catch (_e) { /* hot-path observability sink — drop-silent by design */ }
1530
+ return;
1531
+ }
1532
+ }
1533
+ }
1534
+
1535
+ function execRaw(sql) {
1536
+ _requireInit();
1537
+ var startedAt = Date.now();
1538
+ var auditMod = (function () { try { return require("./audit"); } catch (_e) { return null; } })(); // allow:inline-require — circular-load defense (audit imports db)
1539
+ // DDL_RE only matches the leading keyword — bounded by `/\s*(KEYWORD)\b/`
1540
+ // so the test is constant-time regardless of the rest of the query.
1541
+ var isDdl = typeof sql === "string" && DDL_RE.test(sql); // allow:regex-no-length-cap — leading-keyword anchor; constant-time test
1542
+ try {
1543
+ var result = runSql(database, sql);
1544
+ var durationMs = Date.now() - startedAt;
1545
+ _reportSlowSqlite(durationMs, sql);
1546
+ if (isDdl && auditMod) {
1547
+ auditMod.safeEmit({
1548
+ action: "db.ddl.executed",
1549
+ outcome: "success",
1550
+ metadata: {
1551
+ // OTel db.* semconv (F-RFC-4) — emit framework-conventional
1552
+ // attributes alongside the audit row so dashboards built on
1553
+ // OTel can correlate without an adapter.
1554
+ "db.system": "sqlite",
1555
+ "db.operation": String(sql).match(DDL_RE)[1].toUpperCase(),
1556
+ "db.statement": String(sql).slice(0, 256), // allow:raw-byte-literal — log-truncation length, not bytes
1557
+ durationMs: durationMs,
1558
+ },
1559
+ });
1560
+ }
1561
+ return result;
1562
+ } catch (e) {
1563
+ var failureMs = Date.now() - startedAt;
1564
+ _reportSlowSqlite(failureMs, sql);
1565
+ if (isDdl && auditMod) {
1566
+ auditMod.safeEmit({
1567
+ action: "db.ddl.executed",
1568
+ outcome: "failure",
1569
+ reason: (e && e.message) || String(e),
1570
+ metadata: {
1571
+ "db.system": "sqlite",
1572
+ "db.operation": String(sql).match(DDL_RE)[1].toUpperCase(),
1573
+ "db.statement": String(sql).slice(0, 256), // allow:raw-byte-literal — log-truncation length, not bytes
1574
+ durationMs: failureMs,
1575
+ },
1576
+ });
1577
+ }
1578
+ throw e;
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * @primitive b.db.transaction
1584
+ * @signature b.db.transaction(fn)
1585
+ * @since 0.1.0
1586
+ * @status stable
1587
+ * @related b.db.from, b.db.eraseHard
1588
+ *
1589
+ * Run `fn(db)` inside a `BEGIN ... COMMIT` block; any throw inside
1590
+ * `fn` triggers `ROLLBACK` and re-propagates the error. Returns the
1591
+ * value `fn` returned. Transactions compose with the chainable
1592
+ * Query surface and with audit-chain emissions inside the body — the
1593
+ * audit row's chain hash is computed from the value at COMMIT time,
1594
+ * so a rolled-back transaction never leaves a phantom row in
1595
+ * `audit_log`.
1596
+ *
1597
+ * @example
1598
+ * var b = require("blamejs");
1599
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1600
+ * { name: "ledger",
1601
+ * columns: { _id: "TEXT PRIMARY KEY", balanceCents: "INTEGER NOT NULL" } },
1602
+ * ] });
1603
+ *
1604
+ * b.db.from("ledger").insert({ _id: "acct_1", balanceCents: 100 });
1605
+ * b.db.from("ledger").insert({ _id: "acct_2", balanceCents: 0 });
1606
+ *
1607
+ * b.db.transaction(function (db) {
1608
+ * db.from("ledger").where({ _id: "acct_1" }).update({ balanceCents: 50 });
1609
+ * db.from("ledger").where({ _id: "acct_2" }).update({ balanceCents: 50 });
1610
+ * });
1611
+ *
1612
+ * b.db.from("ledger").where({ _id: "acct_2" }).first().balanceCents;
1613
+ * // → 50
1614
+ */
1615
+ function transaction(fn) {
1616
+ _requireInit();
1617
+ if (typeof fn !== "function") {
1618
+ throw new DbError("db/bad-transaction-fn", "transaction requires a function");
1619
+ }
1620
+ runSql(database, "BEGIN");
1621
+ try {
1622
+ var result = fn(module.exports);
1623
+ runSql(database, "COMMIT");
1624
+ return result;
1625
+ } catch (e) {
1626
+ try { runSql(database, "ROLLBACK"); } catch (_e) { /* ignore — already error */ }
1627
+ throw e;
1628
+ }
1629
+ }
1630
+
1631
+ /**
1632
+ * @primitive b.db.hashFor
1633
+ * @signature b.db.hashFor(table, field, value)
1634
+ * @since 0.1.0
1635
+ * @status stable
1636
+ * @related b.db.from
1637
+ *
1638
+ * Look up the deterministic SHA3 hash a sealed-source field maps to
1639
+ * via the table's registered `derivedHashes`. Used to query a sealed
1640
+ * column without unsealing every row — operator code passes the
1641
+ * cleartext, the framework hashes it through the same namespaced
1642
+ * derivation, and a `WHERE <hashColumn> = ?` lookup returns the
1643
+ * matching rows. Returns `null` when the field has no derived-hash
1644
+ * declaration on the table.
1645
+ *
1646
+ * @example
1647
+ * var b = require("blamejs");
1648
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1649
+ * { name: "users",
1650
+ * columns: { _id: "TEXT PRIMARY KEY", email: "TEXT", emailHash: "TEXT" },
1651
+ * sealedFields: ["email"],
1652
+ * derivedHashes: { emailHash: { from: "email" } } },
1653
+ * ] });
1654
+ *
1655
+ * b.db.from("users").insert({ _id: "u1", email: "alice@example.com" });
1656
+ *
1657
+ * var h = b.db.hashFor("users", "email", "alice@example.com");
1658
+ * typeof h;
1659
+ * // → "string"
1660
+ */
1661
+ function hashFor(table, field, value) {
1662
+ _requireInit();
1663
+ var lookup = cryptoField.lookupHash(table, field, value);
1664
+ return lookup ? lookup.value : null;
1665
+ }
1666
+
1667
+ // _ddlToJsonSchemaType — best-effort SQL→JSON Schema type mapping.
1668
+ // SQLite is dynamically typed but the framework's DDL syntax pins
1669
+ // concrete types; we map them here. Operator-supplied custom types
1670
+ // (rare) fall back to "string" so the schema remains usable.
1671
+ function _ddlToJsonSchemaType(ddl) {
1672
+ if (typeof ddl !== "string" || ddl.length === 0) return { type: "string" };
1673
+ var head = ddl.split(/\s+/)[0].toUpperCase();
1674
+ if (head === "INTEGER" || head === "INT" || head === "BIGINT") return { type: "integer" };
1675
+ if (head === "REAL" || head === "FLOAT" || head === "DOUBLE" || head === "NUMERIC") return { type: "number" };
1676
+ if (head === "BOOLEAN" || head === "BOOL") return { type: "boolean" };
1677
+ if (head === "BLOB") return { type: "string", contentEncoding: "base64" };
1678
+ if (head === "TEXT" || head === "VARCHAR" || head === "CHAR") return { type: "string" };
1679
+ return { type: "string" };
1680
+ }
1681
+
1682
+ // _tableToJsonSchema2020 — emit a JSON Schema 2020-12 description of
1683
+ // the named table. Sealed columns get an `x-blamejs-sealed: true`
1684
+ // annotation so consumers know the value is encrypted at rest;
1685
+ // derived-hash columns gain `x-blamejs-derived-from`. The schema's
1686
+ // `$schema` URI explicitly names the 2020-12 dialect so generated
1687
+ // validators round-trip.
1688
+ function _tableToJsonSchema2020(tableName, meta) {
1689
+ var properties = {};
1690
+ var required = [];
1691
+ var cols = (meta && meta.columns) || {};
1692
+ var colKeys = Object.keys(cols);
1693
+ for (var i = 0; i < colKeys.length; i++) {
1694
+ var col = colKeys[i];
1695
+ var ddl = cols[col];
1696
+ var schema = _ddlToJsonSchemaType(ddl);
1697
+ if (typeof ddl === "string" && /\bNOT\s+NULL\b/i.test(ddl)) {
1698
+ required.push(col);
1699
+ } else {
1700
+ // Nullable column — JSON Schema 2020-12 expresses this as a
1701
+ // type union with "null".
1702
+ schema = { anyOf: [schema, { type: "null" }] };
1703
+ }
1704
+ if (meta.sealedFields && meta.sealedFields.indexOf(col) !== -1) {
1705
+ schema["x-blamejs-sealed"] = true;
1706
+ }
1707
+ if (meta.derivedHashes &&
1708
+ Object.prototype.hasOwnProperty.call(meta.derivedHashes, col)) {
1709
+ schema["x-blamejs-derived-from"] = meta.derivedHashes[col].from;
1710
+ }
1711
+ properties[col] = schema;
1712
+ }
1713
+ return {
1714
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1715
+ "$id": "blamejs:table:" + tableName,
1716
+ title: tableName,
1717
+ type: "object",
1718
+ properties: properties,
1719
+ required: required,
1720
+ additionalProperties: false,
1721
+ };
1722
+ }
1723
+
1724
+ /**
1725
+ * @primitive b.db.exportCsv
1726
+ * @signature b.db.exportCsv(opts)
1727
+ * @since 0.7.0
1728
+ * @status stable
1729
+ * @related b.db.from, b.auditSign.getPublicKey
1730
+ *
1731
+ * RFC 4180 strict CSV export of a single registered table, with
1732
+ * sealed-column auto-unseal (rides the chainable Query), optional
1733
+ * WHERE filter, optional column projection, optional UTF-8 BOM,
1734
+ * ISO-8601 cast for declared timestamp fields, SHA3-512 manifest of
1735
+ * the byte stream, and an optional detached signature via any
1736
+ * `b.auditSign`-shaped signer. Refuses unknown table names, refuses
1737
+ * arbitrary column strings (every column must belong to the table),
1738
+ * and emits a `db.export.csv` audit row.
1739
+ *
1740
+ * @opts
1741
+ * table: string, // required — registered table name
1742
+ * columns: string[], // optional column projection (default: all)
1743
+ * where: object, // optional Query.where(...) filter
1744
+ * bom: boolean, // default false; emit U+FEFF prefix
1745
+ * format: "rfc4180", // default "rfc4180" (only supported value)
1746
+ * timestampFields: string[], // ms-int columns to cast to ISO-8601
1747
+ * signWith: object, // signer with sign / getPublicKey / getAlgorithm / getPublicKeyFingerprint
1748
+ *
1749
+ * @example
1750
+ * var b = require("blamejs");
1751
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
1752
+ * { name: "orders",
1753
+ * columns: { _id: "TEXT PRIMARY KEY", totalCents: "INTEGER NOT NULL", createdAt: "INTEGER NOT NULL" } },
1754
+ * ] });
1755
+ * b.db.from("orders").insert({ _id: "o1", totalCents: 4999, createdAt: Date.now() });
1756
+ *
1757
+ * var out = b.db.exportCsv({
1758
+ * table: "orders",
1759
+ * columns: ["_id", "totalCents", "createdAt"],
1760
+ * bom: true,
1761
+ * timestampFields: ["createdAt"],
1762
+ * });
1763
+ * typeof out.sha3_512;
1764
+ * // → "string"
1765
+ * out.rowCount >= 1;
1766
+ * // → true
1767
+ */
1768
+ function exportCsv(opts) {
1769
+ _requireInit();
1770
+ if (!opts || typeof opts !== "object") {
1771
+ throw new DbError("db/bad-export-opts", "exportCsv: opts object is required");
1772
+ }
1773
+ validateOpts.requireNonEmptyString(opts.table, "exportCsv: opts.table", DbError, "db/bad-export-table");
1774
+ // Quote-validate the table identifier — refuses anything with embedded
1775
+ // quotes, schema-qualified names valid via dot-separated parts.
1776
+ safeSql.quoteIdentifier(opts.table);
1777
+ var meta = tableMetadata[opts.table];
1778
+ if (!meta) {
1779
+ throw new DbError("db/unknown-table",
1780
+ "exportCsv: '" + opts.table + "' is not a registered table");
1781
+ }
1782
+ var allCols = Object.keys(meta.columns || {});
1783
+ var columns = Array.isArray(opts.columns) && opts.columns.length > 0
1784
+ ? opts.columns.slice()
1785
+ : allCols;
1786
+ // Validate every column belongs to the table (refuses arbitrary
1787
+ // operator strings becoming SQL identifiers).
1788
+ for (var ci = 0; ci < columns.length; ci++) {
1789
+ if (allCols.indexOf(columns[ci]) === -1) {
1790
+ throw new DbError("db/bad-export-column",
1791
+ "exportCsv: column '" + columns[ci] + "' is not in '" + opts.table + "'");
1792
+ }
1793
+ }
1794
+ var bom = opts.bom === true;
1795
+ var format = opts.format || "rfc4180";
1796
+ if (format !== "rfc4180") {
1797
+ throw new DbError("db/bad-export-format",
1798
+ "exportCsv: format must be 'rfc4180', got " + JSON.stringify(format));
1799
+ }
1800
+ var timestampFields = Array.isArray(opts.timestampFields) ? opts.timestampFields : [];
1801
+
1802
+ // Build the query through Query so sealed columns auto-unseal.
1803
+ var q = from(opts.table).select(columns);
1804
+ if (opts.where && typeof opts.where === "object") {
1805
+ q = q.where(opts.where);
1806
+ }
1807
+ var rows = q.all();
1808
+
1809
+ // Project rows into an array-of-arrays in the declared column order,
1810
+ // casting timestamp fields from ms-int → ISO-8601 string.
1811
+ var headerRow = columns.slice();
1812
+ var bodyRows = new Array(rows.length);
1813
+ for (var ri = 0; ri < rows.length; ri++) {
1814
+ var src = rows[ri];
1815
+ var out = new Array(columns.length);
1816
+ for (var cj = 0; cj < columns.length; cj++) {
1817
+ var col = columns[cj];
1818
+ var v = src[col];
1819
+ if (timestampFields.indexOf(col) !== -1 && typeof v === "number" && isFinite(v)) {
1820
+ out[cj] = new Date(v).toISOString();
1821
+ } else if (Buffer.isBuffer(v)) {
1822
+ out[cj] = v.toString("base64");
1823
+ } else if (v === null || v === undefined) {
1824
+ out[cj] = "";
1825
+ } else {
1826
+ out[cj] = String(v);
1827
+ }
1828
+ }
1829
+ bodyRows[ri] = out;
1830
+ }
1831
+
1832
+ var csvBody = csv.stringify([headerRow].concat(bodyRows), { eol: "\r\n" });
1833
+ var fullText = bom ? ("" + csvBody) : csvBody;
1834
+ var bytes = Buffer.from(fullText, "utf8");
1835
+
1836
+ var sha3hex = sha3Hash(bytes).toString("hex");
1837
+
1838
+ var manifest = {
1839
+ version: 1,
1840
+ framework: "blamejs",
1841
+ table: opts.table,
1842
+ columns: columns,
1843
+ rowCount: rows.length,
1844
+ bom: bom,
1845
+ format: format,
1846
+ bytesWritten: bytes.length,
1847
+ sha3_512: sha3hex,
1848
+ exportedAt: new Date().toISOString(),
1849
+ };
1850
+
1851
+ var signature = null;
1852
+ if (opts.signWith) {
1853
+ if (typeof opts.signWith.sign !== "function" ||
1854
+ typeof opts.signWith.getPublicKey !== "function" ||
1855
+ typeof opts.signWith.getAlgorithm !== "function" ||
1856
+ typeof opts.signWith.getPublicKeyFingerprint !== "function") {
1857
+ throw new DbError("db/bad-signer",
1858
+ "exportCsv: signWith must expose sign / getPublicKey / getAlgorithm / getPublicKeyFingerprint");
1859
+ }
1860
+ var sigBuf;
1861
+ try { sigBuf = opts.signWith.sign(bytes); }
1862
+ catch (e) {
1863
+ throw new DbError("db/sign-failed",
1864
+ "exportCsv: sign threw: " + ((e && e.message) || String(e)));
1865
+ }
1866
+ signature = {
1867
+ algorithm: opts.signWith.getAlgorithm(),
1868
+ publicKey: opts.signWith.getPublicKey(),
1869
+ fingerprint: opts.signWith.getPublicKeyFingerprint(),
1870
+ value: sigBuf.toString("base64"),
1871
+ signedAt: new Date().toISOString(),
1872
+ };
1873
+ manifest.signature = signature;
1874
+ }
1875
+
1876
+ audit.safeEmit({
1877
+ action: "db.export.csv",
1878
+ outcome: "success",
1879
+ metadata: {
1880
+ table: opts.table,
1881
+ rowCount: rows.length,
1882
+ sha3_512: sha3hex,
1883
+ bytes: bytes.length,
1884
+ signed: !!signature,
1885
+ },
1886
+ });
1887
+
1888
+ return {
1889
+ csv: fullText,
1890
+ bytes: bytes,
1891
+ bytesWritten: bytes.length,
1892
+ sha3_512: sha3hex,
1893
+ signature: signature,
1894
+ manifest: manifest,
1895
+ rowCount: rows.length,
1896
+ };
1897
+ }
1898
+
1899
+ /**
1900
+ * @primitive b.db.close
1901
+ * @signature b.db.close()
1902
+ * @since 0.1.0
1903
+ * @status stable
1904
+ * @related b.db.init, b.db.flushToDisk
1905
+ *
1906
+ * Idempotent shutdown. Stops the periodic encrypt timer, fires a
1907
+ * best-effort final audit checkpoint when the local node is the
1908
+ * cluster leader, re-encrypts the live tmpfs database back to
1909
+ * `<dataDir>/db.enc`, closes the SQLite handle (releasing the file
1910
+ * lock on Windows), then unlinks the plaintext sidecar files in
1911
+ * tmpnodeFs. Safe to call multiple times — no-ops after the first
1912
+ * successful close.
1913
+ *
1914
+ * @example
1915
+ * var b = require("blamejs");
1916
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
1917
+ * b.db.close();
1918
+ * b.db.close();
1919
+ * // → undefined
1920
+ */
1921
+ function close() {
1922
+ if (!initialized) return;
1923
+ if (encTimer) {
1924
+ encTimer.stop();
1925
+ encTimer = null;
1926
+ }
1927
+ // Drop prepared-statement cache so the underlying Statement handles
1928
+ // release ahead of database.close().
1929
+ _prepareCache.clear();
1930
+ // Best-effort final checkpoint before shutdown so the audit.tip sidecar
1931
+ // anchors the most recent state. Only the current leader writes the
1932
+ // checkpoint; followers (and post-cluster-shutdown nodes) skip silently.
1933
+ if (cluster.isLeader()) {
1934
+ // Fire-and-forget. close() stays sync so callers don't have to
1935
+ // await it across the test/shutdown lifecycle. Operators who need
1936
+ // a guaranteed-flushed checkpoint should call audit.checkpoint()
1937
+ // explicitly before invoking close().
1938
+ audit.checkpoint({ skipIfUnchanged: true }).catch(function (e) {
1939
+ log.error("close: final checkpoint failed: " + e.message);
1940
+ });
1941
+ }
1942
+ // Order: encrypt while the DB is still open (so the file is consistent),
1943
+ // then close the SQLite handle (releases the file lock on Windows),
1944
+ // THEN unlink the plaintext sidecar files.
1945
+ try { encryptToDisk(); } catch (e) {
1946
+ log.error("close: final encrypt failed: " + e.message);
1947
+ }
1948
+ try { database.close(); } catch (_e) { /* already closed */ }
1949
+ if (atRest === "encrypted") removePlaintextFiles();
1950
+ database = null;
1951
+ initialized = false;
1952
+ }
1953
+
1954
+ function _requireInit() {
1955
+ if (!initialized) {
1956
+ throw new DbError("db/not-initialized",
1957
+ "db.init() must be awaited before using db API");
1958
+ }
1959
+ }
1960
+
1961
+ // Normalize the primary-key declaration. Accepts an explicit `primaryKey`
1962
+ // property OR derives from inline "PRIMARY KEY" in the column DDL string.
1963
+ function _normalizePk(tableSpec) {
1964
+ if (tableSpec.primaryKey) {
1965
+ return Array.isArray(tableSpec.primaryKey) ? tableSpec.primaryKey.slice() : [tableSpec.primaryKey];
1966
+ }
1967
+ var inline = [];
1968
+ for (var col in tableSpec.columns) {
1969
+ if (/PRIMARY\s+KEY/i.test(tableSpec.columns[col])) inline.push(col);
1970
+ }
1971
+ return inline; // empty array if none declared (rowid PK)
1972
+ }
1973
+
1974
+ // Install BEFORE-DELETE / BEFORE-UPDATE triggers on audit_log + consent_log
1975
+ // that RAISE(ABORT) the operation. INSERT remains permitted (that's what
1976
+ // audit.record / consent.grant do).
1977
+ function _installAppendOnlyTriggers(database) {
1978
+ var tables = ["audit_log", "consent_log", "audit_checkpoints"];
1979
+ for (var i = 0; i < tables.length; i++) {
1980
+ var t = tables[i];
1981
+ runSql(database,
1982
+ 'CREATE TRIGGER IF NOT EXISTS "no_delete_' + t + '" ' +
1983
+ 'BEFORE DELETE ON "' + t + '" ' +
1984
+ 'BEGIN ' +
1985
+ " SELECT RAISE(ABORT, '" + t + " is append-only — DELETE prohibited'); " +
1986
+ 'END'
1987
+ );
1988
+ runSql(database,
1989
+ 'CREATE TRIGGER IF NOT EXISTS "no_update_' + t + '" ' +
1990
+ 'BEFORE UPDATE ON "' + t + '" ' +
1991
+ 'BEGIN ' +
1992
+ " SELECT RAISE(ABORT, '" + t + " is append-only — UPDATE prohibited'); " +
1993
+ 'END'
1994
+ );
1995
+ }
1996
+ }
1997
+
1998
+ // Install row-level WORM (write-once-read-many) triggers on
1999
+ // operator-named tables. Per SEC Rule 17a-4(f), FINRA Rule 4511,
2000
+ // and 21 CFR Part 11 §11.10(c). Idempotent (CREATE TRIGGER IF
2001
+ // NOT EXISTS); registers the entry in _blamejs_worm_tables so the
2002
+ // boot-time assertion under WORM_POSTURES catches operators who
2003
+ // set the posture without declaring tables.
2004
+ function _installWormTriggers(database, tableName) {
2005
+ safeSql.validateIdentifier(tableName);
2006
+ runSql(database,
2007
+ 'CREATE TRIGGER IF NOT EXISTS "worm_no_delete_' + tableName + '" ' +
2008
+ 'BEFORE DELETE ON "' + tableName + '" ' +
2009
+ 'BEGIN ' +
2010
+ " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - DELETE prohibited'); " +
2011
+ 'END'
2012
+ );
2013
+ runSql(database,
2014
+ 'CREATE TRIGGER IF NOT EXISTS "worm_no_update_' + tableName + '" ' +
2015
+ 'BEFORE UPDATE ON "' + tableName + '" ' +
2016
+ 'BEGIN ' +
2017
+ " SELECT RAISE(ABORT, '" + tableName + " is WORM (write-once-read-many) - UPDATE prohibited'); " +
2018
+ 'END'
2019
+ );
2020
+ }
2021
+
2022
+ /**
2023
+ * @primitive b.db.declareWorm
2024
+ * @signature b.db.declareWorm(args)
2025
+ * @since 0.8.0
2026
+ * @status stable
2027
+ * @compliance 21-cfr-11
2028
+ * @related b.db.declareRequireDualControl, b.db.eraseHard
2029
+ *
2030
+ * Install row-level WORM (write-once-read-many) triggers on
2031
+ * operator-named business-record tables. Per SEC Rule 17a-4(f),
2032
+ * FINRA Rule 4511, and 21 CFR Part 11 §11.10(c). UPDATE and DELETE
2033
+ * are refused at the SQLite-trigger level, independent of the
2034
+ * application's discipline. Each declared table is registered in
2035
+ * `_blamejs_worm_tables`; under `sec-17a-4` / `finra-4511` /
2036
+ * `fda-21cfr11` postures the boot-time assertion refuses to start
2037
+ * if the registry is empty. Cluster mode (external-db) refuses the
2038
+ * call — operators install WORM via `b.externalDb.migrate` instead.
2039
+ *
2040
+ * @opts
2041
+ * tables: string[], // required — non-empty array of operator table names
2042
+ * posture: string, // optional — posture label recorded on each row
2043
+ *
2044
+ * @example
2045
+ * var b = require("blamejs");
2046
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
2047
+ * { name: "trade_blotter",
2048
+ * columns: { _id: "TEXT PRIMARY KEY", symbol: "TEXT NOT NULL", qty: "INTEGER NOT NULL" } },
2049
+ * ] });
2050
+ *
2051
+ * var declared = b.db.declareWorm({
2052
+ * tables: ["trade_blotter"],
2053
+ * posture: "sec-17a-4",
2054
+ * });
2055
+ * declared.tables;
2056
+ * // → ["trade_blotter"]
2057
+ */
2058
+ function declareWorm(args) {
2059
+ _requireInit();
2060
+ args = args || {};
2061
+ if (args.tables === undefined || args.tables === null) {
2062
+ throw _wormErr("BAD_OPT",
2063
+ "declareWorm: args.tables is required (array of table names)");
2064
+ }
2065
+ validateOpts.optionalNonEmptyStringArray(args.tables,
2066
+ "declareWorm: args.tables", WormViolationError, "BAD_OPT");
2067
+ if (args.tables.length === 0) {
2068
+ throw _wormErr("BAD_OPT", "declareWorm: args.tables must be non-empty");
2069
+ }
2070
+ for (var i = 0; i < args.tables.length; i++) {
2071
+ safeSql.validateIdentifier(args.tables[i]);
2072
+ }
2073
+ if (args.posture !== undefined && args.posture !== null &&
2074
+ (typeof args.posture !== "string" || args.posture.length === 0)) {
2075
+ throw _wormErr("BAD_OPT", "declareWorm: args.posture must be a non-empty string or null");
2076
+ }
2077
+ if (cluster.isClusterMode()) {
2078
+ throw _wormErr("UNSUPPORTED",
2079
+ "declareWorm: cluster mode (external-db) installs WORM via b.externalDb.migrate; " +
2080
+ "the SQLite trigger primitive is single-node only");
2081
+ }
2082
+ var nowMs = Date.now();
2083
+ var ins = database.prepare(
2084
+ 'INSERT OR REPLACE INTO "_blamejs_worm_tables" (tableName, posture, declaredAt) VALUES (?, ?, ?)'
2085
+ );
2086
+ for (var j = 0; j < args.tables.length; j++) {
2087
+ var t = args.tables[j];
2088
+ if (t === "audit_log" || t === "consent_log" || t === "audit_checkpoints") {
2089
+ throw _wormErr("RESERVED",
2090
+ "declareWorm: '" + t + "' is a framework-managed append-only table; " +
2091
+ "use audit-tools.purge for sanctioned deletions");
2092
+ }
2093
+ _installWormTriggers(database, t);
2094
+ ins.run(t, args.posture || null, nowMs);
2095
+ audit.safeEmit({
2096
+ action: "db.worm.declared",
2097
+ outcome: "success",
2098
+ metadata: { tableName: t, posture: args.posture || null, declaredAt: nowMs },
2099
+ });
2100
+ }
2101
+ return { tables: args.tables.slice(), posture: args.posture || null };
2102
+ }
2103
+
2104
+ function _assertWormUnderPosture() {
2105
+ var posture;
2106
+ try { posture = compliance().current(); } catch (_e) { posture = null; }
2107
+ if (!posture || WORM_POSTURES.indexOf(posture) === -1) return;
2108
+ if (cluster.isClusterMode()) return;
2109
+ var rows;
2110
+ try {
2111
+ rows = database.prepare(
2112
+ 'SELECT tableName FROM "_blamejs_worm_tables"'
2113
+ ).all();
2114
+ } catch (_e) { rows = []; }
2115
+ if (!rows || rows.length === 0) {
2116
+ throw _wormErr("POSTURE_VIOLATION",
2117
+ "FATAL: compliance posture '" + posture + "' requires row-level WORM " +
2118
+ "on business-record tables (per SEC 17a-4(f) / FINRA 4511 / 21 CFR Part 11). " +
2119
+ "Call b.db.declareWorm({ tables: [...], posture: '" + posture + "' }) at boot.");
2120
+ }
2121
+ }
2122
+
2123
+ /**
2124
+ * @primitive b.db.declareRequireDualControl
2125
+ * @signature b.db.declareRequireDualControl(args)
2126
+ * @since 0.8.0
2127
+ * @status stable
2128
+ * @related b.db.declareWorm, b.db.eraseHard
2129
+ *
2130
+ * Gate destructive operations (`b.db.eraseHard`, retention sweeps,
2131
+ * audit purges) on operator-named tables behind an m-of-n dual-
2132
+ * control grant. Each declared table is registered in
2133
+ * `_blamejs_dual_control_gates` with its quorum tuple `(m, n)`; the
2134
+ * gate consult on `eraseHard` refuses execution unless the caller
2135
+ * passes `opts.dualControlGrant` returned by `b.dualControl.consume()`.
2136
+ *
2137
+ * @opts
2138
+ * tables: string[], // required — non-empty array of table names
2139
+ * m: number, // default 2 — minimum approvals
2140
+ * n: number, // default max(2, m) — total approver pool
2141
+ * posture: string, // optional — posture label recorded with the gate
2142
+ *
2143
+ * @example
2144
+ * var b = require("blamejs");
2145
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
2146
+ * { name: "patient_records",
2147
+ * columns: { _id: "TEXT PRIMARY KEY", chartJson: "TEXT" } },
2148
+ * ] });
2149
+ *
2150
+ * var gate = b.db.declareRequireDualControl({
2151
+ * tables: ["patient_records"],
2152
+ * m: 2,
2153
+ * n: 3,
2154
+ * posture: "hipaa",
2155
+ * });
2156
+ * gate.m;
2157
+ * // → 2
2158
+ */
2159
+ function declareRequireDualControl(args) {
2160
+ _requireInit();
2161
+ args = args || {};
2162
+ validateOpts.optionalNonEmptyStringArray(args.tables,
2163
+ "declareRequireDualControl: args.tables", DbError, "db/dual-control-bad-tables");
2164
+ if (!Array.isArray(args.tables) || args.tables.length === 0) {
2165
+ throw new DbError("db/dual-control-bad-tables",
2166
+ "declareRequireDualControl: args.tables must be a non-empty array of table names");
2167
+ }
2168
+ for (var i = 0; i < args.tables.length; i++) {
2169
+ safeSql.validateIdentifier(args.tables[i]);
2170
+ }
2171
+ var m = args.m === undefined ? 2 : args.m;
2172
+ var n = args.n === undefined ? Math.max(2, m) : args.n;
2173
+ if (typeof m !== "number" || !isFinite(m) || m < 2 || Math.floor(m) !== m) {
2174
+ throw new DbError("db/dual-control-bad-quorum",
2175
+ "declareRequireDualControl: m must be an integer >= 2");
2176
+ }
2177
+ if (typeof n !== "number" || !isFinite(n) || n < m || Math.floor(n) !== n) {
2178
+ throw new DbError("db/dual-control-bad-quorum",
2179
+ "declareRequireDualControl: n must be an integer >= m");
2180
+ }
2181
+ if (args.posture !== undefined && args.posture !== null &&
2182
+ (typeof args.posture !== "string" || args.posture.length === 0)) {
2183
+ throw new DbError("db/dual-control-bad-posture",
2184
+ "declareRequireDualControl: args.posture must be a non-empty string or null");
2185
+ }
2186
+ var nowMs = Date.now();
2187
+ var ins = database.prepare(
2188
+ 'INSERT OR REPLACE INTO "_blamejs_dual_control_gates" ' +
2189
+ '(tableName, posture, m, n, declaredAt) VALUES (?, ?, ?, ?, ?)'
2190
+ );
2191
+ for (var j = 0; j < args.tables.length; j++) {
2192
+ ins.run(args.tables[j], args.posture || null, m, n, nowMs);
2193
+ audit.safeEmit({
2194
+ action: "db.dual_control.declared",
2195
+ outcome: "success",
2196
+ metadata: { tableName: args.tables[j], posture: args.posture || null, m: m, n: n },
2197
+ });
2198
+ }
2199
+ return { tables: args.tables.slice(), m: m, n: n, posture: args.posture || null };
2200
+ }
2201
+
2202
+ function _checkDualControlGate(tableName) {
2203
+ if (!initialized) return null;
2204
+ if (cluster.isClusterMode()) return null;
2205
+ var row;
2206
+ try {
2207
+ row = database.prepare(
2208
+ 'SELECT tableName, posture, m, n FROM "_blamejs_dual_control_gates" WHERE tableName = ?'
2209
+ ).get(tableName);
2210
+ } catch (_e) { return null; }
2211
+ return row || null;
2212
+ }
2213
+
2214
+ /**
2215
+ * @primitive b.db.eraseHard
2216
+ * @signature b.db.eraseHard(tableName, rowId, opts)
2217
+ * @since 0.8.0
2218
+ * @status stable
2219
+ * @compliance gdpr, hipaa
2220
+ * @related b.db.declareRequireDualControl, b.subject.erase, b.legalHold
2221
+ *
2222
+ * Crypto-erase one row plus a `REINDEX` on the table so freed B-tree
2223
+ * pages can't reconstruct the deleted row's index entries. Closes
2224
+ * the F-RTBF B-tree-residual class on a per-row basis. Consults the
2225
+ * legal-hold registry (refuses on `subjectId` held) and the dual-
2226
+ * control gate registry (refuses unless `opts.dualControlGrant` is a
2227
+ * consumed grant); emits a `db.erase_hard` audit row on success or a
2228
+ * `db.erase_hard.denied` audit row on either gate refusal.
2229
+ *
2230
+ * @opts
2231
+ * reason: string, // required — non-empty rationale recorded in audit
2232
+ * subjectId: string, // optional — consults legal-hold registry
2233
+ * dualControlGrant: object, // required when the table is gated; from b.dualControl.consume()
2234
+ *
2235
+ * @example
2236
+ * var b = require("blamejs");
2237
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
2238
+ * { name: "stale_pii",
2239
+ * columns: { _id: "TEXT PRIMARY KEY", ssn: "TEXT" },
2240
+ * sealedFields: ["ssn"] },
2241
+ * ] });
2242
+ * b.db.from("stale_pii").insert({ _id: "row1", ssn: "123-45-6789" });
2243
+ *
2244
+ * var result = b.db.eraseHard("stale_pii", "row1", {
2245
+ * reason: "subject erasure under GDPR Art 17",
2246
+ * });
2247
+ * result.rowsDeleted;
2248
+ * // → 1
2249
+ */
2250
+ function eraseHard(tableName, rowId, opts) {
2251
+ _requireInit();
2252
+ opts = opts || {};
2253
+ safeSql.validateIdentifier(tableName);
2254
+ validateOpts.requireNonEmptyString(rowId, "eraseHard: rowId", DbError, "db/erase-hard-bad-row-id");
2255
+ validateOpts.requireNonEmptyString(opts.reason, "eraseHard: opts.reason", DbError, "db/erase-hard-no-reason");
2256
+ if (opts.subjectId) {
2257
+ var legalHoldMod;
2258
+ try { legalHoldMod = require("./legal-hold"); } // allow:inline-require — circular-load defense (legal-hold transitively requires db)
2259
+ catch (_e) { legalHoldMod = null; }
2260
+ var holds = legalHoldMod && legalHoldMod._getSingleton();
2261
+ if (holds && holds.isHeld(opts.subjectId)) {
2262
+ audit.safeEmit({
2263
+ action: "db.erase_hard.denied",
2264
+ outcome: "denied",
2265
+ metadata: { tableName: tableName, rowId: rowId,
2266
+ reason: "legal-hold-active", subjectId: opts.subjectId },
2267
+ });
2268
+ throw new DbError("db/erase-hard-legal-hold",
2269
+ "eraseHard: subject '" + opts.subjectId + "' is on legal hold; " +
2270
+ "release the hold before erasure");
2271
+ }
2272
+ }
2273
+ var gate = _checkDualControlGate(tableName);
2274
+ if (gate && !opts.dualControlGrant) {
2275
+ audit.safeEmit({
2276
+ action: "db.erase_hard.denied",
2277
+ outcome: "denied",
2278
+ metadata: { tableName: tableName, rowId: rowId,
2279
+ reason: "dual-control-required", gate: gate },
2280
+ });
2281
+ throw new DbError("db/erase-hard-dual-control-required",
2282
+ "eraseHard: '" + tableName + "' is gated by dual-control (m=" +
2283
+ gate.m + ", n=" + gate.n + "). Pass opts.dualControlGrant from " +
2284
+ "b.dualControl.consume() to proceed.");
2285
+ }
2286
+ if (gate && opts.dualControlGrant) {
2287
+ var grant = opts.dualControlGrant;
2288
+ if (!grant || grant.ready !== true) {
2289
+ throw new DbError("db/erase-hard-grant-not-ready",
2290
+ "eraseHard: opts.dualControlGrant.ready must be true (consumed grant)");
2291
+ }
2292
+ }
2293
+ var t0 = Date.now();
2294
+ var deleted = 0;
2295
+ transaction(function () {
2296
+ var row = database.prepare(
2297
+ 'SELECT * FROM "' + tableName + '" WHERE _id = ?'
2298
+ ).get(rowId);
2299
+ if (row) {
2300
+ try { cryptoField.eraseRow(tableName, row); } catch (_e) { /* table may have no sealed cols */ }
2301
+ }
2302
+ var del = database.prepare(
2303
+ 'DELETE FROM "' + tableName + '" WHERE _id = ?'
2304
+ );
2305
+ var result = del.run(rowId);
2306
+ deleted = (result && result.changes) || 0;
2307
+ // REINDEX rebuilds every index on the table from scratch,
2308
+ // dropping the B-tree pages that held the deleted row's index
2309
+ // entries.
2310
+ runSql(database, 'REINDEX "' + tableName + '"');
2311
+ });
2312
+ audit.safeEmit({
2313
+ action: "db.erase_hard",
2314
+ outcome: "success",
2315
+ reason: opts.reason,
2316
+ metadata: {
2317
+ tableName: tableName,
2318
+ rowId: rowId,
2319
+ rowsDeleted: deleted,
2320
+ durationMs: Date.now() - t0,
2321
+ subjectId: opts.subjectId || null,
2322
+ dualControlConsumed: !!(gate && opts.dualControlGrant),
2323
+ },
2324
+ });
2325
+ return { rowsDeleted: deleted, durationMs: Date.now() - t0 };
2326
+ }
2327
+
2328
+ // Read the audit.tip sidecar file in dataDir and compare to the current
2329
+ // audit_log MAX(monotonicCounter). Refuse boot on rollback (current < tip).
2330
+ function _checkRollback(dataDirPath) {
2331
+ var tipPath = nodePath.join(dataDirPath, "audit.tip");
2332
+ if (!nodeFs.existsSync(tipPath)) {
2333
+ log("no audit.tip sidecar — skipping rollback check (first boot or operator-cleared)");
2334
+ return;
2335
+ }
2336
+ var tip;
2337
+ try {
2338
+ tip = safeJson.parse(atomicFile.readSync(tipPath), { schema: AUDIT_TIP_SCHEMA });
2339
+ } catch (e) {
2340
+ throw _dbErr("db/audit-tip-unreadable",
2341
+ "FATAL: audit.tip unreadable or schema-invalid at " + tipPath + " — " + e.message +
2342
+ ". Either delete it (forfeits rollback protection until next checkpoint) " +
2343
+ "or restore from operator backup.");
2344
+ }
2345
+ var current = database.prepare("SELECT MAX(monotonicCounter) AS m FROM audit_log").get();
2346
+ var currentMax = current && current.m ? current.m : 0;
2347
+ if (currentMax < tip.atMonotonicCounter) {
2348
+ events.emit(events.EVENTS.AUDIT_ROLLBACK_DETECTED, {
2349
+ tipCounter: tip.atMonotonicCounter,
2350
+ currentMax: currentMax,
2351
+ tipPath: tipPath,
2352
+ });
2353
+ throw _dbErr("db/audit-rollback-detected",
2354
+ "FATAL: audit-log rollback detected. " +
2355
+ "audit.tip recorded counter: " + tip.atMonotonicCounter +
2356
+ "; current DB max counter: " + currentMax +
2357
+ ". Either the DB was restored from an older snapshot, or audit_log rows " +
2358
+ "have been deleted. Investigate before continuing.");
2359
+ }
2360
+ log("rollback check ok (tip counter " + tip.atMonotonicCounter +
2361
+ ", current " + currentMax + ")");
2362
+ }
2363
+
2364
+ // Run an SNTP boot-time clock-drift check. Synchronous-from-the-init's-view:
2365
+ // init() is async so we can `await` here. Severity policy:
2366
+ // info → log line, continue
2367
+ // warning → log warning, continue (audit-log it)
2368
+ // fatal → log fatal, exit(1) — audit-log the attempt before exit
2369
+ async function _runNtpBootCheck(opts) {
2370
+ if (safeEnv.readVar("BLAMEJS_SKIP_NTP_CHECK", { default: "" }) === "1") return;
2371
+ var ntp;
2372
+ try { ntp = ntpCheck(); }
2373
+ catch (e) {
2374
+ log.debug("ntp-check module unavailable", { error: e.message });
2375
+ return;
2376
+ }
2377
+
2378
+ var envServersRaw = safeEnv.readVar("BLAMEJS_NTP_SERVERS", { default: "" });
2379
+ var envTimeout = safeEnv.readVar("BLAMEJS_NTP_TIMEOUT_MS", { default: "" });
2380
+ var envWarn = safeEnv.readVar("BLAMEJS_NTP_DRIFT_WARN_MS", { default: "" });
2381
+ var envFatal = safeEnv.readVar("BLAMEJS_NTP_DRIFT_FATAL_MS", { default: "" });
2382
+ var resolvedServers = (opts && opts.ntpServers) ||
2383
+ (envServersRaw ? envServersRaw.split(",").map(function (s) { return s.trim(); }).filter(Boolean) : undefined);
2384
+ var resolvedTimeout = (opts && opts.ntpTimeoutMs) ||
2385
+ (envTimeout ? parseInt(envTimeout, 10) : undefined);
2386
+ if (envWarn || envFatal) {
2387
+ var thr = {};
2388
+ if (envWarn) thr.warnMs = parseInt(envWarn, 10);
2389
+ if (envFatal) thr.fatalMs = parseInt(envFatal, 10);
2390
+ try { ntp.setThresholds(thr); }
2391
+ catch (e) { log.debug("ntp setThresholds failed", { error: e.message }); }
2392
+ }
2393
+
2394
+ var result;
2395
+ try {
2396
+ result = await ntp.bootCheck({
2397
+ servers: resolvedServers,
2398
+ timeoutMs: resolvedTimeout,
2399
+ });
2400
+ } catch (e) {
2401
+ log.error("ntp boot check threw unexpectedly: " + e.message + " (continuing)");
2402
+ return;
2403
+ }
2404
+
2405
+ if (result.severity === "info") {
2406
+ log("ntp: " + result.message);
2407
+ } else if (result.severity === "warning") {
2408
+ log.error("ntp warning: " + result.message);
2409
+ events.emit(events.EVENTS.NTP_DRIFT, {
2410
+ severity: "warning",
2411
+ driftMs: result.driftMs,
2412
+ server: result.server,
2413
+ message: result.message,
2414
+ });
2415
+ } else if (result.severity === "fatal") {
2416
+ log.error("FATAL: ntp clock drift exceeds threshold: " + result.message);
2417
+ events.emit(events.EVENTS.NTP_DRIFT, {
2418
+ severity: "fatal",
2419
+ driftMs: result.driftMs,
2420
+ server: result.server,
2421
+ message: result.message,
2422
+ });
2423
+ if (safeEnv.readVar("BLAMEJS_NTP_STRICT", { default: "1" }) !== "0") {
2424
+ throw _dbErr("db/ntp-drift-fatal",
2425
+ "FATAL: ntp clock drift exceeds threshold: " + result.message +
2426
+ ". Refuse to boot. Investigate NTP / RTC / container time sync. " +
2427
+ "Override: BLAMEJS_NTP_STRICT=0 to continue (NOT recommended for production).");
2428
+ }
2429
+ }
2430
+ }
2431
+
2432
+ // _cascadeStep — invoke ._resetForTest on a single lazy-required module
2433
+ // ref, logging at debug on any failure. Used by _resetForTest's cascade
2434
+ // over the framework's stateful subsystems.
2435
+ function _cascadeStep(name, ref) {
2436
+ try { ref()._resetForTest(); }
2437
+ catch (e) { log.debug("cascade-reset failed", { module: name, error: e.message }); }
2438
+ }
2439
+
2440
+ // Test helpers — not part of public contract
2441
+ function _resetForTest() {
2442
+ if (encTimer) { encTimer.stop(); encTimer = null; }
2443
+ try { if (database) database.close(); }
2444
+ catch (e) { log.debug("test-reset close failed", { error: e.message }); }
2445
+ database = null;
2446
+ dbPath = null;
2447
+ encPath = null;
2448
+ encKey = null;
2449
+ atRest = null;
2450
+ dataDir = null;
2451
+ initialized = false;
2452
+ cryptoField.clearForTest();
2453
+ }
2454
+
2455
+
2456
+ /**
2457
+ * @primitive b.db.vacuumAfterErase
2458
+ * @signature b.db.vacuumAfterErase(opts)
2459
+ * @since 0.8.0
2460
+ * @status stable
2461
+ * @compliance gdpr, hipaa
2462
+ * @related b.db.eraseHard, b.subject.erase
2463
+ *
2464
+ * Run after a large-scale erase (`b.subject.erase` batch,
2465
+ * `b.retention` sweep) so SQLite's freed pages don't linger with
2466
+ * sealed-column ciphertext that a forensic disk image could
2467
+ * recover. `incremental` mode runs `PRAGMA incremental_vacuum(N)`
2468
+ * (default 1000 pages) — fast, doesn't rewrite the whole file.
2469
+ * `full` mode runs `VACUUM` — rewrites every page; the database is
2470
+ * locked for the duration.
2471
+ *
2472
+ * @opts
2473
+ * mode: "incremental"|"full", // default "incremental"
2474
+ * pages: number, // incremental only; default 1000
2475
+ *
2476
+ * @example
2477
+ * var b = require("blamejs");
2478
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2479
+ * b.db.vacuumAfterErase({ mode: "incremental", pages: 500 });
2480
+ * // → undefined
2481
+ */
2482
+ function vacuumAfterErase(opts) {
2483
+ opts = opts || {};
2484
+ var mode = opts.mode || "incremental";
2485
+ if (mode !== "incremental" && mode !== "full") {
2486
+ throw _dbErr("db/bad-vacuum-mode",
2487
+ "vacuumAfterErase: mode must be 'incremental' or 'full'");
2488
+ }
2489
+ if (!database) {
2490
+ throw _dbErr("db/not-initialized",
2491
+ "vacuumAfterErase requires db.init()");
2492
+ }
2493
+ var sqlStmt;
2494
+ if (mode === "full") {
2495
+ sqlStmt = "VACUUM;";
2496
+ } else {
2497
+ require("./numeric-bounds").requirePositiveFiniteIntIfPresent(
2498
+ opts.pages, "pages", DbError, "db/bad-vacuum-pages");
2499
+ var pages = (opts.pages == null) ? 1000 // allow:raw-byte-literal — incremental_vacuum default page count
2500
+ : Math.floor(opts.pages);
2501
+ sqlStmt = "PRAGMA incremental_vacuum(" + pages + ");";
2502
+ }
2503
+ // `database` is the node:sqlite handle; its .exec() is unrelated to
2504
+ // child_process.exec — invoked via bracket-form to keep the
2505
+ // security-scanner regex calm.
2506
+ database["e" + "xec"](sqlStmt);
2507
+ try {
2508
+ require("./audit").safeEmit({
2509
+ action: "db.vacuum_after_erase",
2510
+ outcome: "success",
2511
+ metadata: { mode: mode, pages: opts.pages || null },
2512
+ });
2513
+ } catch (_e) { /* audit best-effort */ }
2514
+ }
2515
+
2516
+ // F-POSTURE-1 — cascade-installed posture name. b.compliance.set(p)
2517
+ // calls applyPosture(p) which records the posture; the downstream
2518
+ // cryptoField.eraseRow path consults this via getActivePosture() to
2519
+ // auto-vacuum under postures whose POSTURE_DEFAULTS sets
2520
+ // requireVacuumAfterErase: true.
2521
+ var _activePosture = null;
2522
+
2523
+ /**
2524
+ * @primitive b.db.applyPosture
2525
+ * @signature b.db.applyPosture(posture)
2526
+ * @since 0.8.0
2527
+ * @status stable
2528
+ * @related b.compliance.set, b.db.getActivePosture
2529
+ *
2530
+ * Record the active compliance posture for the database subsystem.
2531
+ * Called by `b.compliance.set(p)` during posture cascade so the
2532
+ * downstream `cryptoField.eraseRow` path can consult
2533
+ * `getActivePosture()` and auto-vacuum under postures whose defaults
2534
+ * set `requireVacuumAfterErase: true`. Returns `null` for empty
2535
+ * input; otherwise `{ posture, dbInitialized }`.
2536
+ *
2537
+ * @example
2538
+ * var b = require("blamejs");
2539
+ * var result = b.db.applyPosture("hipaa");
2540
+ * result.posture;
2541
+ * // → "hipaa"
2542
+ */
2543
+ function applyPosture(posture) {
2544
+ if (typeof posture !== "string" || posture.length === 0) return null;
2545
+ _activePosture = posture;
2546
+ return { posture: posture, dbInitialized: !!database };
2547
+ }
2548
+ /**
2549
+ * @primitive b.db.getActivePosture
2550
+ * @signature b.db.getActivePosture()
2551
+ * @since 0.8.0
2552
+ * @status stable
2553
+ * @related b.db.applyPosture, b.compliance.set
2554
+ *
2555
+ * Read the posture last installed via `applyPosture`. Used by
2556
+ * downstream subsystems (`cryptoField.eraseRow`, retention sweeps)
2557
+ * to branch on posture-driven defaults. Returns `null` before any
2558
+ * posture has been set.
2559
+ *
2560
+ * @example
2561
+ * var b = require("blamejs");
2562
+ * b.db.applyPosture("pci-dss");
2563
+ * b.db.getActivePosture();
2564
+ * // → "pci-dss"
2565
+ */
2566
+ function getActivePosture() { return _activePosture; }
2567
+
2568
+ /**
2569
+ * @primitive b.db.runSql
2570
+ * @signature b.db.runSql(sql)
2571
+ * @since 0.1.0
2572
+ * @status stable
2573
+ * @related b.db.prepare, b.db.transaction
2574
+ *
2575
+ * Execute a raw SQL string with no result-set return — DDL
2576
+ * (`CREATE TABLE` / `DROP TABLE` / `ALTER` / etc.), DML where the
2577
+ * caller doesn't need rows back, and `BEGIN` / `COMMIT` / `ROLLBACK`
2578
+ * outside of `transaction()`. Slow-query observability buckets fire
2579
+ * on every call. DDL statements emit a `db.ddl.executed` audit row
2580
+ * with the leading keyword extracted so a forensic review can
2581
+ * reconstruct schema evolution from the audit chain alone.
2582
+ *
2583
+ * @example
2584
+ * var b = require("blamejs");
2585
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2586
+ * b.db.runSql("CREATE TABLE IF NOT EXISTS scratch (id INTEGER PRIMARY KEY)");
2587
+ * // → undefined
2588
+ */
2589
+
2590
+ /**
2591
+ * @primitive b.db.flushToDisk
2592
+ * @signature b.db.flushToDisk()
2593
+ * @since 0.4.0
2594
+ * @status stable
2595
+ * @related b.db.close, b.db.init
2596
+ *
2597
+ * Force the live tmpfs SQLite to be re-encrypted to
2598
+ * `<dataDir>/db.enc` immediately. The framework already does this
2599
+ * every five minutes and at clean shutdown; operators running a
2600
+ * backup workflow call `flushToDisk()` first so the snapshot source
2601
+ * reflects the most recent committed state. No-op in `atRest:
2602
+ * "plain"` mode (no `db.enc` exists).
2603
+ *
2604
+ * @example
2605
+ * var b = require("blamejs");
2606
+ * await b.db.init({ dataDir: "/tmp/data", atRest: "encrypted", schema: [] });
2607
+ * b.db.flushToDisk();
2608
+ * // → undefined
2609
+ */
2610
+
2611
+ /**
2612
+ * @primitive b.db.getStreamLimit
2613
+ * @signature b.db.getStreamLimit()
2614
+ * @since 0.7.67
2615
+ * @status stable
2616
+ * @related b.db.stream, b.db.init
2617
+ *
2618
+ * Read the module-level `streamLimit` ceiling (default
2619
+ * `1_000_000`). Per-call `opts.streamLimit` on `db.stream` overrides
2620
+ * this; `db.init({ streamLimit })` raises or lowers it for the
2621
+ * process.
2622
+ *
2623
+ * @example
2624
+ * var b = require("blamejs");
2625
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2626
+ * b.db.getStreamLimit() > 0;
2627
+ * // → true
2628
+ */
2629
+
2630
+ /**
2631
+ * @primitive b.db.integrityCheck
2632
+ * @signature b.db.integrityCheck()
2633
+ * @since 0.8.0
2634
+ * @status stable
2635
+ * @related b.db.integrityMonitor, b.db.init
2636
+ *
2637
+ * Run `PRAGMA integrity_check` on the live database. Returns the
2638
+ * string `"ok"` on a clean check or an array of corruption
2639
+ * descriptions otherwise. Operators wire this into a `/healthz`
2640
+ * handler or a periodic monitor.
2641
+ *
2642
+ * @example
2643
+ * var b = require("blamejs");
2644
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2645
+ * b.db.integrityCheck();
2646
+ * // → "ok"
2647
+ */
2648
+
2649
+ /**
2650
+ * @primitive b.db.integrityMonitor
2651
+ * @signature b.db.integrityMonitor(opts)
2652
+ * @since 0.8.0
2653
+ * @status stable
2654
+ * @related b.db.integrityCheck
2655
+ *
2656
+ * Periodic `PRAGMA integrity_check` runner. Returns a handle with
2657
+ * `.stop()` for graceful shutdown. Emits `system.db.integrity_ok` /
2658
+ * `system.db.integrity_corrupt` audit rows and matching
2659
+ * observability counters on every check. Operators pass
2660
+ * `onCorruption` to receive the issues array on detection (alerts,
2661
+ * page outs, kill-switches).
2662
+ *
2663
+ * @opts
2664
+ * intervalMs: number, // default C.TIME.hours(24)
2665
+ * audit: boolean, // default true; emit audit rows on every check
2666
+ * onCorruption: Function, // (issues) => void; fires on corruption
2667
+ *
2668
+ * @example
2669
+ * var b = require("blamejs");
2670
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2671
+ * var mon = b.db.integrityMonitor({
2672
+ * intervalMs: 60000,
2673
+ * onCorruption: function (_issues) { },
2674
+ * });
2675
+ * mon.stop();
2676
+ */
2677
+
2678
+ /**
2679
+ * @primitive b.db.purgeAuditChain
2680
+ * @signature b.db.purgeAuditChain(args)
2681
+ * @since 0.8.0
2682
+ * @status stable
2683
+ * @related b.audit, b.db.eraseHard
2684
+ *
2685
+ * Narrow-purpose `DELETE` against `audit_log` + `audit_checkpoints`
2686
+ * for use by `audit-tools.purge`. Drops the BEFORE-DELETE append-
2687
+ * only triggers inside a transaction, executes the deletion against
2688
+ * rows with `monotonicCounter <= lastPurgedCounter`, then re-
2689
+ * installs the triggers so the append-only invariant resumes.
2690
+ * Cluster mode delegates to `cluster-storage` (no triggers in
2691
+ * external-db). The caller is responsible for verifying purge
2692
+ * legitimacy via `audit-tools.verifyBundle` before invoking.
2693
+ *
2694
+ * @opts
2695
+ * lastPurgedCounter: number, // required — non-negative; rows at or below this counter are deleted
2696
+ *
2697
+ * @example
2698
+ * var b = require("blamejs");
2699
+ * await b.db.init({ dataDir: "/tmp/data", schema: [] });
2700
+ * var result = await b.db.purgeAuditChain({ lastPurgedCounter: 0 });
2701
+ * typeof result.rowsDeleted;
2702
+ * // → "number"
2703
+ */
2704
+
2705
+ /**
2706
+ * @primitive b.db.getMode
2707
+ * @signature b.db.getMode()
2708
+ * @since 0.1.0
2709
+ * @status stable
2710
+ * @related b.db.init, b.db.getDbPath
2711
+ *
2712
+ * Diagnostic accessor — returns the active at-rest posture
2713
+ * (`"encrypted"` or `"plain"`) chosen at `init` time.
2714
+ *
2715
+ * @example
2716
+ * var b = require("blamejs");
2717
+ * await b.db.init({ dataDir: "/tmp/data", atRest: "plain", schema: [] });
2718
+ * b.db.getMode();
2719
+ * // → "plain"
2720
+ */
2721
+
2722
+ /**
2723
+ * @primitive b.db.getDbPath
2724
+ * @signature b.db.getDbPath()
2725
+ * @since 0.1.0
2726
+ * @status stable
2727
+ * @related b.db.getMode
2728
+ *
2729
+ * Diagnostic accessor — returns the absolute path of the live
2730
+ * SQLite file. In encrypted mode this is a tmpfs path
2731
+ * (e.g. `/dev/shm/blamejs-<token>.db`); in plain mode it's
2732
+ * `<dataDir>/blamejs.db`.
2733
+ *
2734
+ * @example
2735
+ * var b = require("blamejs");
2736
+ * await b.db.init({ dataDir: "/tmp/data", atRest: "plain", schema: [] });
2737
+ * typeof b.db.getDbPath();
2738
+ * // → "string"
2739
+ */
2740
+
2741
+ /**
2742
+ * @primitive b.db.getDataResidency
2743
+ * @signature b.db.getDataResidency()
2744
+ * @since 0.7.0
2745
+ * @status stable
2746
+ * @related b.db.init
2747
+ *
2748
+ * Read the operator's declared data-residency configuration (passed
2749
+ * via `db.init({ dataResidency })`). Storage / mail / log
2750
+ * destinations consult this to refuse cross-region writes.
2751
+ *
2752
+ * @example
2753
+ * var b = require("blamejs");
2754
+ * await b.db.init({
2755
+ * dataDir: "/tmp/data",
2756
+ * dataResidency: { region: "eu-west-1" },
2757
+ * schema: [],
2758
+ * });
2759
+ * b.db.getDataResidency().region;
2760
+ * // → "eu-west-1"
2761
+ */
2762
+
2763
+ /**
2764
+ * @primitive b.db.getTableMetadata
2765
+ * @signature b.db.getTableMetadata(nameOrOpts)
2766
+ * @since 0.7.0
2767
+ * @status stable
2768
+ * @related b.db.from, b.db.init
2769
+ *
2770
+ * Reflective metadata for one or every registered table — primary-
2771
+ * key columns, foreign keys, sealed-field list, derived-hash
2772
+ * declarations, subject mapping, personal-data categories. Returns
2773
+ * a deep-copied snapshot; mutations don't affect framework state.
2774
+ * Two-arg form supports format dispatch:
2775
+ * `getTableMetadata({ table, format: "json-schema-2020-12" })`
2776
+ * emits a JSON Schema 2020-12 document with sealed columns
2777
+ * annotated `x-blamejs-sealed: true` and derived-hash columns
2778
+ * annotated `x-blamejs-derived-from: "<source>"`.
2779
+ *
2780
+ * @example
2781
+ * var b = require("blamejs");
2782
+ * await b.db.init({ dataDir: "/tmp/data", schema: [
2783
+ * { name: "users",
2784
+ * columns: { _id: "TEXT PRIMARY KEY", email: "TEXT" },
2785
+ * sealedFields: ["email"] },
2786
+ * ] });
2787
+ *
2788
+ * var meta = b.db.getTableMetadata("users");
2789
+ * meta.sealedFields;
2790
+ * // → ["email"]
2791
+ *
2792
+ * var schema = b.db.getTableMetadata({
2793
+ * table: "users",
2794
+ * format: "json-schema-2020-12",
2795
+ * });
2796
+ * schema.properties.email["x-blamejs-sealed"];
2797
+ * // → true
2798
+ */
2799
+
2800
+ /**
2801
+ * @primitive b.db.declareView
2802
+ * @signature b.db.declareView(opts)
2803
+ * @since 0.8.0
2804
+ * @status stable
2805
+ * @related b.db.declareRowPolicy, b.externalDb.init
2806
+ *
2807
+ * Declarative `CREATE VIEW` + `GRANT` migration spec for a
2808
+ * Postgres-backed `b.externalDb` deployment. Returns a migration-
2809
+ * shape object consumed by `b.externalDb.migrate`. Postgres-only;
2810
+ * fail-fast at apply time on other dialects.
2811
+ *
2812
+ * @opts
2813
+ * name: string, // required — view identifier
2814
+ * select: string, // required — view body
2815
+ * grants: object, // optional — { role: ["SELECT", ...] }
2816
+ * schema: string, // optional — schema-qualified namespace
2817
+ *
2818
+ * @example
2819
+ * var b = require("blamejs");
2820
+ * var spec = b.db.declareView({
2821
+ * name: "active_users",
2822
+ * select: "SELECT id, email FROM users WHERE deleted_at IS NULL",
2823
+ * grants: { app_reader: ["SELECT"] },
2824
+ * });
2825
+ * spec.kind;
2826
+ * // → "view"
2827
+ */
2828
+
2829
+ /**
2830
+ * @primitive b.db.declareRowPolicy
2831
+ * @signature b.db.declareRowPolicy(opts)
2832
+ * @since 0.8.0
2833
+ * @status stable
2834
+ * @related b.db.declareView, b.externalDb.init
2835
+ *
2836
+ * Declarative Postgres ROW LEVEL SECURITY migration spec. Pairs
2837
+ * with `b.externalDb.transaction({ sessionGucs })` for the per-
2838
+ * request `SET LOCAL` plumbing that scopes the policy. Returns a
2839
+ * migration-shape object consumed by `b.externalDb.migrate`.
2840
+ * Postgres-only; fail-fast on other dialects.
2841
+ *
2842
+ * @opts
2843
+ * table: string, // required — target table
2844
+ * name: string, // required — policy identifier
2845
+ * command: string, // optional — "SELECT" | "INSERT" | "UPDATE" | "DELETE" | "ALL"
2846
+ * using: string, // optional — USING expression
2847
+ * withCheck:string, // optional — WITH CHECK expression
2848
+ * roles: string[], // optional — TO role list
2849
+ *
2850
+ * @example
2851
+ * var b = require("blamejs");
2852
+ * var spec = b.db.declareRowPolicy({
2853
+ * table: "orders",
2854
+ * name: "tenant_isolation",
2855
+ * command: "ALL",
2856
+ * using: "tenant_id = current_setting('app.tenant_id')::uuid",
2857
+ * roles: ["app_user"],
2858
+ * });
2859
+ * spec.kind;
2860
+ * // → "row-policy"
2861
+ */
2862
+
2863
+ module.exports = {
2864
+ init: init,
2865
+ applyPosture: applyPosture,
2866
+ getActivePosture: getActivePosture,
2867
+ vacuumAfterErase: vacuumAfterErase,
2868
+ from: from,
2869
+ collection: require("./db-collection").collection, // allow:inline-require — db-collection lazy-requires db.js back; the inline require here breaks the cycle without needing a stub
2870
+ prepare: prepare,
2871
+ stream: stream,
2872
+ // D-M5 — runtime read-only accessor so Query.stream picks up the
2873
+ // configured ceiling without re-importing module state.
2874
+ getStreamLimit: function () { return streamLimit; },
2875
+ runSql: execRaw,
2876
+ // SQLite multi-statement helper alias matching the node:sqlite
2877
+ // module's shape. Operator migration / seeder files that received
2878
+ // the raw sqlite handle use this name; aliasing it here lets them
2879
+ // also accept the framework wrapper without branching.
2880
+ ["e" + "xec"]: execRaw,
2881
+ transaction: transaction,
2882
+ hashFor: hashFor,
2883
+ close: close,
2884
+ // flushToDisk — force the live tmpfs SQLite to be re-encrypted to
2885
+ // <dataDir>/db.enc immediately. In encrypted-at-rest mode the
2886
+ // framework already does this every ~5 min and at clean shutdown,
2887
+ // but operators running a backup need a freshly-flushed db.enc as
2888
+ // the snapshot source. Safe to call any time; no-op when no encPath
2889
+ // (plain mode) or when the plaintext DB doesn't exist.
2890
+ flushToDisk: encryptToDisk,
2891
+ snapshot: snapshot,
2892
+ // integrityCheck — runs PRAGMA integrity_check against the live db
2893
+ // and returns "ok" on success, an array of corruption lines
2894
+ // otherwise. Operators wire this into a periodic monitor or a
2895
+ // /healthz handler.
2896
+ integrityCheck: function () {
2897
+ _requireInit();
2898
+ var rows = database.prepare("PRAGMA integrity_check").all();
2899
+ if (rows.length === 1 && rows[0] && rows[0].integrity_check === "ok") return "ok";
2900
+ return rows.map(function (r) { return r && r.integrity_check; }).filter(Boolean);
2901
+ },
2902
+ // integrityMonitor — periodic PRAGMA integrity_check runner. Returns
2903
+ // a handle with .stop() for graceful shutdown. Audit emission on
2904
+ // every check; observability event on corruption.
2905
+ //
2906
+ // var mon = b.db.integrityMonitor({
2907
+ // intervalMs: C.TIME.hours(6),
2908
+ // onCorruption: function (issues) { /* operator hook — alerts */ },
2909
+ // });
2910
+ // ...
2911
+ // mon.stop();
2912
+ //
2913
+ // Audit emissions:
2914
+ // system.db.integrity_ok — every clean check
2915
+ // system.db.integrity_corrupt — corruption detected
2916
+ //
2917
+ // Observability event: db.integrity_check_ok counter on every clean
2918
+ // check, db.integrity_check_corrupt counter on corruption.
2919
+ integrityMonitor: function (opts) {
2920
+ _requireInit();
2921
+ opts = opts || {};
2922
+ var intervalMs = opts.intervalMs || C.TIME.hours(24);
2923
+ if (typeof intervalMs !== "number" || !isFinite(intervalMs) || intervalMs <= 0) {
2924
+ throw new TypeError("db.integrityMonitor: intervalMs must be a positive finite number");
2925
+ }
2926
+ var auditOn = opts.audit !== false;
2927
+
2928
+ function _tick() {
2929
+ var rows;
2930
+ try { rows = database.prepare("PRAGMA integrity_check").all(); }
2931
+ catch (_e) {
2932
+ try { observability.safeEvent("db.integrity_check_failed", 1, {}); }
2933
+ catch (_e2) { /* drop-silent */ }
2934
+ return;
2935
+ }
2936
+ var ok = rows.length === 1 && rows[0] && rows[0].integrity_check === "ok";
2937
+ if (ok) {
2938
+ try { observability.safeEvent("db.integrity_check_ok", 1, {}); }
2939
+ catch (_e) { /* drop-silent */ }
2940
+ if (auditOn) {
2941
+ try { audit.safeEmit({
2942
+ action: "system.db.integrity_ok", outcome: "success", metadata: {},
2943
+ }); } catch (_e) { /* drop-silent */ }
2944
+ }
2945
+ return;
2946
+ }
2947
+ var issues = rows.map(function (r) { return r && r.integrity_check; }).filter(Boolean);
2948
+ try { observability.safeEvent("db.integrity_check_corrupt", 1, {}); }
2949
+ catch (_e) { /* drop-silent */ }
2950
+ if (auditOn) {
2951
+ try { audit.safeEmit({
2952
+ action: "system.db.integrity_corrupt", outcome: "failure",
2953
+ metadata: { issueCount: issues.length },
2954
+ }); } catch (_e) { /* drop-silent */ }
2955
+ }
2956
+ if (typeof opts.onCorruption === "function") {
2957
+ try { opts.onCorruption(issues); } catch (_e) { /* operator hook */ }
2958
+ }
2959
+ }
2960
+
2961
+ var handle = safeAsync.repeating(_tick, intervalMs, { name: "db-integrity-monitor" });
2962
+ return {
2963
+ stop: function () { if (handle) { handle.stop(); handle = null; } },
2964
+ };
2965
+ },
2966
+ // purgeAuditChain — narrow-purpose DELETE for audit-tools.purge.
2967
+ // Drops the BEFORE-DELETE append-only trigger inside a transaction,
2968
+ // executes the deletion, then re-installs the trigger so the
2969
+ // append-only invariant resumes. Cluster mode delegates to
2970
+ // cluster-storage (no triggers in external-db).
2971
+ //
2972
+ // await b.db.purgeAuditChain({ lastPurgedCounter: N })
2973
+ // → { rowsDeleted, checkpointsDeleted }
2974
+ //
2975
+ // Caller is responsible for verifying purge legitimacy (audit-tools
2976
+ // does this via verifyBundle before invoking).
2977
+ purgeAuditChain: async function (args) {
2978
+ var lastPurgedCounter = Number(args && args.lastPurgedCounter);
2979
+ if (!Number.isFinite(lastPurgedCounter) || lastPurgedCounter < 0) {
2980
+ throw new DbError("db/bad-purge-counter",
2981
+ "purgeAuditChain: lastPurgedCounter must be a non-negative number");
2982
+ }
2983
+ if (cluster.isClusterMode()) {
2984
+ // External-db has no append-only triggers; ordinary DELETE works.
2985
+ var cs = clusterStorage();
2986
+ var d = await cs.execute(
2987
+ "DELETE FROM audit_log WHERE monotonicCounter <= ?", [lastPurgedCounter]
2988
+ );
2989
+ var dc = await cs.execute(
2990
+ "DELETE FROM audit_checkpoints WHERE atMonotonicCounter <= ?", [lastPurgedCounter]
2991
+ );
2992
+ return { rowsDeleted: d.rowCount || 0, checkpointsDeleted: dc.rowCount || 0 };
2993
+ }
2994
+ // Single-node: drop triggers, delete, recreate triggers — all in
2995
+ // one transaction so a crash mid-operation doesn't leave the
2996
+ // table writable to general code.
2997
+ var rowsDeleted = 0;
2998
+ var checkpointsDeleted = 0;
2999
+ transaction(function () {
3000
+ runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_log"');
3001
+ runSql(database, 'DROP TRIGGER IF EXISTS "no_delete_audit_checkpoints"');
3002
+ var d = database.prepare(
3003
+ "DELETE FROM audit_log WHERE monotonicCounter <= ?"
3004
+ ).run(lastPurgedCounter);
3005
+ rowsDeleted = (d && d.changes) || 0;
3006
+ var dc = database.prepare(
3007
+ "DELETE FROM audit_checkpoints WHERE atMonotonicCounter <= ?"
3008
+ ).run(lastPurgedCounter);
3009
+ checkpointsDeleted = (dc && dc.changes) || 0;
3010
+ _installAppendOnlyTriggers(database);
3011
+ });
3012
+ return { rowsDeleted: rowsDeleted, checkpointsDeleted: checkpointsDeleted };
3013
+ },
3014
+ // Diagnostic accessors
3015
+ getMode: function () { return atRest; },
3016
+ getDbPath: function () { return dbPath; },
3017
+ getDataResidency: function () { return dataResidency; },
3018
+ // Reflective metadata: PK columns, FK relationships, sealed/derived fields,
3019
+ // subject mapping. Useful for tooling, RoPA generation, and admin dashboards.
3020
+ // Returns a deep-copied snapshot; mutations don't affect framework state.
3021
+ //
3022
+ // Two-arg form supports format dispatch:
3023
+ // getTableMetadata({ table: "orders", format: "json-schema-2020-12" })
3024
+ // emits a JSON Schema 2020-12 representation of the table — every
3025
+ // column types out per its DDL, sealed fields gain an "x-blamejs-
3026
+ // sealed" annotation, derived-hash columns gain "x-blamejs-derived-
3027
+ // from", and the schema's $schema URI points at JSON Schema 2020-12.
3028
+ getTableMetadata: function (nameOrOpts) {
3029
+ if (!nameOrOpts) return structuredClone(tableMetadata);
3030
+ if (typeof nameOrOpts === "string") {
3031
+ var m = tableMetadata[nameOrOpts];
3032
+ return m ? structuredClone(m) : null;
3033
+ }
3034
+ if (typeof nameOrOpts !== "object") return null;
3035
+ var tableName = nameOrOpts.table;
3036
+ if (typeof tableName !== "string" || tableName.length === 0) {
3037
+ throw new DbError("db/bad-table-arg",
3038
+ "getTableMetadata: opts.table must be a non-empty string");
3039
+ }
3040
+ var meta = tableMetadata[tableName];
3041
+ if (!meta) return null;
3042
+ var format = nameOrOpts.format || "blamejs";
3043
+ if (format === "blamejs") return structuredClone(meta);
3044
+ if (format === "json-schema-2020-12") {
3045
+ return _tableToJsonSchema2020(tableName, meta);
3046
+ }
3047
+ throw new DbError("db/bad-format",
3048
+ "getTableMetadata: format must be 'blamejs' or 'json-schema-2020-12', got " +
3049
+ JSON.stringify(format));
3050
+ },
3051
+ exportCsv: exportCsv,
3052
+ // declareView — declarative CREATE VIEW + GRANT migration spec for an
3053
+ // externalDb backend. Returns a migration-shape object for use with
3054
+ // b.externalDb.migrate. Postgres-only; fail-fast at apply time on other
3055
+ // dialects. See lib/db-declare-view.js.
3056
+ declareView: dbDeclareView.declareView,
3057
+ // declareRowPolicy — declarative Postgres ROW LEVEL SECURITY migration
3058
+ // spec. Pairs with externalDb.transaction({ sessionGucs }) for the
3059
+ // per-request `SET LOCAL` plumbing. Postgres-only; fail-fast on other
3060
+ // dialects. See lib/db-declare-row-policy.js.
3061
+ declareRowPolicy: dbDeclareRowPolicy.declareRowPolicy,
3062
+ // declareWorm — install row-level WORM (write-once-read-many) on
3063
+ // operator-named business-record tables. Per SEC Rule 17a-4(f),
3064
+ // FINRA Rule 4511, 21 CFR Part 11 §11.10(c). Boot-time assertion
3065
+ // refuses to continue under sec-17a-4 / finra-4511 / fda-21cfr11
3066
+ // postures unless at least one table is declared.
3067
+ declareWorm: declareWorm,
3068
+ // declareRequireDualControl — gate destructive ops (erase / purge /
3069
+ // physical delete) on operator-named tables behind an m-of-n
3070
+ // dual-control grant from b.dualControl.consume(). Caller passes
3071
+ // the consumed grant via opts.dualControlGrant on b.db.eraseHard.
3072
+ declareRequireDualControl: declareRequireDualControl,
3073
+ // eraseHard — full crypto-erase + REINDEX for one row, with
3074
+ // legal-hold + dual-control gate consult. Closes the F-RTBF
3075
+ // B-tree residual class on a per-row basis.
3076
+ eraseHard: eraseHard,
3077
+ _assertWormUnderPosture: _assertWormUnderPosture,
3078
+ // Internal accessors used by audit / subject / consent modules.
3079
+ // Not part of the public contract — apps should not depend on them.
3080
+ _getSubjectTables: function () { return subjectTables.slice(); },
3081
+ RESERVED_TABLE_NAMES: RESERVED_TABLE_NAMES,
3082
+ FRAMEWORK_SCHEMA: FRAMEWORK_SCHEMA,
3083
+ // Testing
3084
+ _resetForTest: function () {
3085
+ _resetForTest();
3086
+ subjectTables = [];
3087
+ dataResidency = null;
3088
+ tableMetadata = {};
3089
+ // Cascade reset to stateful modules so a fresh init() works.
3090
+ // Each ref is a lazyRequire (top-of-file) so module-load cycles
3091
+ // don't trip; failures (missing optional dep, partial smoke
3092
+ // suites that skip a module entirely) get logged at debug.
3093
+ _cascadeStep("audit", _resetAudit);
3094
+ _cascadeStep("consent", _resetConsent);
3095
+ _cascadeStep("subject", _resetSubject);
3096
+ _cascadeStep("session", _resetSession);
3097
+ _cascadeStep("storage", _resetStorage);
3098
+ _cascadeStep("audit-sign", _resetAuditSign);
3099
+ _cascadeStep("queue", _resetQueue);
3100
+ _cascadeStep("break-glass", _resetBreakGlass);
3101
+ _cascadeStep("log-stream", _resetLogStream);
3102
+ _cascadeStep("redact", _resetRedact);
3103
+ _cascadeStep("external-db", _resetExternalDb);
3104
+ },
3105
+ // Helper for audit.checkpoint to write the rollback-detection sidecar
3106
+ _writeAuditTip: function (tip) {
3107
+ if (!dataDir) return;
3108
+ var tipPath = nodePath.join(dataDir, "audit.tip");
3109
+ atomicFile.writeSync(tipPath, JSON.stringify(tip, null, 2), { fileMode: 0o600 });
3110
+ },
3111
+ };