@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,2160 @@
1
+ "use strict";
2
+ /**
3
+ * @module shop.storefront
4
+ * @title Storefront — server-rendered HTML for end customers
5
+ *
6
+ * @intro
7
+ * v1 ships a minimum viable storefront: read-only HTML routes
8
+ * for the home page (product list), the product detail page
9
+ * (PDP), and the cart view. Each renderer is a pure function
10
+ * returning an HTML string; `mount(router, deps)` wires the
11
+ * routes into a `b.router` instance and reads data via the
12
+ * provided catalog / cart primitives.
13
+ *
14
+ * Templates are inline string templates with the same strict
15
+ * `{{var}}` renderer the email primitive uses — HTML-escaped
16
+ * substitution, refusal of unknown / unused placeholders at
17
+ * composition time. The full theme primitive (with file-backed
18
+ * templates via `b.template`, asset fingerprinting via
19
+ * `b.objectStore`, theme inheritance + override resolution) lands
20
+ * in v1.x; the inline shape exists so the storefront is
21
+ * demonstrable today.
22
+ *
23
+ * POST routes (add-to-cart, checkout submit) land in the next
24
+ * patch alongside the Stripe Elements wiring — v0.0.8 is
25
+ * read-only HTML.
26
+ */
27
+
28
+ var emailModule = require("./email");
29
+ var pricing = require("./pricing");
30
+
31
+ var bShop;
32
+ function _b() {
33
+ if (!bShop) bShop = require("./index");
34
+ return bShop.framework;
35
+ }
36
+
37
+ // Re-use the strict renderer from the email primitive (same shape,
38
+ // same XSS guard, same unknown / unused refusal).
39
+ var _render = emailModule._render;
40
+
41
+ // ---- shared layout ------------------------------------------------------
42
+
43
+ // Visual identity reference: the framework ships with two
44
+ // reference ecommerce templates (Lager + odor-buyer-file in
45
+ // .template/) — the layout below adopts odor's monochrome-plus-
46
+ // orange-accent palette (#191919 / #fa4f09 / #ffffff) and
47
+ // Montserrat headlines as the default theme. Customers fork the
48
+ // theme later by overriding LAYOUT + the per-page templates; the
49
+ // theme primitive (v1.x) makes that swap a per-directory drop-in.
50
+ //
51
+ // Brand assets live under R2 at `brand/<file>` — the layout
52
+ // references `/assets/brand/logo.png` which the Worker resolves to
53
+ // the bound R2 bucket. The 1536×1024 source PNG is committed
54
+ // only to .template/ (local-only) and uploaded once via
55
+ // `wrangler r2 object put`.
56
+ var LAYOUT =
57
+ "<!DOCTYPE html>\n" +
58
+ "<html lang=\"en\">\n" +
59
+ "<head>\n" +
60
+ " <meta charset=\"utf-8\">\n" +
61
+ " <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n" +
62
+ " <title>{{title}} — {{shop_name}}</title>\n" +
63
+ " <link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/brand/favicon.svg\">\n" +
64
+ " <link rel=\"stylesheet\" href=\"{{theme_css}}\">\n" +
65
+ "</head>\n" +
66
+ "<body>\n" +
67
+ " <a class=\"skip-link\" href=\"#main\">Skip to content</a>\n" +
68
+ "\n" +
69
+ " <div class=\"utility-bar\" role=\"complementary\">\n" +
70
+ " <div class=\"utility-bar__inner\">\n" +
71
+ " <span class=\"utility-bar__pill\"><span class=\"dot dot--live\" aria-hidden=\"true\"></span> Open source · Apache 2.0</span>\n" +
72
+ " <span class=\"utility-bar__msg\">Server-rendered HTML · post-quantum crypto on by default · zero npm runtime deps</span>\n" +
73
+ " <a class=\"utility-bar__link\" href=\"https://github.com/blamejs/blamejs.shop\" rel=\"noopener\">Star on GitHub →</a>\n" +
74
+ " </div>\n" +
75
+ " </div>\n" +
76
+ "\n" +
77
+ " <header class=\"site-header\">\n" +
78
+ " <div class=\"site-header__inner\">\n" +
79
+ " <a href=\"/\" class=\"brand\" aria-label=\"{{shop_name}}\"><img src=\"/assets/brand/logo.png\" alt=\"{{shop_name}}\"></a>\n" +
80
+ " <form class=\"site-search\" action=\"/search\" method=\"get\" role=\"search\">\n" +
81
+ " <div class=\"site-search__inner\">\n" +
82
+ " <label for=\"site-search-q\" class=\"skip-link\">Search products</label>\n" +
83
+ " <svg class=\"site-search__icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\"><path d=\"M21 21l-4.35-4.35M11 19a8 8 0 1 1 0-16 8 8 0 0 1 0 16Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\"/></svg>\n" +
84
+ " <input id=\"site-search-q\" type=\"search\" name=\"q\" value=\"{{search_q}}\" placeholder=\"Search the catalog\" autocomplete=\"off\" spellcheck=\"false\" maxlength=\"200\">\n" +
85
+ " <button type=\"submit\">Search</button>\n" +
86
+ " </div>\n" +
87
+ " </form>\n" +
88
+ " <nav class=\"site-nav\" aria-label=\"Primary\">\n" +
89
+ " <a class=\"site-nav__link\" href=\"/\">Shop</a>\n" +
90
+ " <a class=\"site-nav__link\" href=\"#framework\">Framework</a>\n" +
91
+ " <a class=\"site-nav__icon\" href=\"/account\" aria-label=\"Account\"><svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm-7 9a7 7 0 0 1 14 0\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\"/></svg></a>\n" +
92
+ " <a class=\"cart-pill\" href=\"/cart\" aria-label=\"Cart, {{cart_count}} items\"><svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\"><path d=\"M3 4h2l2.4 12.1a2 2 0 0 0 2 1.6h7.6a2 2 0 0 0 1.95-1.55L21 8H6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><circle cx=\"10\" cy=\"21\" r=\"1.4\" fill=\"currentColor\"/><circle cx=\"17\" cy=\"21\" r=\"1.4\" fill=\"currentColor\"/></svg><span class=\"cart-pill__count\">{{cart_count}}</span></a>\n" +
93
+ " </nav>\n" +
94
+ " </div>\n" +
95
+ " </header>\n" +
96
+ "\n" +
97
+ " <main id=\"main\">{{body}}</main>\n" +
98
+ "\n" +
99
+ " <section class=\"newsletter-band\" aria-labelledby=\"newsletter-title\">\n" +
100
+ " <div class=\"newsletter-band__inner\">\n" +
101
+ " <div class=\"newsletter-band__copy\">\n" +
102
+ " <p class=\"eyebrow eyebrow--on-dark\">Stay in the loop</p>\n" +
103
+ " <h2 id=\"newsletter-title\">Get release notes the day they ship.</h2>\n" +
104
+ " <p class=\"newsletter-band__lede\">No marketing emails. A single short note when there's a new framework release, a security advisory, or a primitive worth knowing about.</p>\n" +
105
+ " </div>\n" +
106
+ " <form class=\"newsletter-band__form\" method=\"post\" action=\"/newsletter\">\n" +
107
+ " <label class=\"skip-link\" for=\"newsletter-email\">Email address</label>\n" +
108
+ " <input id=\"newsletter-email\" type=\"email\" name=\"email\" required placeholder=\"you@example.com\" autocomplete=\"email\">\n" +
109
+ " <button type=\"submit\">Subscribe</button>\n" +
110
+ " </form>\n" +
111
+ " </div>\n" +
112
+ " </section>\n" +
113
+ "\n" +
114
+ " <footer class=\"site-footer\">\n" +
115
+ " <div class=\"site-footer__inner\">\n" +
116
+ " <div class=\"site-footer__brand-col\">\n" +
117
+ " <img class=\"site-footer__logo\" src=\"/assets/brand/logo.png\" alt=\"{{shop_name}}\">\n" +
118
+ " <p class=\"site-footer__tagline\">An open-source shop framework — server-rendered HTML, zero npm runtime dependencies, security defaults on.</p>\n" +
119
+ " <ul class=\"site-footer__social\" aria-label=\"Project links\">\n" +
120
+ " <li><a href=\"https://github.com/blamejs/blamejs.shop\" rel=\"noopener\" aria-label=\"GitHub\"><svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\"><path d=\"M12 .5a11.5 11.5 0 0 0-3.6 22.4c.6.1.8-.3.8-.6v-2c-3.2.7-3.9-1.5-3.9-1.5-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.4-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.3 1.2a11 11 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.3v3.4c0 .3.2.7.8.6A11.5 11.5 0 0 0 12 .5Z\" fill=\"currentColor\"/></svg></a></li>\n" +
121
+ " <li><a href=\"https://npmjs.com/package/blamejs\" rel=\"noopener\" aria-label=\"npm\"><svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\"><path d=\"M2 7v10h6v-7h3v7h11V7H2Zm15 8h-2v-5h-3v5h-1V9h6v6Z\" fill=\"currentColor\"/></svg></a></li>\n" +
122
+ " <li><a href=\"/feed.xml\" aria-label=\"RSS feed\"><svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\"><path d=\"M5 4v3a13 13 0 0 1 13 13h3A16 16 0 0 0 5 4Zm0 6v3a7 7 0 0 1 7 7h3a10 10 0 0 0-10-10Zm1 7a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z\" fill=\"currentColor\"/></svg></a></li>\n" +
123
+ " </ul>\n" +
124
+ " </div>\n" +
125
+ " <div class=\"site-footer__col\">\n" +
126
+ " <h4>Shop</h4>\n" +
127
+ " <ul>\n" +
128
+ " <li><a href=\"/\">All products</a></li>\n" +
129
+ " <li><a href=\"/?sort=new\">New arrivals</a></li>\n" +
130
+ " <li><a href=\"/?sort=sale\">On sale</a></li>\n" +
131
+ " <li><a href=\"/cart\">Cart</a></li>\n" +
132
+ " </ul>\n" +
133
+ " </div>\n" +
134
+ " <div class=\"site-footer__col\">\n" +
135
+ " <h4>Framework</h4>\n" +
136
+ " <ul>\n" +
137
+ " <li><a href=\"https://github.com/blamejs/blamejs.shop\" rel=\"noopener\">Source on GitHub</a></li>\n" +
138
+ " <li><a href=\"https://github.com/blamejs/blamejs\" rel=\"noopener\">blamejs core</a></li>\n" +
139
+ " <li><a href=\"/SECURITY.md\">Security policy</a></li>\n" +
140
+ " <li><a href=\"/CHANGELOG.md\">Changelog</a></li>\n" +
141
+ " </ul>\n" +
142
+ " </div>\n" +
143
+ " <div class=\"site-footer__col\">\n" +
144
+ " <h4>Operators</h4>\n" +
145
+ " <ul>\n" +
146
+ " <li><a href=\"/account\">Account</a></li>\n" +
147
+ " <li><a href=\"/orders\">Orders</a></li>\n" +
148
+ " <li><a href=\"/admin\">Admin</a></li>\n" +
149
+ " <li><a href=\"mailto:hello@blamejs.shop\">Contact</a></li>\n" +
150
+ " </ul>\n" +
151
+ " </div>\n" +
152
+ " </div>\n" +
153
+ " <div class=\"site-footer__copy\">\n" +
154
+ " <p>&copy; {{year}} {{shop_name}} — built on blamejs · Apache 2.0 licensed.</p>\n" +
155
+ " <ul>\n" +
156
+ " <li><a href=\"/SECURITY.md\">Security</a></li>\n" +
157
+ " <li><a href=\"/privacy\">Privacy</a></li>\n" +
158
+ " <li><a href=\"/terms\">Terms</a></li>\n" +
159
+ " </ul>\n" +
160
+ " </div>\n" +
161
+ " </footer>\n" +
162
+ "</body>\n" +
163
+ "</html>\n";
164
+
165
+ // Default theme stylesheet URL. Operators pass `opts.theme_css` (or
166
+ // `opts.theme.assetUrl("css/main.css")` via the theme primitive) on
167
+ // each render call to override; absent that, every storefront page
168
+ // references the shipped default theme — so a fresh install renders
169
+ // styled out of the box without any wiring.
170
+ // The default theme stylesheet ships from R2 at this path. The
171
+ // `?v=` query is a build-time cache-buster — operator uploads to R2
172
+ // rewrite the bytes at the same path, and without the version
173
+ // param browsers happily serve the previous cached CSS for the
174
+ // five-minute default TTL. Bumping this constant on each release
175
+ // forces every active session to re-fetch.
176
+ var DEFAULT_THEME_CSS_URL = "/assets/themes/default/css/main.css?v=" + require("../package.json").version;
177
+
178
+ function _wrap(opts) {
179
+ var themeCss = (opts && typeof opts.theme_css === "string" && opts.theme_css.length)
180
+ ? opts.theme_css
181
+ : DEFAULT_THEME_CSS_URL;
182
+ return _render(LAYOUT, {
183
+ title: opts.title,
184
+ shop_name: opts.shop_name,
185
+ cart_count: opts.cart_count == null ? 0 : opts.cart_count,
186
+ year: String(new Date().getUTCFullYear()),
187
+ search_q: opts.search_q == null ? "" : opts.search_q,
188
+ theme_css: themeCss,
189
+ body: "RAW_BODY_PLACEHOLDER",
190
+ }).replace("RAW_BODY_PLACEHOLDER", opts.body);
191
+ // The body is RAW HTML (already rendered + escaped at the
192
+ // per-fragment level). The placeholder swap is post-render so the
193
+ // outer renderer's HTML-escape doesn't double-escape the inner
194
+ // markup. `search_q` is HTML-escaped by the renderer like any
195
+ // other placeholder, so a customer-supplied query like
196
+ // `"><script>` lands as escaped text inside the input's `value`.
197
+ }
198
+
199
+ // ---- home --------------------------------------------------------------
200
+
201
+ // PRODUCT_CARD has two flavors composed at render time —
202
+ // `_buildProductCard()` picks between them based on whether the
203
+ // product carries a media row. The image-bearing card uses an
204
+ // anchor-wrapper so the whole tile is clickable; the text-only
205
+ // fallback keeps the existing link inside the card body.
206
+ var PRODUCT_CARD_IMAGE =
207
+ "<a class=\"product-card\" href=\"/products/{{slug}}\">\n" +
208
+ " <figure class=\"product-card__media\">\n" +
209
+ " <img src=\"{{image_url}}\" alt=\"{{image_alt}}\" loading=\"lazy\">\n" +
210
+ " </figure>\n" +
211
+ " <div class=\"product-card__meta\">\n" +
212
+ " <h3 class=\"product-card__title\">{{title}}</h3>\n" +
213
+ " <p class=\"product-card__price\">{{price}}</p>\n" +
214
+ " </div>\n" +
215
+ "</a>\n";
216
+
217
+ var PRODUCT_CARD =
218
+ "<div class=\"card\">\n" +
219
+ " <h2>{{title}}</h2>\n" +
220
+ " <p class=\"price\">{{price}}</p>\n" +
221
+ " <a href=\"/products/{{slug}}\" class=\"card-link\">View product →</a>\n" +
222
+ "</div>\n";
223
+
224
+ // Render-time picker. Image-bearing cards become the dominant
225
+ // surface as soon as a product carries media; text-only cards
226
+ // remain the fallback so a freshly-listed product doesn't render
227
+ // an empty image slot.
228
+ function _buildProductCard(p) {
229
+ if (p.image_url) {
230
+ return _render(PRODUCT_CARD_IMAGE, {
231
+ title: p.title,
232
+ price: p.price,
233
+ slug: p.slug,
234
+ image_url: p.image_url,
235
+ image_alt: p.image_alt || p.title,
236
+ });
237
+ }
238
+ return _render(PRODUCT_CARD, {
239
+ title: p.title,
240
+ price: p.price,
241
+ slug: p.slug,
242
+ });
243
+ }
244
+
245
+ var HOME_HERO =
246
+ "<section class=\"hero hero--dark\">\n" +
247
+ " <div class=\"hero__bg\" aria-hidden=\"true\">\n" +
248
+ " <div class=\"hero__grid\"></div>\n" +
249
+ " <div class=\"hero__glow hero__glow--1\"></div>\n" +
250
+ " <div class=\"hero__glow hero__glow--2\"></div>\n" +
251
+ " </div>\n" +
252
+ " <div class=\"hero__inner\">\n" +
253
+ " <div class=\"hero__copy\">\n" +
254
+ " <p class=\"eyebrow eyebrow--on-dark\"><span class=\"dot dot--accent\" aria-hidden=\"true\"></span> Open-source ecommerce framework · v" + require("../package.json").version + "</p>\n" +
255
+ " <h1 class=\"hero__title\">Run a shop that owes <span class=\"accent\">nothing</span> to the dependency graph.</h1>\n" +
256
+ " <p class=\"hero__lede\">Server-rendered HTML. Post-quantum crypto on by default. Zero npm runtime dependencies. Every primitive is composed from a single vendored framework — no transitive supply chain to audit.</p>\n" +
257
+ " <div class=\"hero__cta\">\n" +
258
+ " <a href=\"#catalog\" class=\"btn-primary\">Browse the shop <span aria-hidden=\"true\">→</span></a>\n" +
259
+ " <a href=\"https://github.com/blamejs/blamejs.shop\" class=\"btn-ghost btn-ghost--on-dark\" rel=\"noopener\">View on GitHub</a>\n" +
260
+ " </div>\n" +
261
+ " <dl class=\"hero__stats\">\n" +
262
+ " <div><dt>Products live</dt><dd>{{product_count}}</dd></div>\n" +
263
+ " <div><dt>npm runtime deps</dt><dd>0</dd></div>\n" +
264
+ " <div><dt>Default crypto</dt><dd>PQC</dd></div>\n" +
265
+ " <div><dt>License</dt><dd>Apache 2.0</dd></div>\n" +
266
+ " </dl>\n" +
267
+ " </div>\n" +
268
+ " <aside class=\"hero__card\" aria-label=\"Storefront preview\">\n" +
269
+ " <div class=\"hero__card-bar\">\n" +
270
+ " <span class=\"hero__card-dot hero__card-dot--r\"></span>\n" +
271
+ " <span class=\"hero__card-dot hero__card-dot--y\"></span>\n" +
272
+ " <span class=\"hero__card-dot hero__card-dot--g\"></span>\n" +
273
+ " <span class=\"hero__card-url\">blamejs.shop / order / o-42</span>\n" +
274
+ " </div>\n" +
275
+ " <pre class=\"hero__card-body\"><code><span class=\"tk-c\">// Server-rendered order page</span>\n" +
276
+ "<span class=\"tk-k\">var</span> bShop = <span class=\"tk-f\">require</span>(<span class=\"tk-s\">\"./lib\"</span>);\n" +
277
+ "\n" +
278
+ "bShop.checkout.<span class=\"tk-f\">finalize</span>({\n" +
279
+ " cart_id: <span class=\"tk-s\">\"c_2024\"</span>,\n" +
280
+ " payment_intent: <span class=\"tk-s\">\"pi_3RtA…\"</span>,\n" +
281
+ " cursor_secret: <span class=\"tk-f\">b.crypto.namespaceHash</span>(\n" +
282
+ " <span class=\"tk-s\">\"order-cursor\"</span>, secret),\n" +
283
+ "}).<span class=\"tk-f\">then</span>(<span class=\"tk-k\">function</span> (o) {\n" +
284
+ " res.<span class=\"tk-f\">setHeader</span>(<span class=\"tk-s\">\"content-type\"</span>,\n" +
285
+ " <span class=\"tk-s\">\"text/html; charset=utf-8\"</span>);\n" +
286
+ " res.<span class=\"tk-f\">end</span>(storefront.<span class=\"tk-f\">renderOrder</span>(o));\n" +
287
+ "});</code></pre>\n" +
288
+ " </aside>\n" +
289
+ " </div>\n" +
290
+ "</section>\n" +
291
+ "\n" +
292
+ "<section class=\"marquee\" aria-hidden=\"true\">\n" +
293
+ " <div class=\"marquee__track\">\n" +
294
+ " <span>ML-KEM-1024 key agreement</span><span class=\"marquee__sep\">◆</span>\n" +
295
+ " <span>ML-DSA-65 signatures</span><span class=\"marquee__sep\">◆</span>\n" +
296
+ " <span>XChaCha20-Poly1305 sealed sessions</span><span class=\"marquee__sep\">◆</span>\n" +
297
+ " <span>Argon2id passphrase hashing</span><span class=\"marquee__sep\">◆</span>\n" +
298
+ " <span>SHAKE256 KDF</span><span class=\"marquee__sep\">◆</span>\n" +
299
+ " <span>Stripe-first payments</span><span class=\"marquee__sep\">◆</span>\n" +
300
+ " <span>WebAuthn passkeys</span><span class=\"marquee__sep\">◆</span>\n" +
301
+ " <span>SLSA L3 provenance</span><span class=\"marquee__sep\">◆</span>\n" +
302
+ " <span>Sigstore-keyless SBOM</span><span class=\"marquee__sep\">◆</span>\n" +
303
+ " <span>Trusted Types enforced</span><span class=\"marquee__sep\">◆</span>\n" +
304
+ " <span>Server-rendered HTML</span><span class=\"marquee__sep\">◆</span>\n" +
305
+ " <span>Zero npm runtime deps</span><span class=\"marquee__sep\">◆</span>\n" +
306
+ " <span aria-hidden=\"true\">ML-KEM-1024 key agreement</span><span class=\"marquee__sep\">◆</span>\n" +
307
+ " <span aria-hidden=\"true\">ML-DSA-65 signatures</span><span class=\"marquee__sep\">◆</span>\n" +
308
+ " <span aria-hidden=\"true\">XChaCha20-Poly1305 sealed sessions</span><span class=\"marquee__sep\">◆</span>\n" +
309
+ " <span aria-hidden=\"true\">Argon2id passphrase hashing</span><span class=\"marquee__sep\">◆</span>\n" +
310
+ " <span aria-hidden=\"true\">SHAKE256 KDF</span><span class=\"marquee__sep\">◆</span>\n" +
311
+ " <span aria-hidden=\"true\">Stripe-first payments</span><span class=\"marquee__sep\">◆</span>\n" +
312
+ " <span aria-hidden=\"true\">WebAuthn passkeys</span><span class=\"marquee__sep\">◆</span>\n" +
313
+ " <span aria-hidden=\"true\">SLSA L3 provenance</span><span class=\"marquee__sep\">◆</span>\n" +
314
+ " <span aria-hidden=\"true\">Sigstore-keyless SBOM</span><span class=\"marquee__sep\">◆</span>\n" +
315
+ " <span aria-hidden=\"true\">Trusted Types enforced</span><span class=\"marquee__sep\">◆</span>\n" +
316
+ " <span aria-hidden=\"true\">Server-rendered HTML</span><span class=\"marquee__sep\">◆</span>\n" +
317
+ " <span aria-hidden=\"true\">Zero npm runtime deps</span><span class=\"marquee__sep\">◆</span>\n" +
318
+ " </div>\n" +
319
+ "</section>\n" +
320
+ "\n" +
321
+ "<section class=\"collections\" aria-labelledby=\"collections-title\">\n" +
322
+ " <header class=\"section-head\">\n" +
323
+ " <p class=\"eyebrow\">Featured collections</p>\n" +
324
+ " <h2 id=\"collections-title\" class=\"section-head__title\">Shaped for a storefront that ships from day one.</h2>\n" +
325
+ " <p class=\"section-head__lede\">Drop products into any of these starting categories — or define your own taxonomy through the catalog admin and the framework will server-render the grids, filters, and PDP routes for free.</p>\n" +
326
+ " </header>\n" +
327
+ " <div class=\"collections__grid\">\n" +
328
+ " <a class=\"collection-card\" href=\"/search?q=tee\">\n" +
329
+ " <div class=\"collection-card__art collection-card__art--1\" aria-hidden=\"true\"></div>\n" +
330
+ " <div class=\"collection-card__meta\">\n" +
331
+ " <h3>Apparel</h3>\n" +
332
+ " <p>Sized, colored, inventoried.</p>\n" +
333
+ " </div>\n" +
334
+ " </a>\n" +
335
+ " <a class=\"collection-card\" href=\"/search?q=edge\">\n" +
336
+ " <div class=\"collection-card__art collection-card__art--2\" aria-hidden=\"true\"></div>\n" +
337
+ " <div class=\"collection-card__meta\">\n" +
338
+ " <h3>Hardware</h3>\n" +
339
+ " <p>Serialized, warranty-tracked.</p>\n" +
340
+ " </div>\n" +
341
+ " </a>\n" +
342
+ " <a class=\"collection-card\" href=\"/search?q=license\">\n" +
343
+ " <div class=\"collection-card__art collection-card__art--3\" aria-hidden=\"true\"></div>\n" +
344
+ " <div class=\"collection-card__meta\">\n" +
345
+ " <h3>Digital</h3>\n" +
346
+ " <p>License-key fulfillment.</p>\n" +
347
+ " </div>\n" +
348
+ " </a>\n" +
349
+ " <a class=\"collection-card\" href=\"/search?q=subscription\">\n" +
350
+ " <div class=\"collection-card__art collection-card__art--4\" aria-hidden=\"true\"></div>\n" +
351
+ " <div class=\"collection-card__meta\">\n" +
352
+ " <h3>Subscriptions</h3>\n" +
353
+ " <p>Stripe-backed recurring.</p>\n" +
354
+ " </div>\n" +
355
+ " </a>\n" +
356
+ " <a class=\"collection-card\" href=\"/search?q=bundle\">\n" +
357
+ " <div class=\"collection-card__art collection-card__art--5\" aria-hidden=\"true\"></div>\n" +
358
+ " <div class=\"collection-card__meta\">\n" +
359
+ " <h3>Bundles</h3>\n" +
360
+ " <p>Composite SKUs, atomic stock.</p>\n" +
361
+ " </div>\n" +
362
+ " </a>\n" +
363
+ " <a class=\"collection-card\" href=\"/search?q=gift\">\n" +
364
+ " <div class=\"collection-card__art collection-card__art--6\" aria-hidden=\"true\"></div>\n" +
365
+ " <div class=\"collection-card__meta\">\n" +
366
+ " <h3>Gift cards</h3>\n" +
367
+ " <p>PQC-signed redemption codes.</p>\n" +
368
+ " </div>\n" +
369
+ " </a>\n" +
370
+ " </div>\n" +
371
+ "</section>\n" +
372
+ "\n" +
373
+ "<section id=\"framework\" class=\"framework-band\" aria-labelledby=\"framework-title\">\n" +
374
+ " <div class=\"framework-band__inner\">\n" +
375
+ " <div class=\"framework-band__copy\">\n" +
376
+ " <p class=\"eyebrow\">Built on blamejs</p>\n" +
377
+ " <h2 id=\"framework-title\">Every behavior on this site is a composed primitive.</h2>\n" +
378
+ " <p class=\"framework-band__lede\">Twenty-plus shop primitives — cart, catalog, payment, order, subscriptions, customers, webhooks, audit log, sealed sessions, signed cursors, problem-details, money math, FSMs — sit on a vendored blamejs core that ships hundreds more. They compose. Replacing one doesn't fork the others.</p>\n" +
379
+ " <a class=\"link-arrow\" href=\"https://github.com/blamejs/blamejs.shop\" rel=\"noopener\">Read the source on GitHub <span aria-hidden=\"true\">→</span></a>\n" +
380
+ " </div>\n" +
381
+ " <ul class=\"framework-band__list\">\n" +
382
+ " <li>\n" +
383
+ " <span class=\"framework-band__num\">01</span>\n" +
384
+ " <h3>Server-rendered HTML</h3>\n" +
385
+ " <p>Every route renders a complete document at the origin. No client framework, no hydration spinner, no JavaScript required to read the page.</p>\n" +
386
+ " </li>\n" +
387
+ " <li>\n" +
388
+ " <span class=\"framework-band__num\">02</span>\n" +
389
+ " <h3>PQC-first crypto</h3>\n" +
390
+ " <p>ML-KEM-1024, ML-DSA-65, XChaCha20-Poly1305, SHAKE256, HKDF-SHA3-512, Argon2id. The application never reaches for AES-GCM, SHA-256, or classical ECDH on its own.</p>\n" +
391
+ " </li>\n" +
392
+ " <li>\n" +
393
+ " <span class=\"framework-band__num\">03</span>\n" +
394
+ " <h3>Zero runtime npm deps</h3>\n" +
395
+ " <p>One vendored framework, shipped byte-for-byte from a signed release tag. The tarball you install is the tarball that ran in CI.</p>\n" +
396
+ " </li>\n" +
397
+ " <li>\n" +
398
+ " <span class=\"framework-band__num\">04</span>\n" +
399
+ " <h3>Security defaults on</h3>\n" +
400
+ " <p>CSRF, fetch-metadata, origin, bot-guard, sealed cookies, Trusted Types, DoH, cookie-prefix policy — composed into the request lifecycle, not behind a feature flag.</p>\n" +
401
+ " </li>\n" +
402
+ " </ul>\n" +
403
+ " </div>\n" +
404
+ "</section>\n";
405
+
406
+ var CATALOG_EMPTY =
407
+ "<section id=\"catalog\" class=\"catalog-section\">\n" +
408
+ " <header class=\"section-head section-head--with-link\">\n" +
409
+ " <div>\n" +
410
+ " <p class=\"eyebrow\">Catalog</p>\n" +
411
+ " <h2 class=\"section-head__title\">The shop is open, waiting on its first listing.</h2>\n" +
412
+ " <p class=\"section-head__lede\">When you add a product through the admin API, it appears here in a server-rendered grid with filters, sorting, and a fully-routed PDP. Until then, this is the storefront shell.</p>\n" +
413
+ " </div>\n" +
414
+ " <a class=\"link-arrow\" href=\"/admin\">Open admin <span aria-hidden=\"true\">→</span></a>\n" +
415
+ " </header>\n" +
416
+ " <div class=\"catalog-empty\">\n" +
417
+ " <div class=\"catalog-empty__placeholder\" aria-hidden=\"true\">\n" +
418
+ " <span></span><span></span><span></span><span></span>\n" +
419
+ " </div>\n" +
420
+ " <div class=\"catalog-empty__copy\">\n" +
421
+ " <h3>Add the first product</h3>\n" +
422
+ " <p>The grid below renders as soon as a product is listed. Variants, prices, inventory levels, and tax categories are all wired up — you just supply the SKUs.</p>\n" +
423
+ " <pre class=\"catalog-empty__code\"><code>curl -X POST https://blamejs.shop/admin/products \\\n -H \"authorization: Bearer $ADMIN_API_KEY\" \\\n -d '{ \"title\": \"My first product\", \"slug\": \"first\" }'</code></pre>\n" +
424
+ " </div>\n" +
425
+ " </div>\n" +
426
+ "</section>\n";
427
+
428
+ var CATALOG_HEAD =
429
+ "<section id=\"catalog\" class=\"catalog-section\">\n" +
430
+ " <header class=\"section-head section-head--with-link\">\n" +
431
+ " <div>\n" +
432
+ " <p class=\"eyebrow\">Catalog</p>\n" +
433
+ " <h2 class=\"section-head__title\">Products in store</h2>\n" +
434
+ " <p class=\"section-head__lede\">Server-rendered listings — every card, price, and link arrived on the wire as complete HTML.</p>\n" +
435
+ " </div>\n" +
436
+ " <a class=\"link-arrow\" href=\"/?sort=new\">New arrivals <span aria-hidden=\"true\">→</span></a>\n" +
437
+ " </header>\n" +
438
+ " <div class=\"grid\">{{cards}}</div>\n" +
439
+ "</section>\n";
440
+
441
+ function renderHome(opts) {
442
+ if (!opts || !Array.isArray(opts.products)) throw new TypeError("storefront.renderHome: opts.products required");
443
+ var shopName = opts.shop_name || "blamejs.shop";
444
+ var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
445
+ var title = opts.title || "Shop";
446
+ var assetPrefix = opts.asset_prefix || "/assets/";
447
+ var products = opts.products.map(function (p) {
448
+ var priceStr = p.starting_price_minor != null
449
+ ? pricing.format(p.starting_price_minor, p.starting_price_currency || "USD")
450
+ : "—";
451
+ // Hero image — first media row attached to the product (the
452
+ // route handler bundles it in via `p.hero_media`). Image-less
453
+ // products render the text-only PRODUCT_CARD fallback below.
454
+ var imageUrl = p.hero_media ? assetPrefix + p.hero_media.r2_key : null;
455
+ var imageAlt = p.hero_media ? (p.hero_media.alt_text || p.title) : null;
456
+ return { title: p.title, price: priceStr, slug: p.slug, image_url: imageUrl, image_alt: imageAlt };
457
+ });
458
+ if (opts.theme) {
459
+ return opts.theme.render("home", {
460
+ title: title,
461
+ shop_name: shopName,
462
+ cart_count: cartCount,
463
+ products: products,
464
+ has_products: products.length > 0,
465
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
466
+ });
467
+ }
468
+ var cards = products.map(function (p) { return _buildProductCard(p); }).join("\n");
469
+ var catalog = products.length === 0
470
+ ? CATALOG_EMPTY
471
+ : _render(CATALOG_HEAD, { cards: "RAW_CARDS_PLACEHOLDER" }).replace("RAW_CARDS_PLACEHOLDER", cards);
472
+ // Live stat in the hero — operator-facing visitors see the
473
+ // actual catalog size, not a stale hardcoded number. Falls back
474
+ // to a typographic em-dash when the catalog hasn't been seeded.
475
+ var heroProductCount = products.length === 0 ? "—" : String(products.length);
476
+ var hero = _render(HOME_HERO, { product_count: heroProductCount });
477
+ // The hero + value band + catalog section give the home page a
478
+ // designed surface even when no products are loaded yet —
479
+ // visitors land on the storefront shell, not a tech demo.
480
+ var body = hero + catalog;
481
+ return _wrap({
482
+ title: title,
483
+ shop_name: shopName,
484
+ cart_count: cartCount,
485
+ theme_css: opts.theme_css,
486
+ body: body,
487
+ });
488
+ }
489
+
490
+ // ---- search results -----------------------------------------------------
491
+
492
+ var SEARCH_HEADER =
493
+ "<section class=\"search-page\">\n" +
494
+ " <header class=\"section-head section-head--with-link\">\n" +
495
+ " <div>\n" +
496
+ " <p class=\"eyebrow\">Search results</p>\n" +
497
+ " <h1 class=\"section-head__title\">{{title}}</h1>\n" +
498
+ " <p class=\"section-head__lede\">{{summary}}</p>\n" +
499
+ " </div>\n" +
500
+ " <a class=\"link-arrow\" href=\"/\">All products <span aria-hidden=\"true\">→</span></a>\n" +
501
+ " </header>\n" +
502
+ "</section>\n";
503
+
504
+ var SEARCH_EMPTY =
505
+ "<section class=\"search-empty\">\n" +
506
+ " <div class=\"search-empty__inner\">\n" +
507
+ " <p class=\"search-empty__icon\" aria-hidden=\"true\">⌕</p>\n" +
508
+ " <h2>{{heading}}</h2>\n" +
509
+ " <p>{{copy}}</p>\n" +
510
+ " <a href=\"/\" class=\"btn-ghost\">Browse the full catalog</a>\n" +
511
+ " </div>\n" +
512
+ "</section>\n";
513
+
514
+ function renderSearch(opts) {
515
+ if (!opts || typeof opts.q !== "string") throw new TypeError("storefront.renderSearch: opts.q (string) required");
516
+ var products = Array.isArray(opts.products) ? opts.products : [];
517
+ var qTrim = opts.q.trim();
518
+ var title, summary, emptyHeading, emptyCopy;
519
+ if (qTrim.length === 0) {
520
+ title = "Search the catalog";
521
+ summary = "Use the search box in the header to look for a product by title, SKU, or description.";
522
+ emptyHeading = "What are you looking for?";
523
+ emptyCopy = "Type a query in the header search to find products by title, SKU, or description.";
524
+ } else if (products.length === 0) {
525
+ title = "No matches";
526
+ summary = "Nothing in the catalog matched “" + qTrim + "”.";
527
+ emptyHeading = "We don't carry that yet";
528
+ emptyCopy = "Try a broader term, or browse every product on the home page.";
529
+ } else {
530
+ title = "“" + qTrim + "”";
531
+ summary = "Showing " + products.length + " match" + (products.length === 1 ? "" : "es") + " for your query.";
532
+ }
533
+ var header = _render(SEARCH_HEADER, { title: title, summary: summary });
534
+ var body;
535
+ if (products.length === 0) {
536
+ body = header + _render(SEARCH_EMPTY, { heading: emptyHeading, copy: emptyCopy });
537
+ } else {
538
+ var assetPrefix = opts.asset_prefix || "/assets/";
539
+ var cards = products.map(function (p) {
540
+ var priceStr = p.starting_price_minor != null
541
+ ? pricing.format(p.starting_price_minor, p.starting_price_currency || "USD")
542
+ : "—";
543
+ var imageUrl = p.hero_media ? assetPrefix + p.hero_media.r2_key : null;
544
+ var imageAlt = p.hero_media ? (p.hero_media.alt_text || p.title) : null;
545
+ return _buildProductCard({ title: p.title, price: priceStr, slug: p.slug, image_url: imageUrl, image_alt: imageAlt });
546
+ }).join("\n");
547
+ body = header + "<section class=\"search-grid\"><div class=\"grid\">" + cards + "</div></section>";
548
+ }
549
+ return _wrap({
550
+ title: "Search",
551
+ shop_name: opts.shop_name || "blamejs.shop",
552
+ cart_count: opts.cart_count,
553
+ search_q: opts.q,
554
+ theme_css: opts.theme_css,
555
+ body: body,
556
+ });
557
+ }
558
+
559
+ // ---- product detail -----------------------------------------------------
560
+
561
+ // Cart-add form. CSRF defense rests on the `shop_sid` session
562
+ // cookie's SameSite=Lax attribute — a cross-site form POST won't
563
+ // carry the cookie, so any cross-site "add to cart" lands in a
564
+ // fresh anonymous session that the victim never sees. Token-based
565
+ // CSRF as defense-in-depth is added alongside the Stripe Elements
566
+ // payment route in the next patch.
567
+ var VARIANT_ROW =
568
+ "<tr>\n" +
569
+ " <td class=\"variant-row__title\">{{title}}</td>\n" +
570
+ " <td class=\"variant-row__sku\"><code>{{sku}}</code></td>\n" +
571
+ " <td class=\"variant-row__price price\">{{price}}</td>\n" +
572
+ " <td class=\"variant-row__action\">\n" +
573
+ " <form method=\"post\" action=\"/cart/lines\">\n" +
574
+ " <input type=\"hidden\" name=\"variant_id\" value=\"{{variant_id}}\">\n" +
575
+ " <input type=\"number\" name=\"qty\" value=\"1\" min=\"1\" max=\"99\" class=\"variant-row__qty\" aria-label=\"Quantity\">\n" +
576
+ " <button type=\"submit\" class=\"btn-primary btn-primary--sm\">Add to cart</button>\n" +
577
+ " </form>\n" +
578
+ " </td>\n" +
579
+ "</tr>\n";
580
+
581
+ var PRODUCT_PAGE =
582
+ "<section class=\"pdp\">\n" +
583
+ " <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n" +
584
+ " <ol>\n" +
585
+ " <li><a href=\"/\">Shop</a></li>\n" +
586
+ " <li aria-current=\"page\">{{title}}</li>\n" +
587
+ " </ol>\n" +
588
+ " </nav>\n" +
589
+ " <div class=\"pdp__grid\">\n" +
590
+ " <div class=\"pdp__gallery\">RAW_GALLERY_PLACEHOLDER</div>\n" +
591
+ " <div class=\"pdp__info\">\n" +
592
+ " <p class=\"eyebrow\">Catalog product</p>\n" +
593
+ " <h1 class=\"pdp__title\">{{title}}</h1>\n" +
594
+ " <p class=\"pdp__description\">{{description}}</p>\n" +
595
+ " <div class=\"pdp__meta\">\n" +
596
+ " <span class=\"pdp__badge pdp__badge--ok\"><span class=\"dot dot--live\" aria-hidden=\"true\"></span> In stock</span>\n" +
597
+ " <span class=\"pdp__badge\">Ships from origin</span>\n" +
598
+ " <span class=\"pdp__badge\">Stripe-secured checkout</span>\n" +
599
+ " </div>\n" +
600
+ " <div class=\"pdp__variants\">\n" +
601
+ " <h2 class=\"pdp__variants-title\">Choose a variant</h2>\n" +
602
+ " <div class=\"table-scroll\">\n" +
603
+ " <table class=\"variant-table\">\n" +
604
+ " <thead><tr><th>Variant</th><th>SKU</th><th>Price</th><th class=\"variant-table__action-h\">Action</th></tr></thead>\n" +
605
+ " <tbody>{{variant_rows}}</tbody>\n" +
606
+ " </table>\n" +
607
+ " </div>\n" +
608
+ " </div>\n" +
609
+ " </div>\n" +
610
+ " </div>\n" +
611
+ "</section>\n";
612
+
613
+ // PDP gallery markup — composed once per render call from the
614
+ // product's media rows. When media is present, the first row drives
615
+ // the main figure (with `alt_text` for a11y) and up to three more
616
+ // rows feed the thumbnail strip below it. When media is absent the
617
+ // gallery falls back to the existing letter-mark placeholder so a
618
+ // freshly-seeded product never renders an empty square.
619
+ function _buildPdpGallery(product, media, assetPrefix) {
620
+ var prefix = assetPrefix || "/assets/";
621
+ function _escAttr(s) {
622
+ return String(s == null ? "" : s)
623
+ .replace(/&/g, "&amp;")
624
+ .replace(/</g, "&lt;")
625
+ .replace(/>/g, "&gt;")
626
+ .replace(/"/g, "&quot;");
627
+ }
628
+ if (!media || media.length === 0) {
629
+ var initial = (product.title || "?").trim().charAt(0).toUpperCase() || "?";
630
+ return "<figure class=\"pdp__media\" aria-hidden=\"true\">" +
631
+ "<span class=\"pdp__media-mark\">" + _escAttr(initial) + "</span>" +
632
+ "</figure>" +
633
+ "<ul class=\"pdp__thumbs\" aria-hidden=\"true\">" +
634
+ "<li class=\"is-active\"></li><li></li><li></li><li></li>" +
635
+ "</ul>";
636
+ }
637
+ var hero = media[0];
638
+ var heroUrl = prefix + hero.r2_key;
639
+ var heroAlt = hero.alt_text || product.title || "Product image";
640
+ var heroImg = "<figure class=\"pdp__media pdp__media--image\">" +
641
+ "<img src=\"" + _escAttr(heroUrl) + "\" alt=\"" + _escAttr(heroAlt) + "\" loading=\"eager\">" +
642
+ "</figure>";
643
+ // Thumbnail strip — up to four slots, the active one is the hero.
644
+ // Real thumbnails come from additional media rows; missing slots
645
+ // render as dashed placeholders so the strip's grid doesn't
646
+ // collapse on a single-image product.
647
+ var thumbs = ["<li class=\"is-active\">" +
648
+ "<img src=\"" + _escAttr(heroUrl) + "\" alt=\"\">" +
649
+ "</li>"];
650
+ for (var i = 1; i < Math.min(media.length, 4); i += 1) {
651
+ var t = media[i];
652
+ var tUrl = prefix + t.r2_key;
653
+ thumbs.push("<li><img src=\"" + _escAttr(tUrl) + "\" alt=\"\"></li>");
654
+ }
655
+ while (thumbs.length < 4) thumbs.push("<li></li>");
656
+ return heroImg + "<ul class=\"pdp__thumbs\" aria-hidden=\"true\">" + thumbs.join("") + "</ul>";
657
+ }
658
+
659
+ function renderProduct(opts) {
660
+ if (!opts || !opts.product) throw new TypeError("storefront.renderProduct: opts.product required");
661
+ var variants = opts.variants || [];
662
+ var prices = opts.prices || {}; // { variant_id: { currency, amount_minor } }
663
+ var shopName = opts.shop_name || "blamejs.shop";
664
+ var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
665
+ var description = opts.product.description || "";
666
+ var rendered = variants.map(function (v) {
667
+ var price = prices[v.id];
668
+ var priceStr = price ? pricing.format(price.amount_minor, price.currency) : "—";
669
+ var vTitle = v.title || (Object.keys(v.options || {}).map(function (k) { return v.options[k]; }).join(" / ") || "Default");
670
+ return { id: v.id, sku: v.sku, title: vTitle, price: priceStr };
671
+ });
672
+ if (opts.theme) {
673
+ return opts.theme.render("product", {
674
+ title: opts.product.title,
675
+ shop_name: shopName,
676
+ cart_count: cartCount,
677
+ product: { title: opts.product.title, description: description },
678
+ variants: rendered,
679
+ has_variants: rendered.length > 0,
680
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
681
+ });
682
+ }
683
+ var rows = rendered.map(function (v) {
684
+ return _render(VARIANT_ROW, { title: v.title, sku: v.sku, price: v.price, variant_id: v.id });
685
+ }).join("");
686
+ if (!rows) rows = "<tr><td colspan=\"4\" class=\"empty\">No variants available.</td></tr>";
687
+ var galleryHtml = _buildPdpGallery(opts.product, opts.media || [], opts.asset_prefix || "/assets/");
688
+ var body = _render(PRODUCT_PAGE, {
689
+ title: opts.product.title,
690
+ description: description,
691
+ variant_rows: "RAW_ROWS_PLACEHOLDER",
692
+ })
693
+ .replace("RAW_GALLERY_PLACEHOLDER", galleryHtml)
694
+ .replace("RAW_ROWS_PLACEHOLDER", rows);
695
+ return _wrap({
696
+ title: opts.product.title,
697
+ shop_name: shopName,
698
+ cart_count: cartCount,
699
+ theme_css: opts.theme_css,
700
+ body: body,
701
+ });
702
+ }
703
+
704
+ // ---- cart --------------------------------------------------------------
705
+
706
+ var CART_LINE =
707
+ "<tr><td>{{sku}}</td><td>{{qty}}</td><td class=\"price\">{{unit}}</td><td class=\"price\">{{total}}</td></tr>\n";
708
+
709
+ // Editable cart line — shown on the /cart page. Includes an inline
710
+ // qty form (POST /cart/lines/:id/update) and a remove form (POST
711
+ // /cart/lines/:id/remove). HTML forms don't natively support
712
+ // PATCH/DELETE so the framework routes use POST with verb-suffix
713
+ // paths.
714
+ var CART_LINE_EDITABLE =
715
+ "<tr>\n" +
716
+ " <td class=\"cart-line__sku\"><code>{{sku}}</code></td>\n" +
717
+ " <td class=\"cart-line__qty\">\n" +
718
+ " <form method=\"post\" action=\"/cart/lines/{{line_id}}/update\" class=\"cart-line__update\">\n" +
719
+ " <input type=\"number\" name=\"qty\" value=\"{{qty}}\" min=\"1\" max=\"99\" class=\"cart-line__qty-input\" aria-label=\"Quantity\">\n" +
720
+ " <button type=\"submit\" class=\"cart-line__btn\">Update</button>\n" +
721
+ " </form>\n" +
722
+ " </td>\n" +
723
+ " <td class=\"price\">{{unit}}</td>\n" +
724
+ " <td class=\"price\">{{total}}</td>\n" +
725
+ " <td class=\"cart-line__remove-cell\">\n" +
726
+ " <form method=\"post\" action=\"/cart/lines/{{line_id}}/remove\">\n" +
727
+ " <button type=\"submit\" class=\"cart-line__btn cart-line__btn--remove\" aria-label=\"Remove line\">Remove</button>\n" +
728
+ " </form>\n" +
729
+ " </td>\n" +
730
+ "</tr>\n";
731
+
732
+ // ---- checkout form + payment page + order confirmation -----------------
733
+
734
+ var CHECKOUT_PAGE =
735
+ "<section class=\"checkout-page\">\n" +
736
+ " <header class=\"section-head\">\n" +
737
+ " <p class=\"eyebrow\">Checkout</p>\n" +
738
+ " <h1 class=\"section-head__title\">Shipping details</h1>\n" +
739
+ " <p class=\"section-head__lede\">Enter where the order should ship. Payment runs through Stripe on the next step.</p>\n" +
740
+ " </header>\n" +
741
+ " <form method=\"post\" action=\"/checkout\" class=\"form-stack\">\n" +
742
+ " <div class=\"form-row\"><label class=\"form-field\"><span class=\"form-field__label\">Email</span><input type=\"email\" name=\"email\" required autocomplete=\"email\"></label></div>\n" +
743
+ " <div class=\"form-row\"><label class=\"form-field\"><span class=\"form-field__label\">Full name</span><input type=\"text\" name=\"name\" required autocomplete=\"name\"></label></div>\n" +
744
+ " <div class=\"form-row form-row--inline\">\n" +
745
+ " <label class=\"form-field\"><span class=\"form-field__label\">Country (ISO 3166-1)</span><input type=\"text\" name=\"country\" value=\"US\" maxlength=\"2\" pattern=\"[A-Z]{2}\" required autocomplete=\"country\" class=\"form-field__input--xs\"></label>\n" +
746
+ " <label class=\"form-field\"><span class=\"form-field__label\">State / Region</span><input type=\"text\" name=\"state\" maxlength=\"5\" autocomplete=\"address-level1\" class=\"form-field__input--xs\"></label>\n" +
747
+ " <label class=\"form-field\"><span class=\"form-field__label\">Postal code</span><input type=\"text\" name=\"postal\" maxlength=\"16\" autocomplete=\"postal-code\" class=\"form-field__input--sm\"></label>\n" +
748
+ " </div>\n" +
749
+ " <div class=\"checkout-summary\">\n" +
750
+ " <h3>Order summary</h3>\n" +
751
+ " <dl>\n" +
752
+ " <div><dt>Subtotal</dt><dd>{{subtotal}}</dd></div>\n" +
753
+ " <div class=\"checkout-summary__total\"><dt>Total <span class=\"small\">(plus tax + shipping)</span></dt><dd>{{subtotal}}</dd></div>\n" +
754
+ " </dl>\n" +
755
+ " </div>\n" +
756
+ " <div class=\"form-actions\"><button type=\"submit\" class=\"btn-primary\">Continue to payment <span aria-hidden=\"true\">→</span></button></div>\n" +
757
+ " </form>\n" +
758
+ "</section>\n";
759
+
760
+ function renderCheckoutForm(opts) {
761
+ if (!opts) throw new TypeError("storefront.renderCheckoutForm: opts required");
762
+ var lines = opts.lines || [];
763
+ var totals = opts.totals || { subtotal_minor: 0, currency: "USD" };
764
+ var shopName = opts.shop_name || "blamejs.shop";
765
+ var subtotal = pricing.format(totals.subtotal_minor, totals.currency);
766
+ if (opts.theme) {
767
+ return opts.theme.render("checkout", {
768
+ title: "Checkout",
769
+ shop_name: shopName,
770
+ cart_count: lines.length,
771
+ subtotal: subtotal,
772
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
773
+ });
774
+ }
775
+ var body = _render(CHECKOUT_PAGE, { subtotal: subtotal });
776
+ return _wrap({
777
+ title: "Checkout",
778
+ shop_name: shopName,
779
+ cart_count: lines.length,
780
+ theme_css: opts.theme_css,
781
+ body: body,
782
+ });
783
+ }
784
+
785
+ // Stripe Elements payment page — embeds Stripe.js + a minimal
786
+ // mount block. The publishable key is operator-supplied (env
787
+ // `STRIPE_PUBLISHABLE_KEY` → forwarded into the rendered HTML).
788
+ // The client_secret is per-order; never logged, never persisted.
789
+ var PAY_PAGE =
790
+ "<section class=\"pay-page\">\n" +
791
+ " <header class=\"section-head\">\n" +
792
+ " <p class=\"eyebrow\">Secure checkout · Stripe</p>\n" +
793
+ " <h1 class=\"section-head__title\">Pay {{grand_total}}</h1>\n" +
794
+ " <p class=\"section-head__lede\">Order <code class=\"inline-code\">{{order_id}}</code> · the Stripe Payment Element is mounted below in a same-origin form.</p>\n" +
795
+ " </header>\n" +
796
+ " <div class=\"pay-card\">\n" +
797
+ " <div id=\"payment-element\" class=\"pay-card__element\"></div>\n" +
798
+ " <button id=\"submit\" type=\"button\" class=\"btn-primary pay-card__submit\">Pay {{grand_total}}</button>\n" +
799
+ " <p id=\"payment-message\" class=\"pay-card__message\"></p>\n" +
800
+ " </div>\n" +
801
+ " <script src=\"https://js.stripe.com/v3/\"></script>\n" +
802
+ " <script>\n" +
803
+ " (function () {\n" +
804
+ " var stripe = Stripe({{pk_json}});\n" +
805
+ " var elements = stripe.elements({ clientSecret: {{client_secret_json}}, appearance: { theme: \"stripe\" } });\n" +
806
+ " var paymentElement = elements.create(\"payment\");\n" +
807
+ " paymentElement.mount(\"#payment-element\");\n" +
808
+ " document.getElementById(\"submit\").addEventListener(\"click\", function () {\n" +
809
+ " document.getElementById(\"payment-message\").textContent = \"Processing...\";\n" +
810
+ " stripe.confirmPayment({ elements: elements, confirmParams: { return_url: window.location.origin + \"/orders/{{order_id}}\" } }).then(function (result) {\n" +
811
+ " if (result.error) { document.getElementById(\"payment-message\").textContent = result.error.message || \"Payment failed.\"; }\n" +
812
+ " });\n" +
813
+ " });\n" +
814
+ " })();\n" +
815
+ " </script>\n" +
816
+ "</section>\n";
817
+
818
+ function renderPayPage(opts) {
819
+ if (!opts || !opts.order) throw new TypeError("storefront.renderPayPage: opts.order required");
820
+ if (!opts.client_secret) throw new TypeError("storefront.renderPayPage: opts.client_secret required");
821
+ if (!opts.publishable_key) throw new TypeError("storefront.renderPayPage: opts.publishable_key required");
822
+ var shopName = opts.shop_name || "blamejs.shop";
823
+ var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
824
+ var grandTotal = pricing.format(opts.order.grand_total_minor, opts.order.currency);
825
+ // Stripe.js and client_secret values must be JSON-encoded so the
826
+ // template engine treats them as raw expressions (`{{{ }}}` /
827
+ // post-render replace) rather than HTML-escaping the quotes. The
828
+ // values are otherwise opaque to the renderer — no string
829
+ // concatenation possible at this layer.
830
+ var pkJson = JSON.stringify(opts.publishable_key);
831
+ var secretJson = JSON.stringify(opts.client_secret);
832
+ if (opts.theme) {
833
+ return opts.theme.render("pay", {
834
+ title: "Pay",
835
+ shop_name: shopName,
836
+ cart_count: cartCount,
837
+ order_id: opts.order.id,
838
+ grand_total: grandTotal,
839
+ pk_json: pkJson,
840
+ client_secret_json: secretJson,
841
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
842
+ });
843
+ }
844
+ var body = _render(PAY_PAGE, {
845
+ order_id: opts.order.id,
846
+ grand_total: grandTotal,
847
+ pk_json: "RAW_PK",
848
+ client_secret_json: "RAW_SECRET",
849
+ }).replace("RAW_PK", pkJson)
850
+ .replace("RAW_SECRET", secretJson);
851
+ return _wrap({
852
+ title: "Pay",
853
+ shop_name: shopName,
854
+ cart_count: cartCount,
855
+ theme_css: opts.theme_css,
856
+ body: body,
857
+ });
858
+ }
859
+
860
+ var ORDER_PAGE =
861
+ "<section class=\"order-page\">\n" +
862
+ " <header class=\"section-head\">\n" +
863
+ " <p class=\"eyebrow\">Order confirmed</p>\n" +
864
+ " <h1 class=\"section-head__title\">Order <code class=\"inline-code\">{{order_id}}</code></h1>\n" +
865
+ " <p class=\"section-head__lede\">Status: <span class=\"pdp__badge pdp__badge--ok\"><span class=\"dot dot--live\" aria-hidden=\"true\"></span> {{status}}</span></p>\n" +
866
+ " </header>\n" +
867
+ " <div class=\"order-page__grid\">\n" +
868
+ " <div class=\"order-page__items\">\n" +
869
+ " <h2 class=\"pdp__variants-title\">Items</h2>\n" +
870
+ " <div class=\"table-scroll\">\n" +
871
+ " <table>\n" +
872
+ " <thead><tr><th>SKU</th><th>Qty</th><th>Unit</th><th>Total</th></tr></thead>\n" +
873
+ " <tbody>{{line_rows}}</tbody>\n" +
874
+ " </table>\n" +
875
+ " </div>\n" +
876
+ " </div>\n" +
877
+ " <aside class=\"order-page__totals\">\n" +
878
+ " <h2 class=\"pdp__variants-title\">Totals</h2>\n" +
879
+ " <dl class=\"totals-list\">\n" +
880
+ " <div><dt>Subtotal</dt><dd>{{subtotal}}</dd></div>\n" +
881
+ " <div><dt>Tax</dt><dd>{{tax}}</dd></div>\n" +
882
+ " <div><dt>Shipping</dt><dd>{{shipping}}</dd></div>\n" +
883
+ " <div class=\"totals-list__grand\"><dt>Total</dt><dd>{{total}}</dd></div>\n" +
884
+ " </dl>\n" +
885
+ " </aside>\n" +
886
+ " </div>\n" +
887
+ "</section>\n";
888
+
889
+ function renderOrder(opts) {
890
+ if (!opts || !opts.order) throw new TypeError("storefront.renderOrder: opts.order required");
891
+ var o = opts.order;
892
+ var lines = o.lines || [];
893
+ var shopName = opts.shop_name || "blamejs.shop";
894
+ var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
895
+ var rendered = lines.map(function (l) {
896
+ return {
897
+ sku: l.sku,
898
+ qty: String(l.qty),
899
+ unit: pricing.format(l.unit_amount_minor, l.unit_currency),
900
+ total: pricing.format(l.line_total_minor || (l.qty * l.unit_amount_minor), l.unit_currency),
901
+ };
902
+ });
903
+ var subtotal = pricing.format(o.subtotal_minor, o.currency);
904
+ var tax = pricing.format(o.tax_minor, o.currency);
905
+ var shipping = pricing.format(o.shipping_minor, o.currency);
906
+ var total = pricing.format(o.grand_total_minor, o.currency);
907
+ if (opts.theme) {
908
+ return opts.theme.render("order", {
909
+ title: "Order " + o.id,
910
+ shop_name: shopName,
911
+ cart_count: cartCount,
912
+ order_id: o.id,
913
+ status: o.status,
914
+ lines: rendered,
915
+ has_lines: rendered.length > 0,
916
+ subtotal: subtotal,
917
+ tax: tax,
918
+ shipping: shipping,
919
+ total: total,
920
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
921
+ });
922
+ }
923
+ var rows = rendered.map(function (l) {
924
+ return _render(CART_LINE, { sku: l.sku, qty: l.qty, unit: l.unit, total: l.total });
925
+ }).join("");
926
+ if (!rows) rows = "<tr><td colspan=\"4\" class=\"empty\">No items.</td></tr>";
927
+ var body = _render(ORDER_PAGE, {
928
+ order_id: o.id,
929
+ status: o.status,
930
+ line_rows: "RAW_LINES",
931
+ subtotal: subtotal,
932
+ tax: tax,
933
+ shipping: shipping,
934
+ total: total,
935
+ }).replace("RAW_LINES", rows);
936
+ return _wrap({
937
+ title: "Order " + o.id,
938
+ shop_name: shopName,
939
+ cart_count: cartCount,
940
+ theme_css: opts.theme_css,
941
+ body: body,
942
+ });
943
+ }
944
+
945
+ var CART_PAGE =
946
+ "<section class=\"cart-page\">\n" +
947
+ " <header class=\"section-head\">\n" +
948
+ " <p class=\"eyebrow\">Your cart</p>\n" +
949
+ " <h1 class=\"section-head__title\">Review your items</h1>\n" +
950
+ " </header>\n" +
951
+ " <div class=\"cart-page__grid\">\n" +
952
+ " <div class=\"cart-page__items\">\n" +
953
+ " <div class=\"table-scroll\">\n" +
954
+ " <table class=\"cart-table\">\n" +
955
+ " <thead><tr><th>SKU</th><th>Quantity</th><th>Unit</th><th>Total</th><th class=\"variant-table__action-h\">Action</th></tr></thead>\n" +
956
+ " <tbody>{{line_rows}}</tbody>\n" +
957
+ " </table>\n" +
958
+ " </div>\n" +
959
+ " </div>\n" +
960
+ " <aside class=\"cart-page__summary\">\n" +
961
+ " <h2 class=\"pdp__variants-title\">Order summary</h2>\n" +
962
+ " <dl class=\"totals-list\">\n" +
963
+ " <div><dt>Subtotal</dt><dd>{{subtotal}}</dd></div>\n" +
964
+ " <div class=\"totals-list__grand\"><dt>Total</dt><dd>{{total}}</dd></div>\n" +
965
+ " </dl>\n" +
966
+ " <a href=\"/checkout\" class=\"btn-primary cart-page__checkout\">Continue to checkout <span aria-hidden=\"true\">→</span></a>\n" +
967
+ " <p class=\"cart-page__note\">Tax and shipping are calculated on the next step. Payment runs through Stripe.</p>\n" +
968
+ " </aside>\n" +
969
+ " </div>\n" +
970
+ "</section>\n";
971
+
972
+ function renderCart(opts) {
973
+ if (!opts) throw new TypeError("storefront.renderCart: opts required");
974
+ var lines = opts.lines || [];
975
+ var totals = opts.totals || { subtotal_minor: 0, grand_total_minor: 0, currency: "USD" };
976
+ var shopName = opts.shop_name || "blamejs.shop";
977
+ var rendered = lines.map(function (l) {
978
+ return {
979
+ id: l.id,
980
+ sku: l.sku,
981
+ qty: String(l.qty),
982
+ unit: pricing.format(l.unit_amount_minor, l.unit_currency),
983
+ total: pricing.format(l.qty * l.unit_amount_minor, l.unit_currency),
984
+ };
985
+ });
986
+ var subtotal = pricing.format(totals.subtotal_minor, totals.currency);
987
+ var total = pricing.format(totals.grand_total_minor, totals.currency);
988
+ if (opts.theme) {
989
+ return opts.theme.render("cart", {
990
+ title: "Cart",
991
+ shop_name: shopName,
992
+ cart_count: lines.length,
993
+ lines: rendered,
994
+ has_lines: rendered.length > 0,
995
+ subtotal: subtotal,
996
+ total: total,
997
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
998
+ });
999
+ }
1000
+ var rows = rendered.map(function (l) {
1001
+ return _render(CART_LINE_EDITABLE, {
1002
+ sku: l.sku, qty: l.qty, unit: l.unit, total: l.total, line_id: l.id,
1003
+ });
1004
+ }).join("");
1005
+ if (!rows) rows = "<tr><td colspan=\"5\" class=\"empty\">Your cart is empty.</td></tr>";
1006
+ var body = _render(CART_PAGE, {
1007
+ line_rows: "RAW_LINES",
1008
+ subtotal: subtotal,
1009
+ total: total,
1010
+ }).replace("RAW_LINES", rows);
1011
+ return _wrap({
1012
+ title: "Cart",
1013
+ shop_name: shopName,
1014
+ cart_count: lines.length,
1015
+ theme_css: opts.theme_css,
1016
+ body: body,
1017
+ });
1018
+ }
1019
+
1020
+ // ---- admin landing (HTML — the rest of /admin/* is JSON API) ----------
1021
+
1022
+ function renderAdminLanding(opts) {
1023
+ opts = opts || {};
1024
+ var body =
1025
+ "<section class=\"admin-landing\">" +
1026
+ "<header class=\"section-head\">" +
1027
+ "<p class=\"eyebrow\">Admin API</p>" +
1028
+ "<h1 class=\"section-head__title\">There's no admin GUI — operators talk to the framework over HTTP.</h1>" +
1029
+ "<p class=\"section-head__lede\">Every admin verb (create / update / archive / refund / restock) is a bearer-token-gated JSON endpoint under <code class=\"inline-code\">/admin/*</code>. Pair it with curl, an API client, or any internal ops console.</p>" +
1030
+ "</header>" +
1031
+ "<div class=\"admin-landing__grid\">" +
1032
+ "<article class=\"admin-card\">" +
1033
+ "<p class=\"admin-card__verb\">POST</p>" +
1034
+ "<h2>Create a product</h2>" +
1035
+ "<pre class=\"admin-card__code\"><code>curl -X POST $SHOP/admin/products \\\n -H \"authorization: Bearer $ADMIN_API_KEY\" \\\n -d '{ \"title\": \"Widget\", \"slug\": \"widget\" }'</code></pre>" +
1036
+ "</article>" +
1037
+ "<article class=\"admin-card\">" +
1038
+ "<p class=\"admin-card__verb\">GET</p>" +
1039
+ "<h2>Search products</h2>" +
1040
+ "<pre class=\"admin-card__code\"><code>curl $SHOP/admin/products/search?q=tee \\\n -H \"authorization: Bearer $ADMIN_API_KEY\"</code></pre>" +
1041
+ "</article>" +
1042
+ "<article class=\"admin-card\">" +
1043
+ "<p class=\"admin-card__verb\">POST</p>" +
1044
+ "<h2>Restock inventory</h2>" +
1045
+ "<pre class=\"admin-card__code\"><code>curl -X POST $SHOP/admin/inventory/OPR-TEE-BLK-L/restock \\\n -H \"authorization: Bearer $ADMIN_API_KEY\" \\\n -d '{ \"qty\": 25 }'</code></pre>" +
1046
+ "</article>" +
1047
+ "<article class=\"admin-card\">" +
1048
+ "<p class=\"admin-card__verb\">POST</p>" +
1049
+ "<h2>Issue a refund</h2>" +
1050
+ "<pre class=\"admin-card__code\"><code>curl -X POST $SHOP/admin/orders/$ID/refund \\\n -H \"authorization: Bearer $ADMIN_API_KEY\" \\\n -d '{ \"amount_minor\": 3200 }'</code></pre>" +
1051
+ "</article>" +
1052
+ "</div>" +
1053
+ "<aside class=\"admin-landing__note\">" +
1054
+ "<p><strong>Why no GUI?</strong> Treating the admin as an API keeps the surface area auditable — every state change lands as a request you can replay, log, and sign. Build the GUI your ops team needs; the framework stays out of the way.</p>" +
1055
+ "<p>Endpoint reference: <a href=\"https://github.com/blamejs/blamejs.shop/blob/main/lib/admin.js\" rel=\"noopener\" class=\"link-arrow\">lib/admin.js on GitHub <span aria-hidden=\"true\">→</span></a></p>" +
1056
+ "</aside>" +
1057
+ "</section>";
1058
+ return _wrap({
1059
+ title: "Admin",
1060
+ shop_name: opts.shop_name || "blamejs.shop",
1061
+ cart_count: opts.cart_count,
1062
+ theme_css: opts.theme_css,
1063
+ body: body,
1064
+ });
1065
+ }
1066
+
1067
+ // ---- newsletter thank-you ---------------------------------------------
1068
+
1069
+ function renderNewsletterThanks(opts) {
1070
+ opts = opts || {};
1071
+ // Two flavors: `new` (we just enrolled this address) and `dedup`
1072
+ // (the address was already on the list). Both render the same
1073
+ // shell with copy that tells the visitor what happened, so a
1074
+ // double-submit doesn't read as a quiet failure.
1075
+ var heading, lede;
1076
+ if (opts.status === "dedup") {
1077
+ heading = "You're already on the list.";
1078
+ lede = "We had this address from a previous signup. Nothing changed — you'll get release notes the day they ship.";
1079
+ } else {
1080
+ heading = "You're on the list.";
1081
+ lede = "We'll email you the day there's a new framework release, a security advisory, or a primitive worth knowing about. Nothing else.";
1082
+ }
1083
+ var body =
1084
+ "<section class=\"newsletter-thanks\">" +
1085
+ "<div class=\"newsletter-thanks__card\">" +
1086
+ "<p class=\"eyebrow\">Newsletter</p>" +
1087
+ "<h1 class=\"newsletter-thanks__title\">" + heading + "</h1>" +
1088
+ "<p class=\"newsletter-thanks__lede\">" + lede + "</p>" +
1089
+ "<div class=\"newsletter-thanks__cta\">" +
1090
+ "<a href=\"/\" class=\"btn-primary\">Back to the shop <span aria-hidden=\"true\">→</span></a>" +
1091
+ "<a href=\"https://github.com/blamejs/blamejs.shop\" class=\"btn-ghost\" rel=\"noopener\">Star on GitHub</a>" +
1092
+ "</div>" +
1093
+ "</div>" +
1094
+ "</section>";
1095
+ return _wrap({
1096
+ title: "Newsletter",
1097
+ shop_name: opts.shop_name || "blamejs.shop",
1098
+ cart_count: opts.cart_count,
1099
+ theme_css: opts.theme_css,
1100
+ body: body,
1101
+ });
1102
+ }
1103
+
1104
+ function renderNewsletterError(opts) {
1105
+ opts = opts || {};
1106
+ var body =
1107
+ "<section class=\"newsletter-thanks\">" +
1108
+ "<div class=\"newsletter-thanks__card\">" +
1109
+ "<p class=\"eyebrow\">Newsletter</p>" +
1110
+ "<h1 class=\"newsletter-thanks__title\">Couldn't enroll that address.</h1>" +
1111
+ "<p class=\"newsletter-thanks__lede\">" + (opts.message || "Check the address and try again — only RFC-shape email addresses are accepted.") + "</p>" +
1112
+ "<div class=\"newsletter-thanks__cta\">" +
1113
+ "<a href=\"/\" class=\"btn-primary\">Back to the shop <span aria-hidden=\"true\">→</span></a>" +
1114
+ "</div>" +
1115
+ "</div>" +
1116
+ "</section>";
1117
+ return _wrap({
1118
+ title: "Newsletter",
1119
+ shop_name: opts.shop_name || "blamejs.shop",
1120
+ cart_count: opts.cart_count,
1121
+ theme_css: opts.theme_css,
1122
+ body: body,
1123
+ });
1124
+ }
1125
+
1126
+ // ---- 404 ---------------------------------------------------------------
1127
+
1128
+ function renderNotFound(opts) {
1129
+ opts = opts || {};
1130
+ var shopName = opts.shop_name || "blamejs.shop";
1131
+ var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
1132
+ if (opts.theme) {
1133
+ return opts.theme.render("notfound", {
1134
+ title: "Not found",
1135
+ shop_name: shopName,
1136
+ cart_count: cartCount,
1137
+ asset_css_main: opts.theme.assetUrl("css/main.css"),
1138
+ });
1139
+ }
1140
+ var body =
1141
+ "<section class=\"not-found\">" +
1142
+ "<div class=\"not-found__inner\">" +
1143
+ "<p class=\"not-found__code\">404</p>" +
1144
+ "<h1 class=\"not-found__title\">That page slipped the catalog.</h1>" +
1145
+ "<p class=\"not-found__lede\">The URL didn't match any route on this storefront. The product might have moved, the slug might have changed, or the link might have been mistyped.</p>" +
1146
+ "<div class=\"not-found__cta\">" +
1147
+ "<a href=\"/\" class=\"btn-primary\">Back to the shop <span aria-hidden=\"true\">→</span></a>" +
1148
+ "<a href=\"/cart\" class=\"btn-ghost\">View your cart</a>" +
1149
+ "</div>" +
1150
+ "</div>" +
1151
+ "</section>";
1152
+ return _wrap({
1153
+ title: "Not found",
1154
+ shop_name: shopName,
1155
+ cart_count: cartCount,
1156
+ theme_css: opts.theme_css,
1157
+ body: body,
1158
+ });
1159
+ }
1160
+
1161
+ // ---- route mount -------------------------------------------------------
1162
+ //
1163
+ // Caller (server.js) hands us a b.router instance + the data deps.
1164
+ // We mount the read-only HTML routes. POST routes for cart mutation
1165
+ // land alongside Stripe Elements wiring in the next patch.
1166
+
1167
+ // Session-id cookie binding — carries the cart's session_id across
1168
+ // requests. Plain HttpOnly + Secure + SameSite=Lax is sufficient here
1169
+ // because the value (a UUID) is unguessable and grants ZERO authority
1170
+ // — it's a routing key, not an authentication token. The cart itself
1171
+ // transitions to `customer_id` on login via cart.setCustomer.
1172
+ var SESSION_COOKIE_NAME = "shop_sid";
1173
+ var SESSION_COOKIE_MAX = 60 * 60 * 24 * 30; // 30 days
1174
+
1175
+ // Authenticated-customer cookie — carries an opaque sealed envelope
1176
+ // `{ customer_id, exp }`, AEAD-encrypted via b.vault.seal so the
1177
+ // cookie itself has no internal structure visible to a tampering
1178
+ // network attacker. Cookie name `shop_auth`; HttpOnly + Secure +
1179
+ // SameSite=Lax + Path=/. The vault key is the deployment's KEK; a
1180
+ // rotated vault invalidates every outstanding auth cookie (operator-
1181
+ // initiated logout-everywhere).
1182
+ var AUTH_COOKIE_NAME = "shop_auth";
1183
+ var AUTH_COOKIE_MAX = 60 * 60 * 24 * 14; // 14 days
1184
+ var AUTH_TTL_MS = 14 * 24 * 60 * 60 * 1000;
1185
+
1186
+ // WebAuthn ceremony state cookie — short-lived envelope holding the
1187
+ // random challenge + the ceremony-scoped metadata so register-finish /
1188
+ // login-finish can verify the same challenge the browser was sent.
1189
+ // Path-scoped to /account so it never leaks to other routes.
1190
+ var CHALLENGE_COOKIE_NAME = "shop_auth_chal";
1191
+ var CHALLENGE_COOKIE_MAX = 5 * 60; // 5 minutes
1192
+
1193
+ function _readSidCookie(req) {
1194
+ var raw = (req.headers && (req.headers.cookie || req.headers.Cookie)) || "";
1195
+ if (!raw) return null;
1196
+ var parts = raw.split(";");
1197
+ for (var i = 0; i < parts.length; i += 1) {
1198
+ var p = parts[i].trim();
1199
+ var eq = p.indexOf("=");
1200
+ if (eq <= 0) continue;
1201
+ if (p.slice(0, eq) === SESSION_COOKIE_NAME) {
1202
+ var v = p.slice(eq + 1);
1203
+ // Cookie values are URL-encoded.
1204
+ try { return decodeURIComponent(v); } catch (_e) { return null; }
1205
+ }
1206
+ }
1207
+ return null;
1208
+ }
1209
+
1210
+ function _setSidCookie(res, sid) {
1211
+ var attrs = "Max-Age=" + SESSION_COOKIE_MAX + "; Path=/; HttpOnly; Secure; SameSite=Lax";
1212
+ var header = SESSION_COOKIE_NAME + "=" + encodeURIComponent(sid) + "; " + attrs;
1213
+ if (typeof res.appendHeader === "function") res.appendHeader("Set-Cookie", header);
1214
+ else if (typeof res.setHeader === "function") res.setHeader("Set-Cookie", header);
1215
+ }
1216
+
1217
+ function _readCookie(req, name) {
1218
+ var raw = (req.headers && (req.headers.cookie || req.headers.Cookie)) || "";
1219
+ if (!raw) return null;
1220
+ var parts = raw.split(";");
1221
+ for (var i = 0; i < parts.length; i += 1) {
1222
+ var p = parts[i].trim();
1223
+ var eq = p.indexOf("=");
1224
+ if (eq <= 0) continue;
1225
+ if (p.slice(0, eq) === name) {
1226
+ try { return decodeURIComponent(p.slice(eq + 1)); }
1227
+ catch (_e) { return null; }
1228
+ }
1229
+ }
1230
+ return null;
1231
+ }
1232
+
1233
+ function _appendCookie(res, header) {
1234
+ if (typeof res.appendHeader === "function") res.appendHeader("Set-Cookie", header);
1235
+ else if (typeof res.setHeader === "function") res.setHeader("Set-Cookie", header);
1236
+ }
1237
+
1238
+ function _setAuthCookie(res, sealed) {
1239
+ var attrs = "Max-Age=" + AUTH_COOKIE_MAX + "; Path=/; HttpOnly; Secure; SameSite=Lax";
1240
+ _appendCookie(res, AUTH_COOKIE_NAME + "=" + encodeURIComponent(sealed) + "; " + attrs);
1241
+ }
1242
+
1243
+ function _clearAuthCookie(res) {
1244
+ _appendCookie(res,
1245
+ AUTH_COOKIE_NAME + "=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax");
1246
+ }
1247
+
1248
+ function _setChallengeCookie(res, sealed) {
1249
+ var attrs = "Max-Age=" + CHALLENGE_COOKIE_MAX + "; Path=/account; HttpOnly; Secure; SameSite=Lax";
1250
+ _appendCookie(res, CHALLENGE_COOKIE_NAME + "=" + encodeURIComponent(sealed) + "; " + attrs);
1251
+ }
1252
+
1253
+ function _clearChallengeCookie(res) {
1254
+ _appendCookie(res,
1255
+ CHALLENGE_COOKIE_NAME + "=; Max-Age=0; Path=/account; HttpOnly; Secure; SameSite=Lax");
1256
+ }
1257
+
1258
+ // ---- account-page renderers --------------------------------------------
1259
+
1260
+ var ACCOUNT_LOGIN_PAGE =
1261
+ "<section class=\"auth-page\">\n" +
1262
+ " <div class=\"auth-card\">\n" +
1263
+ " <p class=\"eyebrow\">Sign in</p>\n" +
1264
+ " <h1 class=\"auth-card__title\">Welcome back</h1>\n" +
1265
+ " <p class=\"auth-card__lede\">Enter your email and authenticate with your passkey. No password to type, no recovery email to click.</p>\n" +
1266
+ " <form id=\"login-form\" method=\"post\" class=\"form-stack auth-form\">\n" +
1267
+ " <div class=\"form-row\"><label class=\"form-field\"><span class=\"form-field__label\">Email</span><input type=\"email\" name=\"email\" id=\"email\" required autocomplete=\"email\" autofocus></label></div>\n" +
1268
+ " <div class=\"form-actions\"><button type=\"submit\" class=\"btn-primary auth-form__submit\">Sign in with passkey</button></div>\n" +
1269
+ " <p id=\"login-message\" class=\"auth-form__message\"></p>\n" +
1270
+ " </form>\n" +
1271
+ " <p class=\"auth-card__alt\">New here? <a href=\"/account/register\">Create an account →</a></p>\n" +
1272
+ " </div>\n" +
1273
+ " <script>\n" +
1274
+ " (function () {\n" +
1275
+ " function _b64uToBuf(s){ s = s.replace(/-/g,'+').replace(/_/g,'/'); while(s.length%4)s+='='; var raw=atob(s); var arr=new Uint8Array(raw.length); for(var i=0;i<raw.length;i++)arr[i]=raw.charCodeAt(i); return arr.buffer; }\n" +
1276
+ " function _bufToB64u(buf){ var b=new Uint8Array(buf), s=''; for(var i=0;i<b.length;i++)s+=String.fromCharCode(b[i]); return btoa(s).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,''); }\n" +
1277
+ " var form=document.getElementById('login-form');\n" +
1278
+ " var msg=document.getElementById('login-message');\n" +
1279
+ " form.addEventListener('submit', async function(ev){\n" +
1280
+ " ev.preventDefault(); msg.textContent='Requesting challenge...';\n" +
1281
+ " try {\n" +
1282
+ " var beginR = await fetch('/account/passkey/login-begin', { method:'POST', credentials:'same-origin', headers:{'content-type':'application/json'}, body: JSON.stringify({ email: document.getElementById('email').value }) });\n" +
1283
+ " if (!beginR.ok) { msg.textContent = 'Sign-in unavailable.'; return; }\n" +
1284
+ " var options = await beginR.json();\n" +
1285
+ " options.challenge = _b64uToBuf(options.challenge);\n" +
1286
+ " if (options.allowCredentials) options.allowCredentials = options.allowCredentials.map(function(c){ return Object.assign({}, c, { id: _b64uToBuf(c.id) }); });\n" +
1287
+ " var assertion = await navigator.credentials.get({ publicKey: options });\n" +
1288
+ " var payload = { id: assertion.id, rawId: _bufToB64u(assertion.rawId), type: assertion.type, response: { authenticatorData: _bufToB64u(assertion.response.authenticatorData), clientDataJSON: _bufToB64u(assertion.response.clientDataJSON), signature: _bufToB64u(assertion.response.signature), userHandle: assertion.response.userHandle ? _bufToB64u(assertion.response.userHandle) : null } };\n" +
1289
+ " var finishR = await fetch('/account/passkey/login-finish', { method:'POST', credentials:'same-origin', headers:{'content-type':'application/json'}, body: JSON.stringify(payload) });\n" +
1290
+ " if (finishR.ok) { window.location.href = '/account'; } else { msg.textContent = 'Sign-in failed.'; }\n" +
1291
+ " } catch (e) { msg.textContent = (e && e.message) || 'Sign-in error.'; }\n" +
1292
+ " });\n" +
1293
+ " })();\n" +
1294
+ " </script>\n" +
1295
+ "</section>\n";
1296
+
1297
+ function renderAccountLogin(opts) {
1298
+ opts = opts || {};
1299
+ return _wrap({
1300
+ title: "Sign in",
1301
+ shop_name: opts.shop_name || "blamejs.shop",
1302
+ cart_count: opts.cart_count,
1303
+ theme_css: opts.theme_css,
1304
+ body: ACCOUNT_LOGIN_PAGE,
1305
+ });
1306
+ }
1307
+
1308
+ var ACCOUNT_REGISTER_PAGE =
1309
+ "<section class=\"auth-page\">\n" +
1310
+ " <div class=\"auth-card\">\n" +
1311
+ " <p class=\"eyebrow\">Create an account</p>\n" +
1312
+ " <h1 class=\"auth-card__title\">Enroll a passkey</h1>\n" +
1313
+ " <p class=\"auth-card__lede\">Accounts use a passkey on your device — no password to remember, no shared secret to phish.</p>\n" +
1314
+ " <form id=\"reg-form\" method=\"post\" class=\"form-stack auth-form\">\n" +
1315
+ " <div class=\"form-row\"><label class=\"form-field\"><span class=\"form-field__label\">Email</span><input type=\"email\" name=\"email\" id=\"email\" required autocomplete=\"email\" autofocus></label></div>\n" +
1316
+ " <div class=\"form-row\"><label class=\"form-field\"><span class=\"form-field__label\">Display name</span><input type=\"text\" name=\"display_name\" id=\"display_name\" maxlength=\"128\" required autocomplete=\"name\"></label></div>\n" +
1317
+ " <div class=\"form-actions\"><button type=\"submit\" class=\"btn-primary auth-form__submit\">Create account &amp; enroll passkey</button></div>\n" +
1318
+ " <p id=\"reg-message\" class=\"auth-form__message\"></p>\n" +
1319
+ " </form>\n" +
1320
+ " <p class=\"auth-card__alt\">Already have one? <a href=\"/account/login\">Sign in →</a></p>\n" +
1321
+ " </div>\n" +
1322
+ " <script>\n" +
1323
+ " (function () {\n" +
1324
+ " function _b64uToBuf(s){ s = s.replace(/-/g,'+').replace(/_/g,'/'); while(s.length%4)s+='='; var raw=atob(s); var arr=new Uint8Array(raw.length); for(var i=0;i<raw.length;i++)arr[i]=raw.charCodeAt(i); return arr.buffer; }\n" +
1325
+ " function _bufToB64u(buf){ var b=new Uint8Array(buf), s=''; for(var i=0;i<b.length;i++)s+=String.fromCharCode(b[i]); return btoa(s).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,''); }\n" +
1326
+ " var form=document.getElementById('reg-form');\n" +
1327
+ " var msg=document.getElementById('reg-message');\n" +
1328
+ " form.addEventListener('submit', async function(ev){\n" +
1329
+ " ev.preventDefault(); msg.textContent='Creating account...';\n" +
1330
+ " try {\n" +
1331
+ " var beginR = await fetch('/account/passkey/register-begin', { method:'POST', credentials:'same-origin', headers:{'content-type':'application/json'}, body: JSON.stringify({ email: document.getElementById('email').value, display_name: document.getElementById('display_name').value }) });\n" +
1332
+ " if (!beginR.ok) { msg.textContent = (await beginR.text()) || 'Registration unavailable.'; return; }\n" +
1333
+ " var options = await beginR.json();\n" +
1334
+ " options.challenge = _b64uToBuf(options.challenge);\n" +
1335
+ " options.user.id = _b64uToBuf(options.user.id);\n" +
1336
+ " if (options.excludeCredentials) options.excludeCredentials = options.excludeCredentials.map(function(c){ return Object.assign({}, c, { id: _b64uToBuf(c.id) }); });\n" +
1337
+ " var att = await navigator.credentials.create({ publicKey: options });\n" +
1338
+ " var payload = { id: att.id, rawId: _bufToB64u(att.rawId), type: att.type, response: { attestationObject: _bufToB64u(att.response.attestationObject), clientDataJSON: _bufToB64u(att.response.clientDataJSON), transports: att.response.getTransports ? att.response.getTransports() : [] } };\n" +
1339
+ " var finishR = await fetch('/account/passkey/register-finish', { method:'POST', credentials:'same-origin', headers:{'content-type':'application/json'}, body: JSON.stringify(payload) });\n" +
1340
+ " if (finishR.ok) { window.location.href = '/account'; } else { msg.textContent = (await finishR.text()) || 'Enrollment failed.'; }\n" +
1341
+ " } catch (e) { msg.textContent = (e && e.message) || 'Registration error.'; }\n" +
1342
+ " });\n" +
1343
+ " })();\n" +
1344
+ " </script>\n" +
1345
+ "</section>\n";
1346
+
1347
+ function renderAccountRegister(opts) {
1348
+ opts = opts || {};
1349
+ return _wrap({
1350
+ title: "Create account",
1351
+ shop_name: opts.shop_name || "blamejs.shop",
1352
+ cart_count: opts.cart_count,
1353
+ theme_css: opts.theme_css,
1354
+ body: ACCOUNT_REGISTER_PAGE,
1355
+ });
1356
+ }
1357
+
1358
+ var ACCOUNT_DASH_PAGE =
1359
+ "<section class=\"account-dash\">\n" +
1360
+ " <header class=\"account-dash__head\">\n" +
1361
+ " <div>\n" +
1362
+ " <p class=\"eyebrow\">Account</p>\n" +
1363
+ " <h1 class=\"section-head__title\">Hi, {{display_name}}</h1>\n" +
1364
+ " <p class=\"section-head__lede\">Your recent orders + account controls.</p>\n" +
1365
+ " </div>\n" +
1366
+ " <form method=\"post\" action=\"/account/logout\"><button type=\"submit\" class=\"btn-ghost\">Sign out</button></form>\n" +
1367
+ " </header>\n" +
1368
+ " <div class=\"account-dash__body\">\n" +
1369
+ " <h2 class=\"pdp__variants-title\">Recent orders</h2>\n" +
1370
+ " <div class=\"table-scroll\">\n" +
1371
+ " <table>\n" +
1372
+ " <thead><tr><th>Order</th><th>Status</th><th>Total</th></tr></thead>\n" +
1373
+ " <tbody>{{order_rows}}</tbody>\n" +
1374
+ " </table>\n" +
1375
+ " </div>\n" +
1376
+ " </div>\n" +
1377
+ "</section>\n";
1378
+
1379
+ var ACCOUNT_DASH_ORDER_ROW =
1380
+ "<tr><td><a href=\"/orders/{{order_id}}\">{{order_id_short}}</a></td><td>{{status}}</td><td class=\"price\">{{total}}</td></tr>\n";
1381
+
1382
+ function renderAccount(opts) {
1383
+ if (!opts || !opts.customer) throw new TypeError("storefront.renderAccount: opts.customer required");
1384
+ var rows = (opts.orders || []).map(function (o) {
1385
+ return _render(ACCOUNT_DASH_ORDER_ROW, {
1386
+ order_id: o.id,
1387
+ order_id_short: o.id.slice(0, 8),
1388
+ status: o.status,
1389
+ total: pricing.format(o.grand_total_minor, o.currency),
1390
+ });
1391
+ }).join("");
1392
+ if (!rows) rows = "<tr><td colspan=\"3\" class=\"empty\">No orders yet.</td></tr>";
1393
+ var body = _render(ACCOUNT_DASH_PAGE, {
1394
+ display_name: opts.customer.display_name,
1395
+ order_rows: "RAW_ORDER_ROWS",
1396
+ }).replace("RAW_ORDER_ROWS", rows);
1397
+ return _wrap({
1398
+ title: "Account",
1399
+ shop_name: opts.shop_name || "blamejs.shop",
1400
+ cart_count: opts.cart_count,
1401
+ theme_css: opts.theme_css,
1402
+ body: body,
1403
+ });
1404
+ }
1405
+
1406
+ function mount(router, deps) {
1407
+ if (!router || typeof router.get !== "function") throw new TypeError("storefront.mount: router with .get() required");
1408
+ if (!deps || !deps.catalog || !deps.cart) throw new TypeError("storefront.mount: deps.catalog + deps.cart required");
1409
+ var shopName = (deps.config && deps.config.shop_name) || "blamejs.shop";
1410
+ // Optional theme — when supplied, every renderer below dispatches
1411
+ // to file-backed templates under <themesDir>/<name>/. When absent,
1412
+ // the inline-string templates above stay in force (operators on
1413
+ // older deploys keep their current look without a migration step).
1414
+ var theme = deps.theme || null;
1415
+
1416
+ function _send(res, status, html) {
1417
+ res.status(status);
1418
+ res.setHeader && res.setHeader("content-type", "text/html; charset=utf-8");
1419
+ res.end ? res.end(html) : res.send(html);
1420
+ }
1421
+
1422
+ // Cart-count read shared across every handler that wraps a page in
1423
+ // the layout — the header's cart pill renders the count. Returns 0
1424
+ // for visitors with no session cookie. Defined at the top of mount
1425
+ // so the /admin landing + the onNotFound 404 handler (mounted
1426
+ // outside the `if (deps.customers)` block below) can reach it.
1427
+ async function _cartCountForReq(req) {
1428
+ var sid = _readCookie(req, SESSION_COOKIE_NAME);
1429
+ if (!sid) return 0;
1430
+ var c = await deps.cart.bySession(sid);
1431
+ if (!c) return 0;
1432
+ var lines = await deps.cart.listLines(c.id);
1433
+ return lines.length;
1434
+ }
1435
+
1436
+ // Resolve the cart for this request — read session_id from the
1437
+ // sealed cookie, create one (and the cart) if absent. Returns
1438
+ // the cart row OR null when the cart was just created (caller can
1439
+ // use { sid, cart: null } to skip lookup).
1440
+ async function _getOrCreateCart(req, res, currency) {
1441
+ var sid = _readSidCookie(req);
1442
+ if (!sid) {
1443
+ sid = _b().uuid.v7();
1444
+ _setSidCookie(res, sid);
1445
+ }
1446
+ var existing = await deps.cart.bySession(sid);
1447
+ if (existing) return { sid: sid, cart: existing };
1448
+ var created = await deps.cart.create(sid, { currency: currency || "USD" });
1449
+ return { sid: sid, cart: created };
1450
+ }
1451
+
1452
+ router.get("/", async function (_req, res) {
1453
+ var page = await deps.catalog.products.list({ status: "active", limit: 24 });
1454
+ // Best-effort "starting price" + "hero media" lookup. Each
1455
+ // product on the home grid carries its first variant's USD
1456
+ // price (when one exists) and its first attached media row
1457
+ // (when one exists). Both are best-effort — products without
1458
+ // a price render `—`; products without media render the
1459
+ // text-only PRODUCT_CARD fallback.
1460
+ var products = [];
1461
+ for (var i = 0; i < page.rows.length; i += 1) {
1462
+ var p = page.rows[i];
1463
+ var variants = await deps.catalog.variants.listForProduct(p.id);
1464
+ var startingPrice = null;
1465
+ if (variants.length) {
1466
+ var price = await deps.catalog.prices.current(variants[0].id, "USD");
1467
+ if (price) startingPrice = price;
1468
+ }
1469
+ var media = await deps.catalog.media.listForProduct(p.id);
1470
+ var heroMedia = media.length ? media[0] : null;
1471
+ products.push(Object.assign({}, p, {
1472
+ starting_price_minor: startingPrice ? startingPrice.amount_minor : null,
1473
+ starting_price_currency: startingPrice ? startingPrice.currency : "USD",
1474
+ hero_media: heroMedia,
1475
+ }));
1476
+ }
1477
+ var html = renderHome({ products: products, shop_name: shopName, theme: theme });
1478
+ _send(res, 200, html);
1479
+ });
1480
+
1481
+ router.get("/search", async function (req, res) {
1482
+ var url = req.url ? new URL(req.url, "http://localhost") : null;
1483
+ var qRaw = url && url.searchParams.get("q");
1484
+ var q = typeof qRaw === "string" ? qRaw : "";
1485
+ // Cap at the validator's max length before handing to the
1486
+ // primitive — defends against a 10 MiB `?q=...` mass that would
1487
+ // otherwise round-trip through the LIKE escape function.
1488
+ if (q.length > 200) q = q.slice(0, 200);
1489
+ var products = [];
1490
+ if (q.trim().length > 0) {
1491
+ var page = await deps.catalog.products.search({ q: q, status: "active", limit: 24 });
1492
+ for (var i = 0; i < page.rows.length; i += 1) {
1493
+ var p = page.rows[i];
1494
+ var variants = await deps.catalog.variants.listForProduct(p.id);
1495
+ var startingPrice = null;
1496
+ if (variants.length) {
1497
+ var price = await deps.catalog.prices.current(variants[0].id, "USD");
1498
+ if (price) startingPrice = price;
1499
+ }
1500
+ var media = await deps.catalog.media.listForProduct(p.id);
1501
+ var heroMedia = media.length ? media[0] : null;
1502
+ products.push(Object.assign({}, p, {
1503
+ starting_price_minor: startingPrice ? startingPrice.amount_minor : null,
1504
+ starting_price_currency: startingPrice ? startingPrice.currency : "USD",
1505
+ hero_media: heroMedia,
1506
+ }));
1507
+ }
1508
+ }
1509
+ var sid = _readSidCookie(req);
1510
+ var cartCount = 0;
1511
+ if (sid) {
1512
+ var c = await deps.cart.bySession(sid);
1513
+ if (c) {
1514
+ var lines = await deps.cart.listLines(c.id);
1515
+ cartCount = lines.length;
1516
+ }
1517
+ }
1518
+ _send(res, 200, renderSearch({
1519
+ q: q,
1520
+ products: products,
1521
+ shop_name: shopName,
1522
+ cart_count: cartCount,
1523
+ }));
1524
+ });
1525
+
1526
+ router.get("/products/:slug", async function (req, res) {
1527
+ var slug = req.params && req.params.slug;
1528
+ if (!slug) return _send(res, 400, renderNotFound({ shop_name: shopName, theme: theme }));
1529
+ var product = await deps.catalog.products.bySlug(slug);
1530
+ if (!product) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1531
+ var variants = await deps.catalog.variants.listForProduct(product.id);
1532
+ var prices = {};
1533
+ for (var i = 0; i < variants.length; i += 1) {
1534
+ var p = await deps.catalog.prices.current(variants[i].id, "USD");
1535
+ if (p) prices[variants[i].id] = p;
1536
+ }
1537
+ // Media — first row drives the hero image, the next three feed
1538
+ // the thumbnail strip. `listForProduct` is product-level only;
1539
+ // variant-level media (`listForVariant`) would feed a swap-on-
1540
+ // variant-select interaction we don't ship yet.
1541
+ var media = await deps.catalog.media.listForProduct(product.id);
1542
+ // Render cart count from the current session's cart, if any.
1543
+ var sid = _readSidCookie(req);
1544
+ var cartCount = 0;
1545
+ if (sid) {
1546
+ var c = await deps.cart.bySession(sid);
1547
+ if (c) {
1548
+ var lines = await deps.cart.listLines(c.id);
1549
+ cartCount = lines.length;
1550
+ }
1551
+ }
1552
+ var html = renderProduct({
1553
+ product: product,
1554
+ variants: variants,
1555
+ prices: prices,
1556
+ media: media,
1557
+ shop_name: shopName,
1558
+ cart_count: cartCount,
1559
+ theme: theme,
1560
+ });
1561
+ _send(res, 200, html);
1562
+ });
1563
+
1564
+ router.get("/cart", async function (req, res) {
1565
+ var sid = _readSidCookie(req);
1566
+ if (!sid) {
1567
+ return _send(res, 200, renderCart({
1568
+ lines: [], totals: { subtotal_minor: 0, grand_total_minor: 0, currency: "USD" },
1569
+ shop_name: shopName, theme: theme,
1570
+ }));
1571
+ }
1572
+ var c = await deps.cart.bySession(sid);
1573
+ if (!c) {
1574
+ return _send(res, 200, renderCart({
1575
+ lines: [], totals: { subtotal_minor: 0, grand_total_minor: 0, currency: "USD" },
1576
+ shop_name: shopName, theme: theme,
1577
+ }));
1578
+ }
1579
+ var lines = await deps.cart.listLines(c.id);
1580
+ var totals = pricing.totals(c, lines, {});
1581
+ _send(res, 200, renderCart({ lines: lines, totals: totals, shop_name: shopName, theme: theme }));
1582
+ });
1583
+
1584
+ // ---- checkout flow -------------------------------------------------
1585
+ //
1586
+ // GET /checkout — renders the shipping form
1587
+ // POST /checkout — calls checkout.confirm; redirects to /pay/:order_id
1588
+ // GET /pay/:order_id — Stripe Elements payment page
1589
+ // GET /orders/:order_id — order confirmation (post-purchase landing)
1590
+ //
1591
+ // The checkout / payment / order deps are optional in mount(); the
1592
+ // routes only register when supplied. This lets the framework boot
1593
+ // in pure-storefront mode (catalog + cart only) for stores that
1594
+ // are still configuring payment.
1595
+ if (deps.checkout && deps.order) {
1596
+ router.get("/checkout", async function (req, res) {
1597
+ var sid = _readSidCookie(req);
1598
+ if (!sid) return _send(res, 303, "<a href=\"/cart\">Cart is empty</a>"), res.setHeader && res.setHeader("location", "/cart");
1599
+ var c = await deps.cart.bySession(sid);
1600
+ if (!c) {
1601
+ res.status(303); res.setHeader && res.setHeader("location", "/cart");
1602
+ return res.end ? res.end() : res.send("");
1603
+ }
1604
+ var lines = await deps.cart.listLines(c.id);
1605
+ if (!lines.length) {
1606
+ res.status(303); res.setHeader && res.setHeader("location", "/cart");
1607
+ return res.end ? res.end() : res.send("");
1608
+ }
1609
+ var totals = pricing.totals(c, lines, {});
1610
+ _send(res, 200, renderCheckoutForm({ lines: lines, totals: totals, shop_name: shopName, theme: theme }));
1611
+ });
1612
+
1613
+ router.post("/checkout", async function (req, res) {
1614
+ var body = req.body || {};
1615
+ var sid = _readSidCookie(req);
1616
+ if (!sid) {
1617
+ res.status(400); return res.end ? res.end("No session") : res.send("No session");
1618
+ }
1619
+ var c = await deps.cart.bySession(sid);
1620
+ if (!c) {
1621
+ res.status(400); return res.end ? res.end("No cart") : res.send("No cart");
1622
+ }
1623
+ // Defensive cart-state guard — if the cart has already been
1624
+ // converted (e.g. duplicate-submit on POST refresh), redirect
1625
+ // to the most recent order for this session.
1626
+ if (c.status !== "active") {
1627
+ res.status(303); res.setHeader && res.setHeader("location", "/cart");
1628
+ return res.end ? res.end() : res.send("");
1629
+ }
1630
+ var shipTo = {
1631
+ country: (body.country || "").toUpperCase(),
1632
+ state: body.state ? String(body.state).toUpperCase() : undefined,
1633
+ postal: body.postal || undefined,
1634
+ };
1635
+ try {
1636
+ // default_shipping_id may be a literal string or an
1637
+ // operator-supplied async resolver (e.g. backed by the
1638
+ // config primitive) so re-reads happen per request without
1639
+ // a container restart.
1640
+ var defaultShipId;
1641
+ if (typeof deps.default_shipping_id === "function") {
1642
+ defaultShipId = await deps.default_shipping_id();
1643
+ } else {
1644
+ defaultShipId = deps.default_shipping_id;
1645
+ }
1646
+ var result = await deps.checkout.confirm({
1647
+ cart_id: c.id,
1648
+ ship_to: shipTo,
1649
+ selected_shipping_id: defaultShipId || "std",
1650
+ customer: { email: body.email, name: body.name },
1651
+ idempotency_key: "checkout:" + c.id + ":" + _b().uuid.v7(),
1652
+ });
1653
+ // Set a short-lived pay cookie so /pay/:order_id can serve the
1654
+ // client_secret without re-running confirm.
1655
+ var payCookie = "shop_pay=" + encodeURIComponent(result.payment_intent.client_secret) +
1656
+ "; Max-Age=900; Path=/pay/; HttpOnly; Secure; SameSite=Strict";
1657
+ if (res.appendHeader) res.appendHeader("Set-Cookie", payCookie);
1658
+ else if (res.setHeader) res.setHeader("Set-Cookie", payCookie);
1659
+ res.status(303);
1660
+ res.setHeader && res.setHeader("location", "/pay/" + result.order.id);
1661
+ return res.end ? res.end() : res.send("");
1662
+ } catch (e) {
1663
+ res.status(e instanceof TypeError ? 400 : 500);
1664
+ var msg = (e && e.message) || "checkout failed";
1665
+ return res.end ? res.end(msg) : res.send(msg);
1666
+ }
1667
+ });
1668
+
1669
+ router.get("/pay/:order_id", async function (req, res) {
1670
+ var orderId = req.params && req.params.order_id;
1671
+ if (!orderId) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1672
+ var o = await deps.order.get(orderId);
1673
+ if (!o) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1674
+ // Read the client_secret from the shop_pay cookie set on POST
1675
+ // /checkout. The cookie is scoped Path=/pay/ + SameSite=Strict
1676
+ // so it's only sent to the pay route and never cross-origin.
1677
+ var rawCookies = (req.headers && (req.headers.cookie || req.headers.Cookie)) || "";
1678
+ var clientSecret = null;
1679
+ rawCookies.split(";").forEach(function (p) {
1680
+ var t = p.trim();
1681
+ if (t.indexOf("shop_pay=") === 0) {
1682
+ try { clientSecret = decodeURIComponent(t.slice("shop_pay=".length)); } catch (_e) { /* drop */ }
1683
+ }
1684
+ });
1685
+ if (!clientSecret) {
1686
+ res.status(303); res.setHeader && res.setHeader("location", "/cart");
1687
+ return res.end ? res.end() : res.send("");
1688
+ }
1689
+ var pk = deps.stripe_publishable_key || "";
1690
+ if (!pk) {
1691
+ res.status(503);
1692
+ return res.end ? res.end("Stripe publishable key not configured") : res.send("Stripe publishable key not configured");
1693
+ }
1694
+ _send(res, 200, renderPayPage({
1695
+ order: o,
1696
+ client_secret: clientSecret,
1697
+ publishable_key: pk,
1698
+ shop_name: shopName,
1699
+ theme: theme,
1700
+ }));
1701
+ });
1702
+
1703
+ router.get("/orders/:order_id", async function (req, res) {
1704
+ var orderId = req.params && req.params.order_id;
1705
+ if (!orderId) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1706
+ var o = await deps.order.get(orderId);
1707
+ if (!o) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1708
+ _send(res, 200, renderOrder({ order: o, shop_name: shopName, theme: theme }));
1709
+ });
1710
+ }
1711
+
1712
+ // ---- customer accounts (passkey-only) ------------------------------
1713
+ //
1714
+ // Mount only when deps.customers is supplied (operator opts in by
1715
+ // wiring the customers primitive in server.js). The account routes
1716
+ // also depend on b.vault for sealed-cookie envelopes — the seal /
1717
+ // unseal calls throw `vault/not-initialized` at request time when
1718
+ // the operator hasn't supplied VAULT_PASSPHRASE; routes surface
1719
+ // that as 503 so the rest of the storefront stays up.
1720
+ if (deps.customers) {
1721
+ var rpName = shopName;
1722
+ var rpId = deps.rpId || (deps.shop_origin ? new URL(deps.shop_origin).hostname : "localhost");
1723
+ var expectedOrigin = deps.shop_origin || ("https://" + rpId);
1724
+
1725
+ function _b64u(buf) {
1726
+ // Node Buffer / Uint8Array -> base64url string
1727
+ var b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
1728
+ return b.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1729
+ }
1730
+
1731
+ function _sealEnvelope(obj) {
1732
+ return _b().vault.seal(JSON.stringify(obj));
1733
+ }
1734
+ function _unsealEnvelope(s) {
1735
+ try {
1736
+ var raw = _b().vault.unseal(s);
1737
+ return JSON.parse(raw);
1738
+ } catch (_e) { return null; }
1739
+ }
1740
+
1741
+ function _currentCustomer(req) {
1742
+ var raw = _readCookie(req, AUTH_COOKIE_NAME);
1743
+ if (!raw) return null;
1744
+ var env = _unsealEnvelope(raw);
1745
+ if (!env || !env.customer_id || !env.exp) return null;
1746
+ if (env.exp < Date.now()) return null;
1747
+ return env;
1748
+ }
1749
+
1750
+ function _serviceUnavailable(res, msg) {
1751
+ res.status(503);
1752
+ return res.end ? res.end(msg) : res.send(msg);
1753
+ }
1754
+
1755
+ function _readJsonBody(req) {
1756
+ // b.middleware.bodyParser leaves JSON in req.body when the
1757
+ // request's content-type is application/json. Some test
1758
+ // harnesses POST a string body — fall back to JSON.parse.
1759
+ if (req.body && typeof req.body === "object") return req.body;
1760
+ if (typeof req.body === "string") {
1761
+ try { return JSON.parse(req.body); } catch (_e) { return {}; }
1762
+ }
1763
+ return {};
1764
+ }
1765
+
1766
+ router.get("/account/login", async function (req, res) {
1767
+ var cartCount = await _cartCountForReq(req);
1768
+ _send(res, 200, renderAccountLogin({ shop_name: shopName, cart_count: cartCount }));
1769
+ });
1770
+
1771
+ router.get("/account/register", async function (req, res) {
1772
+ var cartCount = await _cartCountForReq(req);
1773
+ _send(res, 200, renderAccountRegister({ shop_name: shopName, cart_count: cartCount }));
1774
+ });
1775
+
1776
+ router.post("/account/passkey/register-begin", async function (req, res) {
1777
+ try {
1778
+ var body = _readJsonBody(req);
1779
+ // Persist the customer row up-front. The address is the
1780
+ // registration's natural identifier — if enrollment fails
1781
+ // the customer can re-attempt with the same email; the
1782
+ // primitive's duplicate-refusal surfaces as a typed code.
1783
+ var existing = await deps.customers.byEmailHash(
1784
+ deps.customers.hashEmail(body.email),
1785
+ );
1786
+ var customer;
1787
+ if (existing) {
1788
+ customer = existing;
1789
+ } else {
1790
+ customer = await deps.customers.register({
1791
+ email: body.email,
1792
+ display_name: body.display_name,
1793
+ });
1794
+ }
1795
+ var startOpts = await _b().auth.passkey.startRegistration({
1796
+ rpName: rpName,
1797
+ rpId: rpId,
1798
+ userName: customer.email_hash.slice(0, 16),
1799
+ userDisplayName: customer.display_name,
1800
+ attestationType: "none",
1801
+ });
1802
+ // Seal the ceremony state (challenge + customer_id) into the
1803
+ // shop_auth_chal cookie so register-finish verifies against
1804
+ // the same challenge without server-side state.
1805
+ var sealed = _sealEnvelope({
1806
+ kind: "register",
1807
+ customer_id: customer.id,
1808
+ challenge: startOpts.challenge,
1809
+ created_at: Date.now(),
1810
+ });
1811
+ _setChallengeCookie(res, sealed);
1812
+ res.status(200);
1813
+ res.setHeader && res.setHeader("content-type", "application/json");
1814
+ return res.end ? res.end(JSON.stringify(startOpts)) : res.send(JSON.stringify(startOpts));
1815
+ } catch (e) {
1816
+ if (e && e.code === "vault/not-initialized") return _serviceUnavailable(res, "auth not configured");
1817
+ res.status(e instanceof TypeError ? 400 : 500);
1818
+ return res.end ? res.end((e && e.message) || "register-begin failed") : res.send((e && e.message) || "register-begin failed");
1819
+ }
1820
+ });
1821
+
1822
+ router.post("/account/passkey/register-finish", async function (req, res) {
1823
+ try {
1824
+ var rawCookie = _readCookie(req, CHALLENGE_COOKIE_NAME);
1825
+ if (!rawCookie) { res.status(400); return res.end ? res.end("missing challenge") : res.send("missing challenge"); }
1826
+ var env = _unsealEnvelope(rawCookie);
1827
+ if (!env || env.kind !== "register") {
1828
+ res.status(400); return res.end ? res.end("bad challenge") : res.send("bad challenge");
1829
+ }
1830
+ var att = _readJsonBody(req);
1831
+ var rv = await _b().auth.passkey.verifyRegistration({
1832
+ response: att,
1833
+ expectedChallenge: env.challenge,
1834
+ expectedOrigin: expectedOrigin,
1835
+ expectedRPID: rpId,
1836
+ });
1837
+ if (!rv || !rv.verified) {
1838
+ res.status(400); return res.end ? res.end("attestation refused") : res.send("attestation refused");
1839
+ }
1840
+ var info = rv.registrationInfo || {};
1841
+ var credentialId = info.credentialID || att.rawId || att.id;
1842
+ var publicKey = info.credentialPublicKey;
1843
+ if (credentialId && typeof credentialId !== "string") credentialId = _b64u(credentialId);
1844
+ if (publicKey && typeof publicKey !== "string") publicKey = _b64u(publicKey);
1845
+ var transports = "";
1846
+ if (att.response && Array.isArray(att.response.transports)) {
1847
+ transports = att.response.transports.filter(function (t) { return /^[a-z]+$/.test(t); }).join(",");
1848
+ }
1849
+ await deps.customers.addPasskey(env.customer_id, {
1850
+ credential_id: credentialId,
1851
+ public_key: publicKey,
1852
+ counter: info.counter || 0,
1853
+ transports: transports,
1854
+ });
1855
+ _clearChallengeCookie(res);
1856
+ _setAuthCookie(res, _sealEnvelope({
1857
+ customer_id: env.customer_id,
1858
+ exp: Date.now() + AUTH_TTL_MS,
1859
+ }));
1860
+ res.status(200);
1861
+ return res.end ? res.end("ok") : res.send("ok");
1862
+ } catch (e) {
1863
+ if (e && e.code === "vault/not-initialized") return _serviceUnavailable(res, "auth not configured");
1864
+ res.status(e instanceof TypeError ? 400 : 500);
1865
+ return res.end ? res.end((e && e.message) || "register-finish failed") : res.send((e && e.message) || "register-finish failed");
1866
+ }
1867
+ });
1868
+
1869
+ router.post("/account/passkey/login-begin", async function (req, res) {
1870
+ try {
1871
+ var body = _readJsonBody(req);
1872
+ var hash = deps.customers.hashEmail(body.email);
1873
+ var customer = await deps.customers.byEmailHash(hash);
1874
+ // Even when the customer doesn't exist, return a valid-shaped
1875
+ // challenge with an empty allowCredentials list — the client
1876
+ // can't distinguish "no such address" from "wrong passkey",
1877
+ // protecting against email-enumeration.
1878
+ var allow = [];
1879
+ if (customer) {
1880
+ var pks = await deps.customers.listPasskeys(customer.id);
1881
+ allow = pks.map(function (p) {
1882
+ return {
1883
+ id: p.credential_id,
1884
+ type: "public-key",
1885
+ transports: p.transports ? p.transports.split(",") : undefined,
1886
+ };
1887
+ });
1888
+ }
1889
+ var startOpts = await _b().auth.passkey.startAuthentication({
1890
+ rpId: rpId,
1891
+ allowCredentials: allow,
1892
+ userVerification: "preferred",
1893
+ });
1894
+ var sealed = _sealEnvelope({
1895
+ kind: "login",
1896
+ email_hash: hash,
1897
+ challenge: startOpts.challenge,
1898
+ created_at: Date.now(),
1899
+ });
1900
+ _setChallengeCookie(res, sealed);
1901
+ res.status(200);
1902
+ res.setHeader && res.setHeader("content-type", "application/json");
1903
+ return res.end ? res.end(JSON.stringify(startOpts)) : res.send(JSON.stringify(startOpts));
1904
+ } catch (e) {
1905
+ if (e && e.code === "vault/not-initialized") return _serviceUnavailable(res, "auth not configured");
1906
+ res.status(e instanceof TypeError ? 400 : 500);
1907
+ return res.end ? res.end((e && e.message) || "login-begin failed") : res.send((e && e.message) || "login-begin failed");
1908
+ }
1909
+ });
1910
+
1911
+ router.post("/account/passkey/login-finish", async function (req, res) {
1912
+ try {
1913
+ var rawCookie = _readCookie(req, CHALLENGE_COOKIE_NAME);
1914
+ if (!rawCookie) { res.status(400); return res.end ? res.end("missing challenge") : res.send("missing challenge"); }
1915
+ var env = _unsealEnvelope(rawCookie);
1916
+ if (!env || env.kind !== "login") { res.status(400); return res.end ? res.end("bad challenge") : res.send("bad challenge"); }
1917
+ var assertion = _readJsonBody(req);
1918
+ var credentialId = assertion.id || assertion.rawId;
1919
+ if (!credentialId) { res.status(400); return res.end ? res.end("missing credential id") : res.send("missing credential id"); }
1920
+ var passkey = await deps.customers.getPasskeyByCredentialId(credentialId);
1921
+ if (!passkey) { res.status(401); return res.end ? res.end("unknown credential") : res.send("unknown credential"); }
1922
+ var customer = await deps.customers.get(passkey.customer_id);
1923
+ if (!customer) { res.status(401); return res.end ? res.end("unknown customer") : res.send("unknown customer"); }
1924
+ if (customer.email_hash !== env.email_hash) {
1925
+ // The login-begin email and the credential's customer
1926
+ // must agree — refuse cross-account credential reuse.
1927
+ res.status(401); return res.end ? res.end("credential / account mismatch") : res.send("credential / account mismatch");
1928
+ }
1929
+ var rv = await _b().auth.passkey.verifyAuthentication({
1930
+ response: assertion,
1931
+ expectedChallenge: env.challenge,
1932
+ expectedOrigin: expectedOrigin,
1933
+ expectedRPID: rpId,
1934
+ authenticator: {
1935
+ credentialID: passkey.credential_id,
1936
+ credentialPublicKey: passkey.public_key,
1937
+ counter: passkey.counter,
1938
+ },
1939
+ });
1940
+ if (!rv || !rv.verified) {
1941
+ res.status(401); return res.end ? res.end("assertion refused") : res.send("assertion refused");
1942
+ }
1943
+ // Persist the new counter value (clone-detection).
1944
+ var newCounter = (rv.authenticationInfo && rv.authenticationInfo.newCounter) || 0;
1945
+ try {
1946
+ await deps.customers.updatePasskeyCounter(passkey.id, newCounter);
1947
+ } catch (e) {
1948
+ if (e && e.code === "PASSKEY_COUNTER_REGRESSION") {
1949
+ res.status(401); return res.end ? res.end("counter regression — possible clone") : res.send("counter regression — possible clone");
1950
+ }
1951
+ throw e;
1952
+ }
1953
+ // Merge the anonymous cart into a customer-owned cart so
1954
+ // the shopper doesn't lose items on sign-in.
1955
+ var sid = _readCookie(req, SESSION_COOKIE_NAME);
1956
+ if (sid) {
1957
+ try {
1958
+ var anonCart = await deps.cart.bySession(sid);
1959
+ if (anonCart) await deps.cart.setCustomer(anonCart.id, customer.id);
1960
+ } catch (_e) { /* best-effort merge; sign-in itself succeeds */ }
1961
+ }
1962
+ _clearChallengeCookie(res);
1963
+ _setAuthCookie(res, _sealEnvelope({
1964
+ customer_id: customer.id,
1965
+ exp: Date.now() + AUTH_TTL_MS,
1966
+ }));
1967
+ res.status(200);
1968
+ return res.end ? res.end("ok") : res.send("ok");
1969
+ } catch (e) {
1970
+ if (e && e.code === "vault/not-initialized") return _serviceUnavailable(res, "auth not configured");
1971
+ res.status(e instanceof TypeError ? 400 : 500);
1972
+ return res.end ? res.end((e && e.message) || "login-finish failed") : res.send((e && e.message) || "login-finish failed");
1973
+ }
1974
+ });
1975
+
1976
+ router.get("/account", async function (req, res) {
1977
+ var auth;
1978
+ try { auth = _currentCustomer(req); }
1979
+ catch (e) {
1980
+ if (e && e.code === "vault/not-initialized") return _serviceUnavailable(res, "auth not configured");
1981
+ throw e;
1982
+ }
1983
+ if (!auth) {
1984
+ res.status(303); res.setHeader && res.setHeader("location", "/account/login");
1985
+ return res.end ? res.end() : res.send("");
1986
+ }
1987
+ var customer = await deps.customers.get(auth.customer_id);
1988
+ if (!customer) {
1989
+ _clearAuthCookie(res);
1990
+ res.status(303); res.setHeader && res.setHeader("location", "/account/login");
1991
+ return res.end ? res.end() : res.send("");
1992
+ }
1993
+ var orders = [];
1994
+ if (deps.order) {
1995
+ var page = await deps.order.listForCustomer(customer.id, { limit: 10 });
1996
+ orders = page.rows;
1997
+ }
1998
+ var cartCount = await _cartCountForReq(req);
1999
+ _send(res, 200, renderAccount({
2000
+ customer: customer, orders: orders, shop_name: shopName, cart_count: cartCount,
2001
+ }));
2002
+ });
2003
+
2004
+ router.post("/account/logout", function (_req, res) {
2005
+ _clearAuthCookie(res);
2006
+ res.status(303); res.setHeader && res.setHeader("location", "/");
2007
+ return res.end ? res.end() : res.send("");
2008
+ });
2009
+ }
2010
+
2011
+ // POST /cart/lines — add a line. Reads variant_id + qty from the
2012
+ // form body (b.middleware.bodyParser parses it into req.body).
2013
+ // CSRF token validation is the responsibility of the csrfProtect
2014
+ // middleware mounted at the app level (server.js). Redirects to
2015
+ // /cart on success so a refresh doesn't re-submit the form.
2016
+ router.post("/cart/lines", async function (req, res) {
2017
+ var body = req.body || {};
2018
+ var variantId = body.variant_id;
2019
+ var qtyRaw = body.qty;
2020
+ var qty = parseInt(qtyRaw, 10);
2021
+ if (!variantId || !Number.isFinite(qty) || qty < 1 || qty > 99) {
2022
+ res.status(400);
2023
+ return res.end ? res.end("Invalid request") : res.send("Invalid request");
2024
+ }
2025
+ var resolved = await _getOrCreateCart(req, res, "USD");
2026
+ try {
2027
+ await deps.cart.addLine(resolved.cart.id, { variant_id: variantId, qty: qty });
2028
+ } catch (e) {
2029
+ res.status(e instanceof TypeError ? 400 : 500);
2030
+ return res.end ? res.end((e && e.message) || "Error") : res.send((e && e.message) || "Error");
2031
+ }
2032
+ res.status(303);
2033
+ res.setHeader && res.setHeader("location", "/cart");
2034
+ res.end ? res.end() : res.send("");
2035
+ });
2036
+
2037
+ // POST /cart/lines/:line_id/update — change qty on an existing
2038
+ // line. Form value `qty` is the new quantity (1..99). HTML forms
2039
+ // only support GET/POST so the verb is in the path.
2040
+ router.post("/cart/lines/:line_id/update", async function (req, res) {
2041
+ var lineId = req.params && req.params.line_id;
2042
+ var qty = parseInt((req.body || {}).qty, 10);
2043
+ if (!lineId || !Number.isFinite(qty) || qty < 1 || qty > 99) {
2044
+ res.status(400);
2045
+ return res.end ? res.end("Invalid request") : res.send("Invalid request");
2046
+ }
2047
+ try {
2048
+ var updated = await deps.cart.updateLine(lineId, { qty: qty });
2049
+ if (!updated) {
2050
+ res.status(404);
2051
+ return res.end ? res.end("Line not found") : res.send("Line not found");
2052
+ }
2053
+ } catch (e) {
2054
+ res.status(e instanceof TypeError ? 400 : 500);
2055
+ return res.end ? res.end((e && e.message) || "Error") : res.send((e && e.message) || "Error");
2056
+ }
2057
+ res.status(303);
2058
+ res.setHeader && res.setHeader("location", "/cart");
2059
+ res.end ? res.end() : res.send("");
2060
+ });
2061
+
2062
+ // POST /cart/lines/:line_id/remove — delete the line outright.
2063
+ router.post("/cart/lines/:line_id/remove", async function (req, res) {
2064
+ var lineId = req.params && req.params.line_id;
2065
+ if (!lineId) {
2066
+ res.status(400);
2067
+ return res.end ? res.end("Invalid request") : res.send("Invalid request");
2068
+ }
2069
+ try {
2070
+ await deps.cart.removeLine(lineId);
2071
+ } catch (e) {
2072
+ res.status(e instanceof TypeError ? 400 : 500);
2073
+ return res.end ? res.end((e && e.message) || "Error") : res.send((e && e.message) || "Error");
2074
+ }
2075
+ res.status(303);
2076
+ res.setHeader && res.setHeader("location", "/cart");
2077
+ res.end ? res.end() : res.send("");
2078
+ });
2079
+
2080
+ // Newsletter signup — POST /newsletter from the footer band.
2081
+ // Validates the address through `b.guardEmail`, idempotently
2082
+ // enrolls via the newsletter primitive (when wired), and renders
2083
+ // a designed thank-you page. Mount only when `deps.newsletter`
2084
+ // is present so operators that haven't wired the primitive get
2085
+ // a clean 404 instead of a misleading "thanks" response.
2086
+ if (deps.newsletter) {
2087
+ router.post("/newsletter", async function (req, res) {
2088
+ var body = req.body || {};
2089
+ var cartCount = 0;
2090
+ try { cartCount = await _cartCountForReq(req); } catch (_e) { /* drop-silent — empty cart fallback */ }
2091
+ try {
2092
+ var result = await deps.newsletter.signup({
2093
+ email: body.email,
2094
+ source: "storefront-footer",
2095
+ });
2096
+ return _send(res, 200, renderNewsletterThanks({
2097
+ shop_name: shopName,
2098
+ cart_count: cartCount,
2099
+ status: result.status,
2100
+ }));
2101
+ } catch (e) {
2102
+ // TypeError == operator-fault validation refusal; everything
2103
+ // else (D1 unreachable, vault hiccup) hits the 500 branch.
2104
+ var isInputError = e instanceof TypeError;
2105
+ return _send(res, isInputError ? 400 : 500, renderNewsletterError({
2106
+ shop_name: shopName,
2107
+ cart_count: cartCount,
2108
+ message: isInputError ? "That doesn't look like a valid email address. Check the format and try again." : null,
2109
+ }));
2110
+ }
2111
+ });
2112
+ }
2113
+
2114
+ // Designed admin landing — the rest of /admin/* is JSON. This
2115
+ // single GET gives footer links + curious visitors a designed
2116
+ // page explaining the API-only posture instead of a 404.
2117
+ router.get("/admin", async function (req, res) {
2118
+ var cartCount = 0;
2119
+ try { cartCount = await _cartCountForReq(req); } catch (_e) { /* drop-silent — empty cart fallback */ }
2120
+ _send(res, 200, renderAdminLanding({
2121
+ shop_name: shopName,
2122
+ cart_count: cartCount,
2123
+ }));
2124
+ });
2125
+
2126
+ // Catch-all 404 — every unmatched route lands on the designed
2127
+ // not-found page (gradient 404 + back-to-shop CTA) inside the
2128
+ // standard layout, instead of the framework's default
2129
+ // `<h1>404 Not Found</h1>` text body. Wired via the router's
2130
+ // onNotFound hook so it covers GET/POST/HEAD uniformly.
2131
+ if (typeof router.onNotFound === "function") {
2132
+ router.onNotFound(async function (req, res) {
2133
+ var cartCount = 0;
2134
+ try { cartCount = await _cartCountForReq(req); } catch (_e) { /* drop-silent — empty cart fallback */ }
2135
+ _send(res, 404, renderNotFound({
2136
+ shop_name: shopName,
2137
+ cart_count: cartCount,
2138
+ theme: theme,
2139
+ }));
2140
+ });
2141
+ }
2142
+ }
2143
+
2144
+ module.exports = {
2145
+ mount: mount,
2146
+ renderHome: renderHome,
2147
+ renderSearch: renderSearch,
2148
+ renderProduct: renderProduct,
2149
+ renderCart: renderCart,
2150
+ renderCheckoutForm: renderCheckoutForm,
2151
+ renderPayPage: renderPayPage,
2152
+ renderOrder: renderOrder,
2153
+ renderAccountLogin: renderAccountLogin,
2154
+ renderAccountRegister: renderAccountRegister,
2155
+ renderAccount: renderAccount,
2156
+ renderNotFound: renderNotFound,
2157
+ // Layout exposed so operators forking the framework can override.
2158
+ _wrap: _wrap,
2159
+ LAYOUT: LAYOUT,
2160
+ };