@blamejs/blamejs-shop 0.0.45 → 0.0.46

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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.0.x
10
10
 
11
+ - v0.0.46 (2026-05-22) — **Order confirmation page — line items render thumbnails + product titles (same pattern as the cart).** The post-checkout `/orders/:order_id` page used the read-only `CART_LINE` template that emitted just SKU + qty + unit + total. The order confirmation now reuses the same product-cell treatment v0.0.45 shipped on the live cart — small thumbnail (from `catalog.media.listForProduct`) + product title + SKU chip in a slug-linked anchor — so the page a customer lands on after a successful purchase shows what they bought visually, not just a column of opaque SKU codes. **Changed:** *`CART_LINE` (read-only, used by the order page) — same product cell as the editable cart line* — Markup mirrors `CART_LINE_EDITABLE` minus the qty / update / remove forms. First cell is `<a class="cart-line__product-link" href="/products/<slug>">` wrapping the thumbnail + title + SKU chip; remaining cells (qty / unit / total) are unchanged. Reuses the existing `.cart-line__thumb` + `.cart-line__product-*` CSS — no new theme rules. · *`storefront.renderOrder({ ..., product_lookup })` accepts the variant→product map* — Mirrors `renderCart`'s contract. The route handler bundles a `{ variant_id: { product, hero_media } }` cache built per-unique-variant; lines whose variant is missing fall through to SKU-as-title + the placeholder tile. The order page table header is now 'Product' instead of 'SKU'. · *`GET /orders/:order_id` walks the order's frozen lines once* — After loading the order, the route walks `o.lines` and for each unique `variant_id` pulls `catalog.variants.get` → `catalog.products.get` + `catalog.media.listForProduct`. The cache prevents a multi-quantity-same-variant order from hitting the catalog more than once per variant. The lookup is passed to `renderOrder` so the line rendering matches the cart's pattern byte-for-byte.
12
+
11
13
  - v0.0.45 (2026-05-22) — **Cart line items — thumbnail + product title + slug-linked anchor alongside the SKU chip.** Cart rows previously rendered only the SKU code, qty, unit, and total — the visitor had to remember what they actually added. Each line now leads with a 3.5rem rounded thumbnail (from the product's first `catalog.media` row), the product title in a bold inline label, and the SKU as a small monospace chip below. The whole product cell is an anchor back to the PDP so a visitor can re-enter the product page directly from the cart. Lines whose variant is missing media render the placeholder tile (dashed-border + the diagonal-stripe pattern the catalog empty-state uses). **Changed:** *`CART_LINE_EDITABLE` template — product cell replaces SKU-only cell* — First cell is now `<a class="cart-line__product-link" href="/products/<slug>">` wrapping a thumbnail + a `<span class="cart-line__product-meta">` that stacks the product title (bold) and SKU chip (monospace, on a `--bg-2` background). Hover on the link tints both the title and the link affordance to the accent color. The qty / unit / total / action cells are unchanged. · *`storefront.renderCart({ ..., product_lookup })` accepts a variant→product lookup* — Routes pass a `{ variant_id: { product, hero_media } }` map; lines render with the matching product's title + slug-linked URL + hero-media thumbnail. Lines without a lookup match (variant deleted, media not attached yet) fall through to a SKU-as-title display with the placeholder tile. The cart-table column header is now 'Product' instead of 'SKU'. · *`GET /cart` builds the product lookup* — After listing the cart's lines, the route walks each unique `variant_id` (cached by id so a cart with the same variant twice only hits the catalog once), pulls `catalog.variants.get` → `catalog.products.get` + `catalog.media.listForProduct`, and bundles the result for the renderer.
12
14
 
13
15
  - v0.0.44 (2026-05-22) — **Package rename — `@blamejs/blamejs-shop` (scoped under the org alongside `@blamejs/core` + `@blamejs/exceptd-skills`).** The npm package is now published under the `blamejs` organization scope as `@blamejs/blamejs-shop`. Installation switches from `npm install blamejs-shop` to `npm install @blamejs/blamejs-shop`; the import surface is unchanged — `require("@blamejs/blamejs-shop")` returns the same shop primitive set the unscoped name did. The legacy unscoped package will be deprecated on the registry with a pointer to the new name so existing operators see the upgrade path on their next install. **Changed:** *`package.json#name` → `@blamejs/blamejs-shop`* — Matches the existing org-scoped packages (`@blamejs/core`, `@blamejs/exceptd-skills`). The publish workflow already passes `--access public` to `npm publish`, which is the access mode scoped packages need to land as publicly-installable. · *Install command* — `npm install @blamejs/blamejs-shop` replaces `npm install blamejs-shop`. The `require` path follows: `var bShop = require("@blamejs/blamejs-shop");`. The entry point + export shape are byte-for-byte identical to v0.0.43 — only the resolution path changes. **Migration:** *Operator upgrade path* — Operators on the unscoped `blamejs-shop` change one line in their `package.json` dependencies: `"blamejs-shop": "^0.0.43"` → `"@blamejs/blamejs-shop": "^0.0.44"`. Every `require("blamejs-shop")` call site needs the matching update (`require("@blamejs/blamejs-shop")`). The unscoped name will be deprecated on the registry with a pointer to the new scoped path so a fresh `npm install` on the old name surfaces the migration message in npm's stderr.
package/lib/storefront.js CHANGED
@@ -703,8 +703,26 @@ function renderProduct(opts) {
703
703
 
704
704
  // ---- cart --------------------------------------------------------------
705
705
 
706
+ // Read-only line — shown on the order confirmation page. Same
707
+ // thumbnail + title + SKU chip pattern as CART_LINE_EDITABLE,
708
+ // minus the qty/remove forms. The product cell is still a
709
+ // slug-linked anchor so the customer can re-open the PDP from the
710
+ // order page.
706
711
  var CART_LINE =
707
- "<tr><td>{{sku}}</td><td>{{qty}}</td><td class=\"price\">{{unit}}</td><td class=\"price\">{{total}}</td></tr>\n";
712
+ "<tr>\n" +
713
+ " <td class=\"cart-line__product\">\n" +
714
+ " <a class=\"cart-line__product-link\" href=\"{{product_url}}\">\n" +
715
+ " RAW_ORDER_LINE_THUMB\n" +
716
+ " <span class=\"cart-line__product-meta\">\n" +
717
+ " <span class=\"cart-line__product-title\">{{product_title}}</span>\n" +
718
+ " <code class=\"cart-line__sku-chip\">{{sku}}</code>\n" +
719
+ " </span>\n" +
720
+ " </a>\n" +
721
+ " </td>\n" +
722
+ " <td>{{qty}}</td>\n" +
723
+ " <td class=\"price\">{{unit}}</td>\n" +
724
+ " <td class=\"price\">{{total}}</td>\n" +
725
+ "</tr>\n";
708
726
 
709
727
  // Editable cart line — shown on the /cart page. Includes an inline
710
728
  // qty form (POST /cart/lines/:id/update) and a remove form (POST
@@ -882,7 +900,7 @@ var ORDER_PAGE =
882
900
  " <h2 class=\"pdp__variants-title\">Items</h2>\n" +
883
901
  " <div class=\"table-scroll\">\n" +
884
902
  " <table>\n" +
885
- " <thead><tr><th>SKU</th><th>Qty</th><th>Unit</th><th>Total</th></tr></thead>\n" +
903
+ " <thead><tr><th>Product</th><th>Qty</th><th>Unit</th><th>Total</th></tr></thead>\n" +
886
904
  " <tbody>{{line_rows}}</tbody>\n" +
887
905
  " </table>\n" +
888
906
  " </div>\n" +
@@ -905,12 +923,26 @@ function renderOrder(opts) {
905
923
  var lines = o.lines || [];
906
924
  var shopName = opts.shop_name || "blamejs.shop";
907
925
  var cartCount = opts.cart_count == null ? 0 : opts.cart_count;
926
+ var assetPrefix = opts.asset_prefix || "/assets/";
927
+ // Same lookup shape as renderCart — { variant_id: { product, hero_media } }.
928
+ // Route handler bundles it in; missing entries fall through to
929
+ // SKU-as-title with the placeholder tile.
930
+ var lookup = opts.product_lookup || {};
908
931
  var rendered = lines.map(function (l) {
932
+ var match = lookup[l.variant_id] || null;
933
+ var prod = match && match.product;
934
+ var hero = match && match.hero_media;
935
+ var imageUrl = hero ? assetPrefix + hero.r2_key : null;
936
+ var imageAlt = hero ? (hero.alt_text || (prod && prod.title) || l.sku) : null;
909
937
  return {
910
- sku: l.sku,
911
- qty: String(l.qty),
912
- unit: pricing.format(l.unit_amount_minor, l.unit_currency),
913
- total: pricing.format(l.line_total_minor || (l.qty * l.unit_amount_minor), l.unit_currency),
938
+ sku: l.sku,
939
+ qty: String(l.qty),
940
+ unit: pricing.format(l.unit_amount_minor, l.unit_currency),
941
+ total: pricing.format(l.line_total_minor || (l.qty * l.unit_amount_minor), l.unit_currency),
942
+ product_title: (prod && prod.title) || l.sku,
943
+ product_url: prod ? ("/products/" + prod.slug) : "#",
944
+ image_url: imageUrl,
945
+ image_alt: imageAlt,
914
946
  };
915
947
  });
916
948
  var subtotal = pricing.format(o.subtotal_minor, o.currency);
@@ -933,8 +965,22 @@ function renderOrder(opts) {
933
965
  asset_css_main: opts.theme.assetUrl("css/main.css"),
934
966
  });
935
967
  }
968
+ function _orderEsc(s) {
969
+ return String(s == null ? "" : s)
970
+ .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
971
+ }
936
972
  var rows = rendered.map(function (l) {
937
- return _render(CART_LINE, { sku: l.sku, qty: l.qty, unit: l.unit, total: l.total });
973
+ var thumb = l.image_url
974
+ ? "<span class=\"cart-line__thumb\"><img src=\"" + _orderEsc(l.image_url) + "\" alt=\"" + _orderEsc(l.image_alt) + "\" loading=\"lazy\"></span>"
975
+ : "<span class=\"cart-line__thumb cart-line__thumb--empty\" aria-hidden=\"true\"></span>";
976
+ return _render(CART_LINE, {
977
+ sku: l.sku,
978
+ qty: l.qty,
979
+ unit: l.unit,
980
+ total: l.total,
981
+ product_title: l.product_title,
982
+ product_url: l.product_url,
983
+ }).replace("RAW_ORDER_LINE_THUMB", thumb);
938
984
  }).join("");
939
985
  if (!rows) rows = "<tr><td colspan=\"4\" class=\"empty\">No items.</td></tr>";
940
986
  var body = _render(ORDER_PAGE, {
@@ -1768,7 +1814,28 @@ function mount(router, deps) {
1768
1814
  if (!orderId) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1769
1815
  var o = await deps.order.get(orderId);
1770
1816
  if (!o) return _send(res, 404, renderNotFound({ shop_name: shopName, theme: theme }));
1771
- _send(res, 200, renderOrder({ order: o, shop_name: shopName, theme: theme }));
1817
+ // Same variant_id {product, hero_media} lookup pattern as the
1818
+ // cart route, applied to the order's frozen line items so the
1819
+ // post-checkout page shows what the customer bought visually.
1820
+ var productLookup = {};
1821
+ for (var i = 0; i < (o.lines || []).length; i += 1) {
1822
+ var vId = o.lines[i].variant_id;
1823
+ if (productLookup[vId]) continue;
1824
+ var v = await deps.catalog.variants.get(vId);
1825
+ if (!v) { productLookup[vId] = null; continue; }
1826
+ var prod = await deps.catalog.products.get(v.product_id);
1827
+ var media = await deps.catalog.media.listForProduct(v.product_id);
1828
+ productLookup[vId] = {
1829
+ product: prod,
1830
+ hero_media: media.length ? media[0] : null,
1831
+ };
1832
+ }
1833
+ _send(res, 200, renderOrder({
1834
+ order: o,
1835
+ product_lookup: productLookup,
1836
+ shop_name: shopName,
1837
+ theme: theme,
1838
+ }));
1772
1839
  });
1773
1840
  }
1774
1841
 
@@ -3,8 +3,8 @@
3
3
  "_about": "blamejs.shop vendors a single framework — blamejs — which itself bundles every server-side crypto/identity dependency. The transitive packages blamejs ships are surfaced in its own MANIFEST.json at lib/vendor/blamejs/lib/vendor/MANIFEST.json — Trivy / Grype rely on that nested data for CVE attribution.",
4
4
  "packages": {
5
5
  "blamejs": {
6
- "version": "0.12.2",
7
- "tag": "v0.12.2",
6
+ "version": "0.12.3",
7
+ "tag": "v0.12.3",
8
8
  "license": "Apache-2.0",
9
9
  "author": "blamejs contributors",
10
10
  "source": "https://github.com/blamejs/blamejs",
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.12.x
10
10
 
11
+ - v0.12.3 (2026-05-22) — **README "What ships in the box" backfill — mail-stack listeners + JSCalendar + new postures.** The README's "Communication" + "Compliance regimes" bullets lagged behind the v0.11.24-v0.12.1 ship cadence. Backfilled: `b.mail.send.deliver` (turnkey outbound delivery chain), the four mail-server listeners (mx / submission / imap / jmap), the JMAP EmailSubmission/set reference handler, mail-crypto (CMS + PGP+WKD), the mail-stack agent, `b.calendar` (RFC 8984 JSCalendar substrate with full BY*+BYSETPOS+multi-rule expansion), and the 16 newly-promoted postures from v0.12.1 (`42-cfr-part-2` / `hti-1` / `uscdi-v4` / `irs-1075` / `nist-800-172-r3` / `tlp-2.0` / `soci-au` / `ffiec-cat-2` / `cri-profile-v2.0` / `m-22-09` / `m-22-18` / `nist-800-53-r5-privacy` / `nist-ai-600-1-genai` / `nist-csf-2.0` / `sb-53` / `nyc-ll144-2024`). **Changed:** *Communication section names every mail-stack listener + delivery chain + crypto primitive* — New entries: `b.mail.send.deliver` (MX → MTA-STS → DANE → REQUIRETLS → SMTP → DSN chain), four `b.mail.server.*` listeners, JMAP EmailSubmission reference handler, `b.mail.crypto.cms` + `b.mail.crypto.pgp`, `b.mail.agent` + `b.mailStore`, and `b.calendar` (JSCalendar / iCalendar bridge for JMAP Calendars interop). · *Compliance regimes section lists the 16 v0.12.1 backfilled postures* — New rows organise the additions under three sub-bullets: AI governance adds `nyc-ll144-2024` / `sb-53` / `nist-ai-rmf-1.0` / `nist-ai-600-1-genai` alongside the existing AI-act / NYC-LL144 / Colorado / Illinois entries; a new "Federal / sectoral" row covers `42-cfr-part-2` / `hti-1` / `uscdi-v4` / `irs-1075` / `nist-csf-2.0` / `nist-800-53-r5-privacy` / `nist-800-172-r3` / `m-22-09` / `m-22-18` / `ffiec-cat-2` / `cri-profile-v2.0`; a new "Critical infrastructure / info-sharing" row covers `soci-au` / `tlp-2.0`.
12
+
11
13
  - v0.12.2 (2026-05-22) — **Release-process docs point at `scripts/release.js` (the orchestrator shipped in v0.12.0).** `CONTRIBUTING.md` (maintainer section) and `examples/wiki/DEPLOY.md` ("Tag-driven releases") described the old multi-step manual release flow — version bump → commit → push → tag → push tag — without mentioning the v0.12.0 orchestrator. Both docs now point at `node scripts/release.js` as the canonical release mechanism, list the eight idempotent subcommands, and call out the two pre-requisites the script enforces (release-notes JSON + signed-commit config). **Added:** *`scripts/release.js regen` — re-run artifact regeneration mid-flow* — Edits to `release-notes/v<next>.json` after `prepare` (e.g. addressing a Codex finding, fixing a leak-vocabulary refusal) previously required running `node scripts/generate-changelog-entry.js --rebuild` + `scripts/refresh-api-snapshot.js` + `scripts/check-api-snapshot.js` + `scripts/check-changelog-extract.js` manually. The new `regen` subcommand wraps all four into a single idempotent step. Safe to run any time from any branch. The `prepare` phase calls the same shared helper internally so behaviour stays consistent. **Changed:** *`CONTRIBUTING.md` maintainer section names the orchestrator* — The release-process bullet now reads `node scripts/release.js — eight idempotent subcommands (prepare → smoke → commit → push → watch → merge → tag → publish) plus all for a one-shot`. The existing DEPLOY.md link stays as a pointer for the wiki-container side of the same flow. · *`examples/wiki/DEPLOY.md` Tag-driven releases section rewritten* — Replaces the four-bullet manual flow with the orchestrator surface, including the `all` / `all --minor` one-shot, the per-phase subcommands, and the pre-requisites the script enforces (release-notes JSON present, SSH signing config in place). The downstream wiki-image deploy step on the host (pin `docker-compose.prod.yml` + `docker compose pull && up -d`) is unchanged. **Fixed:** *`scripts/release.js` signature verification uses `git verify-commit` as the canonical truth* — The v0.12.0 orchestrator's commit-signature gate parsed `git log -1 --pretty=%h %G? %GS` looking for `G` in the second column. On some platforms the `%G?` format token's `?` character can be eaten by argument resolution, returning empty stdout even when the signature is Good. The fix runs `git verify-commit HEAD` (whose exit code is the canonical signal `required_signatures` GH ruleset enforces) as the primary check; the `%G?` parse stays as a human-readable confirmation but no longer gates the script. Surfaced via dogfooding the orchestrator on this very release. · *`scripts/release.js` Docker bind-mount path handles Windows host paths with spaces* — The `push` phase's gitleaks step bind-mounted the repo root via `-v <path>:/repo`. The previous path transform produced `/C:/Users/...` on Windows, which Docker's `-v src:dst[:mode]` parser interpreted as having three colon-separated fields. Fix: transform `C:\Users\...` to `//c/Users/...` (lowercased drive letter, double-slash prefix — matches Git Bash's `$(pwd)` form Docker Desktop accepts). POSIX hosts pass through unchanged. Operators with Windows paths containing spaces, parentheses, or special characters can now run `node scripts/release.js push` without manual mount fiddling.
12
14
 
13
15
  - v0.12.1 (2026-05-22) — **`b.compliance` posture catalog coverage — 65 missing entries backfilled + drift detector.** Two posture-catalog drifts surfaced during audit: 16 postures had `POSTURE_DEFAULTS` configuration wired but weren't in `KNOWN_POSTURES`, so `b.compliance.set("42-cfr-part-2")` (and 15 others) refused with `bad-posture` despite the cascade defaults existing in the codebase. Separately, 49 `KNOWN_POSTURES` entries had no `REGIME_MAP` record, so `b.compliance.describe(posture)` returned null and admin UI / generated audit reports rendering `"running under <name> (<citation>)"` got empty strings. All 65 entries are now backfilled. New codebase-patterns detector enforces `KNOWN_POSTURES ⊇ POSTURE_DEFAULTS` and `REGIME_MAP ⊇ KNOWN_POSTURES` so the same drift class can't reappear. **Added:** *Sixteen postures promoted into `KNOWN_POSTURES`* — `42-cfr-part-2` (Confidentiality of Substance Use Disorder Patient Records), `hti-1` (ONC HTI-1 health-IT certification), `uscdi-v4` (US Core Data for Interoperability), `irs-1075` (Tax Information Security Guidelines), `nist-800-172-r3` (Enhanced CUI Security), `tlp-2.0` (FIRST Traffic Light Protocol), `soci-au` (Australia SOCI Act), `ffiec-cat-2` (FFIEC Cybersecurity Assessment Tool 2.0), `cri-profile-v2.0` (Cyber Risk Institute Profile), `m-22-09` (OMB Zero Trust Strategy), `m-22-18` (OMB Supply Chain SSDF Attestation), `nist-800-53-r5-privacy` (NIST 800-53 Privacy overlay), `nist-ai-600-1-genai` (NIST GenAI Profile), `nist-csf-2.0` (Cybersecurity Framework 2.0), `sb-53` (California Transparency in Frontier AI Act), `nyc-ll144-2024` (NYC AEDT bias audits). Operators pinning these via `b.compliance.set()` now work end-to-end. · *Forty-nine `REGIME_MAP` records backfilled* — Every `KNOWN_POSTURES` entry now has a `{ name, citation, jurisdiction, domain }` record. Spans US state privacy (vcdpa / co-cpa / ctdpa / ucpa / tdpsa / or-cpa / mt-cdpa / ia-icdpa / in-indpa / de-dpdpa / modpa / wmhmda / bipa / ccpa / nydfs-500), EU regulation (dora / nis2 / cra / ai-act / dsa), international (lgpd-br / pipl-cn / appi-jp / pdpa-sg / pipeda-ca / uk-gdpr / irap / bsi-c5 / ens-es), cybersecurity frameworks (nist-800-53 / nist-csf-2.0 / cis-controls-v8 / cwe-top-25-2024), AI (nist-ai-rmf-1.0 / iso-42001-2023 / iso-23894-2023 / owasp-llm-top-10-2025), supply-chain (slsa-v1.0-build-l3 / cyclonedx-v1.6 / spdx-v3.0 / vex-csaf-2.1 / nist-800-218-ssdf), CMMC levels, and sectoral standards (hipaa-security-rule / hitrust-csf-v11.4 / nerc-cip-007-6 / psd2-rts-sca / swift-cscf-v2026 / iec-62443-3-3 / nist-800-82-r3 / nist-800-63b-rev4 / fda-21cfr11 / fda-annex-11 / sec-1.05 / sox-404 / soc2-cc1.3 / cfpb-1033 / fapi-2.0 / staterramp / uk-g-cloud / hipaa-2026 / quebec-25 / 5 US state student-privacy postures / tcpa-10dlc / iab-tcf-v2.3 / iab-mspa). **Detectors:** *Compliance posture coverage gate* — `testCompliancePostureCoverage` enforces two invariants on every release: (1) every `POSTURE_DEFAULTS` key is in `KNOWN_POSTURES` so `b.compliance.set()` accepts it; (2) every `KNOWN_POSTURES` entry has a `REGIME_MAP` record so `b.compliance.describe()` resolves. Each violation reports the specific posture-name + file:line of the bad entry. Future operators adding a posture see the gate fire if either invariant breaks.
@@ -142,7 +142,13 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
142
142
  - **Pub/sub + events** — distributed pub/sub with cluster-table / Redis PUB/SUB / custom backends (`b.pubsub`); framework-emitted signal bus for breach / integrity events (`b.events`)
143
143
  - **CloudEvents + SSE** — CloudEvents 1.0 envelope for AWS EventBridge / Knative / Azure Event Grid / Google Eventarc / CNCF (`b.cloudEvents`); Server-Sent Events with newline-injection refusal in `event:` / `id:` / `data:` / `Last-Event-ID` (CVE-2026-33128 / 29085 / 44217 class) (`b.sse`, `b.middleware.sse`)
144
144
  - **Mail (outbound)** — multipart + attachments + DKIM + calendar invites; bounce intake (`b.mail`, `b.mailBounce`)
145
+ - **Mail (outbound delivery)** — turnkey MX-lookup → MTA-STS-fetch → DANE-TLSA → REQUIRETLS handshake → SMTP wire layer → RFC 3464 DSN-on-permanent-failure → deferred-retry scheduling, all wired once (`b.mail.send.deliver`)
145
146
  - **Mail (inbound auth)** — SPF / DMARC / ARC verify + ARC chain signing for relays (`b.mail.spf`, `b.mail.dmarc`, `b.mail.arc`)
147
+ - **Mail server listeners** — RFC 5321 MX inbound (`b.mail.server.mx`), RFC 6409 submission with SASL + identity-binding (`b.mail.server.submission`), RFC 9051 IMAP4rev2 with CONDSTORE / QRESYNC / NOTIFY / METADATA / CATENATE (`b.mail.server.imap`), RFC 8620 + RFC 8621 JMAP Core + Mail over HTTP/SSE/WebSocket (`b.mail.server.jmap`), POP3 (`b.mail.server.pop3`), ManageSieve (`b.mail.server.managesieve`)
148
+ - **JMAP EmailSubmission reference** — composes `b.mail.send.deliver` to land the RFC 8621 §7.5 surface end-to-end (`b.mail.server.jmap.emailSubmissionSetHandler`)
149
+ - **Mail crypto** — PQC-first S/MIME via CMS (`b.mail.crypto.cms`) + OpenPGP encrypt/decrypt + WKD key discovery with IDN-homograph defense (`b.mail.crypto.pgp`)
150
+ - **Mail-stack agent** — multi-threaded worker pool + queue dispatch + sealed mail-store backed by SQLite FTS5 (`b.mail.agent`, `b.mailStore`)
151
+ - **JSCalendar** — RFC 8984 Event/Task/Note/Group with iCalendar (RFC 5545) round-trip + RRULE expansion (every BY* filter + BYSETPOS + multi-rule UNION) for JMAP Calendars interop (`b.calendar`)
146
152
  - **Notifications** — generic dispatcher with operator-supplied transports (`b.notify`); TCPA / FCC 1:1 prior-express-written-consent + 10DLC carrier-shaped consent snapshot for SMS marketing (`b.tcpa10dlc`)
147
153
  - **File uploads** — chunked with per-chunk SHA3-512 verification + atomic finalize + tombstone cleanup (`b.fileUpload`)
148
154
  ### AI / agentic
@@ -164,7 +170,9 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
164
170
  - **APAC + LATAM** — `dpdp` / `pipl-cn` / `lgpd-br` / `appi-jp` / `pdpa-sg` / `quebec-25` / `irap` / `kr-ai-basic` / `pipa-kr` / `au-privacy` / `th-pdpa` / `vn-pdp` / `id-pdp` / `my-pdpa` / `cl-pdpa` / `mx-lfpdppp` / `ar-pdpa`
165
171
  - **Child privacy / age-appropriate design** — `ca-aadc` / `ny-safe-kids` / `ny-saffe` / `md-kids-code` / `vt-aadc`
166
172
  - **Financial / data-portability** — `fapi2` / `fapi-2.0-message-signing` / `fdx` / `dsr`
167
- - **AI governance** — `co-ai` / `il-hb3773` / `tx-traiga` / `ut-aipa` / `nyc-ll144` / `ca-tfaia` / `ca-sb942` / `ca-ab853` / `cn-ai-label` / `iso-42001` / `iso-23894`
173
+ - **AI governance** — `co-ai` / `il-hb3773` / `tx-traiga` / `ut-aipa` / `nyc-ll144` / `nyc-ll144-2024` / `sb-53` / `ca-tfaia` / `ca-sb942` / `ca-ab853` / `cn-ai-label` / `iso-42001` / `iso-23894` / `nist-ai-rmf-1.0` / `nist-ai-600-1-genai`
174
+ - **Federal / sectoral** — `42-cfr-part-2` / `hti-1` / `uscdi-v4` / `irs-1075` / `nist-csf-2.0` / `nist-800-53-r5-privacy` / `nist-800-172-r3` / `m-22-09` / `m-22-18` / `ffiec-cat-2` / `cri-profile-v2.0`
175
+ - **Critical infrastructure / info-sharing** — `soci-au` / `tlp-2.0`
168
176
  - **Accessibility** — `wcag-2-2`
169
177
  - **Other** — `bsi-c5` / `ens-es` / etc.
170
178
  - **AI Act ⇄ ISO cross-walk** — `b.compliance.aiAct.crossWalkIso42001()` + `crossWalkIso23894()` map every AI Act article (Art. 9 risk management → Art. 73 incident reporting) to the matching ISO/IEC 42001:2023 Annex A controls and ISO/IEC 23894:2023 risk-management clauses for ISO-certification audit packs
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
- "frameworkVersion": "0.12.2",
4
- "createdAt": "2026-05-22T18:36:43.972Z",
3
+ "frameworkVersion": "0.12.3",
4
+ "createdAt": "2026-05-22T21:50:06.665Z",
5
5
  "exports": {
6
6
  "a2a": {
7
7
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "../scripts/release-notes-schema.json",
3
+ "version": "0.12.3",
4
+ "date": "2026-05-22",
5
+ "headline": "README \"What ships in the box\" backfill — mail-stack listeners + JSCalendar + new postures",
6
+ "summary": "The README's \"Communication\" + \"Compliance regimes\" bullets lagged behind the v0.11.24-v0.12.1 ship cadence. Backfilled: `b.mail.send.deliver` (turnkey outbound delivery chain), the four mail-server listeners (mx / submission / imap / jmap), the JMAP EmailSubmission/set reference handler, mail-crypto (CMS + PGP+WKD), the mail-stack agent, `b.calendar` (RFC 8984 JSCalendar substrate with full BY*+BYSETPOS+multi-rule expansion), and the 16 newly-promoted postures from v0.12.1 (`42-cfr-part-2` / `hti-1` / `uscdi-v4` / `irs-1075` / `nist-800-172-r3` / `tlp-2.0` / `soci-au` / `ffiec-cat-2` / `cri-profile-v2.0` / `m-22-09` / `m-22-18` / `nist-800-53-r5-privacy` / `nist-ai-600-1-genai` / `nist-csf-2.0` / `sb-53` / `nyc-ll144-2024`).",
7
+ "sections": [
8
+ {
9
+ "heading": "Changed",
10
+ "items": [
11
+ {
12
+ "title": "Communication section names every mail-stack listener + delivery chain + crypto primitive",
13
+ "body": "New entries: `b.mail.send.deliver` (MX → MTA-STS → DANE → REQUIRETLS → SMTP → DSN chain), four `b.mail.server.*` listeners, JMAP EmailSubmission reference handler, `b.mail.crypto.cms` + `b.mail.crypto.pgp`, `b.mail.agent` + `b.mailStore`, and `b.calendar` (JSCalendar / iCalendar bridge for JMAP Calendars interop)."
14
+ },
15
+ {
16
+ "title": "Compliance regimes section lists the 16 v0.12.1 backfilled postures",
17
+ "body": "New rows organise the additions under three sub-bullets: AI governance adds `nyc-ll144-2024` / `sb-53` / `nist-ai-rmf-1.0` / `nist-ai-600-1-genai` alongside the existing AI-act / NYC-LL144 / Colorado / Illinois entries; a new \"Federal / sectoral\" row covers `42-cfr-part-2` / `hti-1` / `uscdi-v4` / `irs-1075` / `nist-csf-2.0` / `nist-800-53-r5-privacy` / `nist-800-172-r3` / `m-22-09` / `m-22-18` / `ffiec-cat-2` / `cri-profile-v2.0`; a new \"Critical infrastructure / info-sharing\" row covers `soci-au` / `tlp-2.0`."
18
+ }
19
+ ]
20
+ }
21
+ ],
22
+ "references": []
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.0.45",
3
+ "version": "0.0.46",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {