@blamejs/blamejs-shop 0.1.36 → 0.1.38

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 (31) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/lib/admin.js +205 -42
  4. package/lib/asset-manifest.json +5 -5
  5. package/lib/storefront.js +706 -74
  6. package/lib/translations.js +203 -0
  7. package/lib/vendor/MANIFEST.json +2 -2
  8. package/lib/vendor/blamejs/CHANGELOG.md +6 -0
  9. package/lib/vendor/blamejs/README.md +2 -1
  10. package/lib/vendor/blamejs/api-snapshot.json +6 -2
  11. package/lib/vendor/blamejs/index.js +1 -0
  12. package/lib/vendor/blamejs/lib/ai-disclosure.js +2 -3
  13. package/lib/vendor/blamejs/lib/ai-frontier-protocol.js +196 -0
  14. package/lib/vendor/blamejs/lib/archive-gz.js +5 -3
  15. package/lib/vendor/blamejs/lib/archive-read.js +84 -35
  16. package/lib/vendor/blamejs/lib/archive-tar-read.js +86 -31
  17. package/lib/vendor/blamejs/lib/archive-tar.js +2 -3
  18. package/lib/vendor/blamejs/lib/backup/index.js +2 -3
  19. package/lib/vendor/blamejs/lib/cose.js +4 -3
  20. package/lib/vendor/blamejs/lib/mail-server-jmap.js +9 -9
  21. package/lib/vendor/blamejs/lib/mail-server-submission.js +3 -2
  22. package/lib/vendor/blamejs/lib/mdoc.js +14 -14
  23. package/lib/vendor/blamejs/lib/network-dnssec.js +10 -8
  24. package/lib/vendor/blamejs/package.json +1 -1
  25. package/lib/vendor/blamejs/release-notes/v0.13.6.json +18 -0
  26. package/lib/vendor/blamejs/release-notes/v0.13.7.json +27 -0
  27. package/lib/vendor/blamejs/release-notes/v0.13.8.json +27 -0
  28. package/lib/vendor/blamejs/test/layer-0-primitives/ai-frontier-protocol.test.js +83 -0
  29. package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +50 -0
  30. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +89 -0
  31. package/package.json +1 -1
@@ -537,10 +537,213 @@ function _hydrate(row) {
537
537
  };
538
538
  }
539
539
 
540
+ // ---- storefront UI chrome catalog --------------------------------------
541
+ //
542
+ // The chrome strings the storefront renders on EVERY page — nav links,
543
+ // search controls, footer columns + links, the newsletter band, the
544
+ // locale switcher. These are the default-locale (`en`) baseline; an
545
+ // operator localises any key by authoring a `translations` row under
546
+ // resource_kind `ui`, resource_id `chrome`, field = the dotted key with
547
+ // dots rewritten to underscores (the table's `field` slug grammar
548
+ // forbids dots), locale = the target tag. `resolveChrome` reads those
549
+ // overrides at boot per active locale and layers them over this baseline,
550
+ // so an untranslated key always renders the English string — never a
551
+ // raw key. The exact English values here are the literal strings the
552
+ // layout shipped inline before i18n landed, so an `en` storefront is
553
+ // byte-identical to the pre-i18n markup.
554
+ //
555
+ // Both render paths (the container `lib/storefront.js` and the edge
556
+ // `worker/render/*`) consume this same catalog through `b.i18n`, so a
557
+ // given locale resolves to one identical string set on both sides.
558
+ var CHROME_KEY_PREFIX = "ui.chrome.";
559
+ var CHROME_RESOURCE_KIND = "ui";
560
+ var CHROME_RESOURCE_ID = "chrome";
561
+
562
+ var CHROME_DEFAULTS = Object.freeze({
563
+ skip_to_content: "Skip to content",
564
+
565
+ util_pill: "Open source · Apache 2.0",
566
+ util_msg: "Server-rendered HTML · post-quantum crypto on by default · zero npm runtime deps",
567
+ util_star: "Star on GitHub →",
568
+
569
+ search_label: "Search products",
570
+ search_placeholder: "Search the catalog",
571
+ search_submit: "Search",
572
+
573
+ nav_shop: "Shop",
574
+ nav_framework: "Framework",
575
+ nav_account: "Account",
576
+ // The cart aria-label carries the live item count; `{count}` is
577
+ // interpolated by b.i18n at render time.
578
+ nav_cart_aria: "Cart, {count} items",
579
+
580
+ newsletter_eyebrow: "Stay in the loop",
581
+ newsletter_title: "Get release notes the day they ship.",
582
+ newsletter_lede: "No marketing emails. A single short note when there's a new framework release, a security advisory, or a primitive worth knowing about.",
583
+ newsletter_email: "Email address",
584
+ newsletter_submit: "Subscribe",
585
+
586
+ footer_tagline: "An open-source shop framework — server-rendered HTML, zero npm runtime dependencies, security defaults on.",
587
+
588
+ footer_shop_heading: "Shop",
589
+ footer_shop_all: "All products",
590
+ footer_shop_collections: "Collections",
591
+ footer_shop_categories: "Categories",
592
+ footer_shop_new: "New arrivals",
593
+ footer_shop_sale: "On sale",
594
+ footer_shop_compare: "Compare",
595
+ footer_shop_cart: "Cart",
596
+
597
+ footer_framework_heading: "Framework",
598
+ footer_framework_source: "Source on GitHub",
599
+ footer_framework_core: "blamejs core",
600
+ footer_framework_security: "Security policy",
601
+ footer_framework_changelog: "Changelog",
602
+
603
+ footer_operators_heading: "Operators",
604
+ footer_operators_account: "Account",
605
+ footer_operators_orders: "Orders",
606
+ footer_operators_admin: "Admin",
607
+ footer_operators_contact: "Contact",
608
+
609
+ footer_copy_suffix: "built on blamejs · Apache 2.0 licensed.",
610
+ footer_legal_security: "Security",
611
+ footer_legal_privacy: "Privacy",
612
+ footer_legal_terms: "Terms",
613
+ footer_legal_cookies: "Manage cookies",
614
+
615
+ locale_switcher_label: "Language",
616
+ locale_switcher_submit: "Go",
617
+ });
618
+
619
+ // Default English chrome strings exposed verbatim for the render paths
620
+ // to fall back on when no i18n instance is configured (the storefront
621
+ // stays browsable on a deploy that hasn't seeded a locale policy).
622
+ function chromeDefaults() {
623
+ return Object.assign({}, CHROME_DEFAULTS);
624
+ }
625
+
626
+ // Build a `b.i18n` instance carrying the chrome catalog for every active
627
+ // locale. The baseline `en` tree is the shipped defaults; each other
628
+ // locale's tree starts empty and is layered with the operator's `ui`
629
+ // translation rows (read once here). b.i18n's own lookup chain then
630
+ // falls a missing key through to `defaultLocale` (the shipped English),
631
+ // so a partially-translated locale never surfaces a raw key.
632
+ //
633
+ // `opts.overrides` is the pre-read DB override map
634
+ // ({ "<locale>": { "<field>": "<value>", ... } }) — passed in by the
635
+ // caller (the storefront mount reads it via `readChromeOverrides`)
636
+ // rather than read here, so this stays a synchronous, query-free build
637
+ // that the edge Worker can run with the same inputs as the container.
638
+ function createChromeI18n(opts) {
639
+ opts = opts || {};
640
+ var defaultLocale = opts.defaultLocale || BASELINE_LOCALE;
641
+ var locales = Array.isArray(opts.locales) && opts.locales.length
642
+ ? opts.locales.slice()
643
+ : [defaultLocale];
644
+ if (locales.indexOf(defaultLocale) === -1) locales.unshift(defaultLocale);
645
+ var overrides = opts.overrides || {};
646
+
647
+ // Each locale's inline tree under the single `ui.chrome.*` namespace.
648
+ // The default locale gets the full English baseline; other locales get
649
+ // only their authored overrides (the rest falls through b.i18n's chain
650
+ // to the default locale).
651
+ var translations = {};
652
+ for (var li = 0; li < locales.length; li += 1) {
653
+ var loc = locales[li];
654
+ var chrome = {};
655
+ if (loc === defaultLocale) {
656
+ var defKeys = Object.keys(CHROME_DEFAULTS);
657
+ for (var d = 0; d < defKeys.length; d += 1) chrome[defKeys[d]] = CHROME_DEFAULTS[defKeys[d]];
658
+ }
659
+ var ovr = overrides[loc];
660
+ if (ovr) {
661
+ var ovrKeys = Object.keys(ovr);
662
+ for (var o = 0; o < ovrKeys.length; o += 1) {
663
+ // Only keys we know about land in the tree — an operator typo
664
+ // (a field that isn't a chrome key) is ignored rather than
665
+ // shadowing a real string with garbage.
666
+ if (Object.prototype.hasOwnProperty.call(CHROME_DEFAULTS, ovrKeys[o])) {
667
+ chrome[ovrKeys[o]] = ovr[ovrKeys[o]];
668
+ }
669
+ }
670
+ }
671
+ translations[loc] = { ui: { chrome: chrome } };
672
+ }
673
+
674
+ return b.i18n.create({
675
+ defaultLocale: defaultLocale,
676
+ locales: locales,
677
+ fallbackLocale: defaultLocale,
678
+ translations: translations,
679
+ // A missing chrome key returns the English baseline via the chain;
680
+ // should a key be absent from every locale (only possible if the
681
+ // baseline catalog itself is edited), return the dotted key rather
682
+ // than throwing inside a render. The render path never shows it
683
+ // because every shipped key has an `en` baseline.
684
+ missingKey: "return-key",
685
+ });
686
+ }
687
+
688
+ // Resolve the full chrome string set for one locale into a flat object
689
+ // keyed by the chrome field names (`nav_shop`, `footer_shop_all`, …),
690
+ // each value already locale-resolved through the i18n chain. The render
691
+ // paths feed this straight into the layout placeholders. The
692
+ // `nav_cart_aria` string keeps its literal `{count}` placeholder — the
693
+ // render path interpolates the live cart count uniformly across both
694
+ // substrates, so this stays request-independent and cacheable.
695
+ function resolveChrome(i18n, locale) {
696
+ var out = {};
697
+ var keys = Object.keys(CHROME_DEFAULTS);
698
+ for (var i = 0; i < keys.length; i += 1) {
699
+ var key = keys[i];
700
+ // No interpolation vars — `{count}` (and any future placeholder)
701
+ // survives as a literal for the render path to fill.
702
+ out[key] = i18n.t(CHROME_KEY_PREFIX + key, {}, { locale: locale });
703
+ }
704
+ return out;
705
+ }
706
+
707
+ // Read the operator's `ui`/`chrome` override rows for the given locales
708
+ // into the `{ "<locale>": { "<field>": "<value>" } }` shape
709
+ // `createChromeI18n` consumes. Missing-table-resilient: a deploy whose
710
+ // `translations` table hasn't been migrated (or a transient read error)
711
+ // yields an empty override map, so the storefront falls back to the
712
+ // English baseline rather than 500-ing. The caller passes a `query`
713
+ // function (the externalDb handle).
714
+ async function readChromeOverrides(query, locales) {
715
+ if (typeof query !== "function" || !Array.isArray(locales) || !locales.length) return {};
716
+ var out = {};
717
+ try {
718
+ var placeholders = locales.map(function (_l, i) { return "?" + (i + 3); }).join(", ");
719
+ var rows = (await query(
720
+ "SELECT locale, field, value FROM translations " +
721
+ "WHERE resource_kind = ?1 AND resource_id = ?2 AND locale IN (" + placeholders + ")",
722
+ [CHROME_RESOURCE_KIND, CHROME_RESOURCE_ID].concat(locales)
723
+ )).rows;
724
+ for (var i = 0; i < rows.length; i += 1) {
725
+ var r = rows[i];
726
+ if (!out[r.locale]) out[r.locale] = {};
727
+ out[r.locale][r.field] = r.value;
728
+ }
729
+ } catch (_e) {
730
+ // Missing table / read failure — degrade to the English baseline.
731
+ return {};
732
+ }
733
+ return out;
734
+ }
735
+
540
736
  module.exports = {
541
737
  create: create,
542
738
  MAX_VALUE_LEN: MAX_VALUE_LEN,
543
739
  MAX_BULK_ROWS: MAX_BULK_ROWS,
544
740
  BASELINE_LOCALE: BASELINE_LOCALE,
545
741
  DEFAULT_SAMPLE_SIZE: DEFAULT_SAMPLE_SIZE,
742
+ CHROME_DEFAULTS: CHROME_DEFAULTS,
743
+ CHROME_RESOURCE_KIND: CHROME_RESOURCE_KIND,
744
+ CHROME_RESOURCE_ID: CHROME_RESOURCE_ID,
745
+ chromeDefaults: chromeDefaults,
746
+ createChromeI18n: createChromeI18n,
747
+ resolveChrome: resolveChrome,
748
+ readChromeOverrides: readChromeOverrides,
546
749
  };
@@ -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.13.5",
7
- "tag": "v0.13.5",
6
+ "version": "0.13.8",
7
+ "tag": "v0.13.8",
8
8
  "license": "Apache-2.0",
9
9
  "author": "blamejs contributors",
10
10
  "source": "https://github.com/blamejs/blamejs",
@@ -8,6 +8,12 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.13.x
10
10
 
11
+ - v0.13.8 (2026-05-26) — **In-memory archive extraction for read-only / serverless filesystems.** Archive readers gain an in-memory extraction path so an uploaded archive can be opened and its contents read without writing anything to disk — the case a read-only or ephemeral serverless filesystem requires. b.archive.read.zip(...).extractEntries() and b.archive.read.tar(...).extractEntries() are async generators that yield each regular file entry as { name, bytes, size }, applying the same bomb-policy caps, b.guardArchive metadata cascade (which refuses a Zip-Slip / traversal archive wholesale), and entry-type-policy refusals as the disk extract() — only the disk realpath agreement check is omitted, since nothing is written and the caller owns where the returned bytes land. Directory and link entries carry no content and are not yielded. The guard cascade is factored into one shared path so disk and in-memory extraction refuse identically. Also a documentation fix: b.archive.gz no longer claims a b.archive.zip().toGzip() convenience exists — a ZIP is already DEFLATE-compressed per entry, so gzip-wrapping it gains nothing; gzip the uncompressed tar stream (the canonical .tar.gz) instead. **Added:** *`extractEntries()` — in-memory archive extraction (ZIP + tar)* — `b.archive.read.zip(source).extractEntries(opts?)` and `b.archive.read.tar(source).extractEntries(opts?)` are async generators yielding `{ name, bytes, size }` per regular file entry, never touching disk — for serverless / read-only filesystems where the disk `extract({ destination })` path cannot run. Same bomb-policy, guard-archive cascade, and entry-type-policy refusals as disk extraction; the bytes are byte-identical to what `extract()` writes. **Fixed:** *Removed the inaccurate `b.archive.zip().toGzip()` doc claim* — The `b.archive.gz` documentation described a `b.archive.zip().toGzip()` convenience method that does not (and should not) exist: a ZIP is already DEFLATE-compressed per entry, so gzip-wrapping it would compress already-compressed data for no benefit. `b.archive.tar().toGzip()` (the real `.tar.gz`) is unchanged.
12
+
13
+ - v0.13.7 (2026-05-26) — **Documentation accuracy — several primitives described shipped features as deferred.** A documentation sweep corrected primitive descriptions that still called features deferred after they shipped, so the wiki and inline docs now match the code. b.mdoc documents that device authentication (the ISO 18013-5 §9.1.3 signature variant, verifyDeviceAuth) is verified, not deferred — only the COSE_Mac0 device-auth variant remains refused. b.network.dns.dnssec documents that the root-to-zone chain walk against the IANA trust anchors (verifyChain) and NSEC / NSEC3 denial of existence (verifyDenial / nsec3Hash) ship, where the card previously said they were deferred. b.cose lists COSE_Mac0 and COSE_Encrypt0 among what it ships. The JMAP server documents its push channel, blob upload/download, and EmailSubmission handlers as present, and the submission server documents CHUNKING / BDAT as supported. A new test detector keeps this class of drift from recurring: it fails the build when a comment promises a feature lands in a version that has already shipped. **Fixed:** *Corrected `deferred`/`does-not-ship` docs for features that have shipped* — `b.mdoc` (device authentication, §9.1.3), `b.network.dns.dnssec` (chain walk + NSEC/NSEC3), `b.cose` (COSE_Mac0 + COSE_Encrypt0), the JMAP server (push, blob, EmailSubmission), and the submission server (BDAT/CHUNKING) all carried `@card`/`@intro` text describing shipped capabilities as deferred or not-shipped. The descriptions now match the implemented surface; genuinely-deferred items (the mdoc MAC variant, DNSSEC in-RDATA name canonicalization, COSE multi-signer/multi-recipient) remain documented as such. **Detectors:** *Overdue-defer detector in the codebase-pattern gate* — A new check fails the build when a comment promises a feature "lands in" / is "deferred to" / is "not supported in" a version that the package has already reached — catching stale deferral notes (a feature that shipped but whose comment still says otherwise, or a missed deadline) before they reach a release. An allowlist records the deliberate defer-with-condition exceptions.
14
+
15
+ - v0.13.6 (2026-05-26) — **`b.ai.frontierModelProtocol` — California SB 53 frontier-AI obligations.** b.ai.frontierModelProtocol assesses a developer's obligations under California's Transparency in Frontier Artificial Intelligence Act — SB 53, Cal. Bus. & Prof. Code §22757.10, effective 2026-01-01 — from a model's training compute and the developer's revenue. It reports whether the model crosses the frontier threshold (more than 10^26 training FLOPs), whether the developer is a large frontier developer (prior-year revenue, with affiliates, above $500M), and the resulting obligations: every frontier developer must report critical safety incidents and publish a transparency report, and a large frontier developer must additionally publish an annual safety framework and disclose its catastrophic-risk assessment. Passing a candidate safety framework reports which required elements (risk identification, mitigation, governance, cybersecurity, standards alignment) are missing. b.ai.frontierModelProtocol.incidentReport validates a critical-incident type against the Act's four categories and computes the notification deadline to the California Office of Emergency Services — 15 days from discovery, or 24 hours when there is an imminent risk of death or serious physical injury. The ca-tfaia compliance posture was already in the catalog. **Added:** *`b.ai.frontierModelProtocol` — SB 53 threshold classification, obligations, and incident reporting* — `b.ai.frontierModelProtocol({ trainingFlops, annualRevenueUsd, framework? })` returns `isFrontierModel`, `isLargeFrontierDeveloper`, the `obligations` list, and (when a framework is supplied) its `frameworkGaps`. `b.ai.frontierModelProtocol.incidentReport({ type, discoveredAt, imminentRiskToLife? })` builds a critical-safety-incident report with the California OES recipient and a `dueAt` / `deadlineHours` computed from the 15-day (or 24-hour imminent-risk) statutory window; `type` must be one of the four categories in `INCIDENT_TYPES`. Throws `FrontierProtocolError` on malformed input.
16
+
11
17
  - v0.13.5 (2026-05-26) — **`b.ai.aedtBiasAudit` — NYC Local Law 144 bias audit.** b.ai.aedtBiasAudit computes the bias-audit figures New York City Local Law 144 requires before an Automated Employment Decision Tool may screen candidates (NYC Admin. Code §20-870 et seq.; DCWP rules 6 RCNY §5-300). Given the per-category counts an independent auditor collected — selected/total for a pass-fail tool, or scored-above-the-overall-median/total for a continuous-score tool — it returns the selection (or scoring) rate, the impact ratio (each group's rate divided by the most-selected group's rate), and an adverse-impact flag (impact ratio below the EEOC four-fifths threshold of 0.8) for every group, across the sex, race/ethnicity, and intersectional dimensions, plus the most-selected group per dimension and an overall flag. Categories under 2% of the audited data are marked excluded per DCWP discretion. It is a pure calculation that produces exactly the figures the annual published summary must contain — the law mandates the calculation, not any particular remediation. The relevant compliance postures (nyc-ll144, and ca-tfaia for California SB 53) were already in the catalog. **Added:** *`b.ai.aedtBiasAudit` — Local Law 144 selection/scoring rates and four-fifths impact ratios* — `b.ai.aedtBiasAudit({ type, metadata, categories, minCategoryShare? })` where `type` is `"selection"` (group entries `{ selected, total }`) or `"scoring"` (`{ scoredAboveMedian, total }`). Returns per-group rate, impact ratio, and `adverseImpact` flag across the `sex`, `raceEthnicity`, and `intersectional` dimensions, plus the most-selected group per dimension and an `anyAdverseImpact` summary. Categories below `minCategoryShare` (2% default) are excluded from the impact-ratio basis. Throws `AedtBiasAuditError` on malformed input.
12
18
 
13
19
  - v0.13.4 (2026-05-26) — **`b.crdt` — conflict-free replicated data types.** b.crdt adds state-based Conflict-free Replicated Data Types: data structures that independent replicas update without coordination and still converge to the same value once they have exchanged state. Each type's merge is a join over a semilattice — commutative, associative, and idempotent — so replicas can merge in any order, any number of times, and agree, which makes these the substrate for active/active cluster state, offline-first clients that reconcile on reconnect, and eventually-consistent counters, sets, and maps. The release ships the full state-based family: grow-only and positive-negative counters (gCounter / pnCounter), grow-only, two-phase, and observed-remove sets (gSet / twoPSet / orSet), a last-write-wins register (lwwRegister), and an observed-remove map (orMap). Every type exposes the same contract — local mutators, merge(other) that returns a converged instance without mutating either operand, value() for the materialized value, and state() / fromState() for a JSON-serializable form to snapshot via b.archive or b.backup or ship to a peer — and carries a replicaId so per-replica contributions stay distinct. **Added:** *`b.crdt` — state-based CvRDT counters, sets, register, and map* — `b.crdt.gCounter` / `pnCounter` (grow-only and increment/decrement counters), `b.crdt.gSet` / `twoPSet` / `orSet` (grow-only, two-phase, and observed-remove sets — `orSet` supports re-add and resolves a concurrent add-vs-remove as add-wins), `b.crdt.lwwRegister` (last-write-wins with a deterministic replicaId tie-break), and `b.crdt.orMap` (observed-remove keys with last-write-wins values). Each exposes `merge` / `value` / `state` / `fromState` and converges by the CvRDT laws. `orSet` and `orMap` accept `tombstoneRetention` to bound tombstone memory against a remove flood.
@@ -190,6 +190,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
190
190
  - **AI usage quotas** — per-tenant / per-model budgets metered by tokens / requests / cost-usd / compute-hours over calendar-aligned windows, with an atomic conditional reserve (no charge-then-refund race) + hard/soft/warn enforcement and an optional cross-node store; defends OWASP LLM10:2025 unbounded consumption / denial-of-wallet (`b.ai.quota`)
191
191
  - **AI capability routing** — model-capability registry (context window / modalities / tool use / reasoning tier / cost rates) + a router that picks the cheapest model satisfying a request's requirements, refusing capability mismatches before the inference call (NIST AI RMF MAP + Model Cards); composes with `b.ai.quota` cost budgets (`b.ai.capability`)
192
192
  - **AEDT bias audit** — NYC Local Law 144 bias-audit figures (`b.ai.aedtBiasAudit`): selection / scoring rates and EEOC four-fifths-rule impact ratios across sex, race/ethnicity, and their intersection, with the most-selected group and adverse-impact flags (impact ratio < 0.8) for the annual published summary; sub-2% categories excludable per DCWP §5-301
193
+ - **Frontier AI protocol** — California SB 53 (Transparency in Frontier AI Act) obligations (`b.ai.frontierModelProtocol`): classify the frontier-model (>10²⁶ training FLOPs) and large-frontier-developer (>$500M revenue) thresholds, enumerate the resulting obligations, check a safety framework for required elements, and build a critical-safety-incident report with the 15-day / 24-hour California OES notification deadline (`.incidentReport`)
193
194
  ### Compliance regimes
194
195
 
195
196
  - **Posture coordinator** — `b.compliance` cascades operator-declared regime into retention / audit / db / cryptoField via POSTURE_DEFAULTS:
@@ -227,7 +228,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
227
228
  - **i18n** — CLDR plural rules, Accept-Language negotiation, Intl formatters, RTL (`b.i18n`)
228
229
  - **CSV** — RFC 4180 with Excel formula-injection prevention (`b.csv`)
229
230
  - **IDs + slugs** — RFC 9562 UUID v4 + v7 (`b.uuid`); URL-safe slugs (`b.slug`)
230
- - **Time + archive** — TZ-aware datetime (`b.time`); ZIP creation + adversarial-safe read with bomb caps + path-traversal + LFH/CD-skew defense (`b.archive` + `b.archive.read.zip`); one-liner quarantine extraction (`b.safeArchive.extract`); fs / objectStore / http / buffer / trusted-stream adapter contract (`b.archive.adapters`)
231
+ - **Time + archive** — TZ-aware datetime (`b.time`); ZIP creation + adversarial-safe read with bomb caps + path-traversal + LFH/CD-skew defense (`b.archive` + `b.archive.read.zip`); one-liner quarantine extraction (`b.safeArchive.extract`); in-memory extraction with no disk write for read-only / serverless filesystems (`b.archive.read.zip(...).extractEntries()` / `.tar`); fs / objectStore / http / buffer / trusted-stream adapter contract (`b.archive.adapters`)
231
232
  - **Pagination + forms** — HMAC-signed cursor pagination (`b.pagination`); HTML form rendering + validation + CSRF (`b.forms`)
232
233
 
233
234
  ### Production
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
- "frameworkVersion": "0.13.5",
4
- "createdAt": "2026-05-27T01:38:32.776Z",
3
+ "frameworkVersion": "0.13.8",
4
+ "createdAt": "2026-05-27T05:03:11.726Z",
5
5
  "exports": {
6
6
  "a2a": {
7
7
  "type": "object",
@@ -1577,6 +1577,10 @@
1577
1577
  }
1578
1578
  }
1579
1579
  },
1580
+ "frontierModelProtocol": {
1581
+ "type": "function",
1582
+ "arity": 1
1583
+ },
1580
1584
  "input": {
1581
1585
  "type": "object",
1582
1586
  "members": {
@@ -478,6 +478,7 @@ module.exports = {
478
478
  capability: require("./lib/ai-capability"),
479
479
  dp: require("./lib/ai-dp"),
480
480
  aedtBiasAudit: require("./lib/ai-aedt-bias-audit"),
481
+ frontierModelProtocol: require("./lib/ai-frontier-protocol"),
481
482
  },
482
483
  promisePool: require("./lib/promise-pool"),
483
484
  sdNotify: require("./lib/sd-notify"),
@@ -187,9 +187,8 @@ function chatbot(session, opts) {
187
187
  * machine-readable manner. This primitive returns the disclosure
188
188
  * payload (visible label + structured metadata) the operator wires
189
189
  * into the encoder / response pipeline. C2PA manifest emission is
190
- * deferred to v0.12.21 `b.contentCredentials` this primitive
191
- * supplies the label markup and the metadata schema that the C2PA
192
- * adapter consumes when it lands.
190
+ * handled by `b.contentCredentials`; this primitive supplies the label
191
+ * markup and the metadata schema that the C2PA adapter consumes.
193
192
  *
194
193
  * @opts
195
194
  * contentType: "image" | "audio" | "video" | "text", // required
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.ai.frontierModelProtocol
4
+ * @nav Compliance
5
+ * @title Frontier AI Protocol
6
+ *
7
+ * @intro
8
+ * Assess a developer's obligations under California's Transparency in
9
+ * Frontier Artificial Intelligence Act — SB 53, Cal. Bus. &amp; Prof. Code
10
+ * §22757.10 et seq., signed 2025-09-29 and effective 2026-01-01 — and build
11
+ * the critical-safety-incident report the law requires. SB 53 attaches
12
+ * obligations by two thresholds: a <em>frontier model</em> is one trained
13
+ * with more than 10<sup>26</sup> floating-point operations (including
14
+ * cumulative fine-tuning), and a <em>large frontier developer</em> is a
15
+ * frontier developer whose annual revenue, with affiliates, exceeded
16
+ * $500,000,000 in the prior calendar year. Frontier developers must report
17
+ * critical safety incidents; large frontier developers must additionally
18
+ * publish an annual frontier-AI safety framework.
19
+ *
20
+ * <code>frontierModelProtocol(opts)</code> takes the model's training compute
21
+ * and the developer's revenue and returns which thresholds are crossed and
22
+ * the resulting obligations, optionally checking that a supplied safety
23
+ * framework carries the elements the Act expects (risk identification,
24
+ * mitigation, governance, cybersecurity, and alignment with a recognized
25
+ * standard such as the NIST AI RMF or ISO/IEC 42001).
26
+ * <code>frontierModelProtocol.incidentReport(opts)</code> validates a
27
+ * critical-incident type against the Act's four definitions and computes the
28
+ * notification deadline: a routine report goes to the California Office of
29
+ * Emergency Services within 15 days of discovery; an imminent risk of death
30
+ * or serious physical injury is reported to an applicable authority within 24
31
+ * hours.
32
+ *
33
+ * @card
34
+ * California SB 53 frontier-AI protocol (`b.ai.frontierModelProtocol`) —
35
+ * classify frontier-model / large-developer thresholds (10²⁶ FLOPs, $500M
36
+ * revenue), enumerate obligations, and build the critical-safety-incident
37
+ * report with the 15-day / 24-hour OES notification deadline.
38
+ */
39
+
40
+ var C = require("./constants");
41
+ var { defineClass } = require("./framework-error");
42
+
43
+ var FrontierProtocolError = defineClass("FrontierProtocolError", { alwaysPermanent: true });
44
+
45
+ // SB 53 thresholds.
46
+ var FRONTIER_FLOP_THRESHOLD = 1e26; // > 10^26 training FLOPs → frontier model
47
+ var LARGE_DEVELOPER_REVENUE_USD = 5e8; // > $500,000,000 prior-year revenue → large developer
48
+ var INCIDENT_DEADLINE_MS = C.TIME.days(15); // report to CA OES within 15 days of discovery
49
+ var IMMINENT_DEADLINE_MS = C.TIME.hours(24); // within 24 hours if imminent risk to life
50
+
51
+ // The four critical safety incident categories (Cal. Bus. & Prof. Code §22757.10).
52
+ var CRITICAL_INCIDENT_TYPES = {
53
+ "weights-exfiltration-harm": "Unauthorized access, modification, or exfiltration of frontier-model weights resulting in death or bodily injury",
54
+ "catastrophic-risk-materialization": "Harm resulting from the materialization of a catastrophic risk (including loss of life or property from a dangerous capability)",
55
+ "loss-of-control-harm": "Loss of control of a frontier model causing death or bodily injury",
56
+ "deceptive-control-subversion": "A frontier model using deceptive techniques to subvert developer controls, demonstrating materially increased catastrophic risk",
57
+ };
58
+
59
+ // Elements a large frontier developer's published safety framework must address.
60
+ var REQUIRED_FRAMEWORK_ELEMENTS = ["riskIdentification", "riskMitigation", "governance", "cybersecurity", "standardsAlignment"];
61
+
62
+ function _posNum(v, label) {
63
+ if (typeof v !== "number" || !isFinite(v) || v < 0) throw new FrontierProtocolError("frontier/bad-value", "frontierModelProtocol: " + label + " must be a non-negative finite number");
64
+ return v;
65
+ }
66
+
67
+ /**
68
+ * @primitive b.ai.frontierModelProtocol
69
+ * @signature b.ai.frontierModelProtocol(opts)
70
+ * @since 0.13.6
71
+ * @status stable
72
+ * @compliance ca-tfaia, soc2
73
+ * @related b.ai.aedtBiasAudit, b.ai.disclosure.applyAll
74
+ *
75
+ * Determine SB 53 obligations from a model's training compute and the
76
+ * developer's revenue. Returns <code>isFrontierModel</code> (training FLOPs
77
+ * above 10<sup>26</sup>), <code>isLargeFrontierDeveloper</code> (a frontier
78
+ * developer with revenue above $500M), and the resulting
79
+ * <code>obligations</code>. When <code>framework</code> is supplied, its
80
+ * required elements are checked and reported in <code>frameworkGaps</code>.
81
+ * Throws <code>FrontierProtocolError</code> on malformed input.
82
+ *
83
+ * @opts
84
+ * trainingFlops: number, // total training FLOPs incl. fine-tuning (required)
85
+ * annualRevenueUsd: number, // developer prior-year revenue with affiliates (default: 0)
86
+ * framework: object, // optional safety framework to check for required elements
87
+ *
88
+ * @example
89
+ * var p = b.ai.frontierModelProtocol({ trainingFlops: 5e26, annualRevenueUsd: 1e9 });
90
+ * p.isFrontierModel; // → true
91
+ * p.isLargeFrontierDeveloper; // → true
92
+ * p.obligations; // → ["report-critical-safety-incidents", "publish-annual-safety-framework", ...]
93
+ */
94
+ function frontierModelProtocol(opts) {
95
+ opts = opts || {};
96
+ if (typeof opts !== "object") throw new FrontierProtocolError("frontier/bad-opts", "frontierModelProtocol: opts must be an object");
97
+ var allowed = { trainingFlops: 1, annualRevenueUsd: 1, framework: 1 };
98
+ Object.keys(opts).forEach(function (k) { if (!allowed[k]) throw new FrontierProtocolError("frontier/bad-opts", "frontierModelProtocol: unknown option '" + k + "'"); });
99
+
100
+ var flops = _posNum(opts.trainingFlops, "trainingFlops");
101
+ var revenue = opts.annualRevenueUsd != null ? _posNum(opts.annualRevenueUsd, "annualRevenueUsd") : 0;
102
+
103
+ var isFrontierModel = flops > FRONTIER_FLOP_THRESHOLD;
104
+ var isLargeFrontierDeveloper = isFrontierModel && revenue > LARGE_DEVELOPER_REVENUE_USD;
105
+
106
+ var obligations = [];
107
+ if (isFrontierModel) {
108
+ obligations.push("report-critical-safety-incidents");
109
+ obligations.push("publish-transparency-report");
110
+ }
111
+ if (isLargeFrontierDeveloper) {
112
+ obligations.push("publish-annual-safety-framework");
113
+ obligations.push("disclose-catastrophic-risk-assessment");
114
+ }
115
+
116
+ var out = {
117
+ isFrontierModel: isFrontierModel,
118
+ isLargeFrontierDeveloper: isLargeFrontierDeveloper,
119
+ thresholds: { frontierFlops: FRONTIER_FLOP_THRESHOLD, largeDeveloperRevenueUsd: LARGE_DEVELOPER_REVENUE_USD },
120
+ obligations: obligations,
121
+ };
122
+
123
+ if (opts.framework != null) {
124
+ if (typeof opts.framework !== "object") throw new FrontierProtocolError("frontier/bad-value", "frontierModelProtocol: framework must be an object");
125
+ var gaps = REQUIRED_FRAMEWORK_ELEMENTS.filter(function (el) { return !opts.framework[el]; });
126
+ out.frameworkGaps = gaps;
127
+ out.frameworkComplete = gaps.length === 0;
128
+ }
129
+ return out;
130
+ }
131
+
132
+ /**
133
+ * @primitive b.ai.frontierModelProtocol.incidentReport
134
+ * @signature b.ai.frontierModelProtocol.incidentReport(opts)
135
+ * @since 0.13.6
136
+ * @status stable
137
+ * @compliance ca-tfaia, soc2
138
+ * @related b.ai.frontierModelProtocol, b.ai.aedtBiasAudit
139
+ *
140
+ * Build a critical-safety-incident report and compute its notification
141
+ * deadline to the California Office of Emergency Services. <code>type</code>
142
+ * must be one of the Act's four categories; <code>discoveredAt</code> is when
143
+ * the incident was discovered. The deadline is 15 days from discovery, or 24
144
+ * hours when <code>imminentRiskToLife</code> is set. Returns the structured
145
+ * report with <code>dueAt</code> and <code>deadlineHours</code>. Throws
146
+ * <code>FrontierProtocolError</code> on an unknown type or bad timestamp.
147
+ *
148
+ * @opts
149
+ * type: string, // one of b.ai.frontierModelProtocol.INCIDENT_TYPES (required)
150
+ * discoveredAt: Date, // discovery time (Date or epoch-ms; default: now)
151
+ * imminentRiskToLife: boolean, // true → 24-hour deadline (default: false → 15 days)
152
+ * description: string, // free-text incident description (optional)
153
+ *
154
+ * @example
155
+ * var r = b.ai.frontierModelProtocol.incidentReport({
156
+ * type: "loss-of-control-harm", discoveredAt: new Date("2026-06-01T00:00:00Z"),
157
+ * });
158
+ * r.deadlineHours; // → 360 (15 days)
159
+ * r.recipient; // → "California Office of Emergency Services"
160
+ */
161
+ function incidentReport(opts) {
162
+ opts = opts || {};
163
+ if (typeof opts !== "object") throw new FrontierProtocolError("frontier/bad-opts", "incidentReport: opts must be an object");
164
+ if (!CRITICAL_INCIDENT_TYPES[opts.type]) throw new FrontierProtocolError("frontier/bad-incident-type", "incidentReport: type must be one of " + Object.keys(CRITICAL_INCIDENT_TYPES).join(", "));
165
+
166
+ var discoveredMs;
167
+ if (opts.discoveredAt == null) discoveredMs = Date.now();
168
+ else if (opts.discoveredAt instanceof Date) discoveredMs = opts.discoveredAt.getTime();
169
+ else discoveredMs = Number(opts.discoveredAt);
170
+ if (!isFinite(discoveredMs) || discoveredMs < 0) throw new FrontierProtocolError("frontier/bad-value", "incidentReport: discoveredAt must be a Date or non-negative epoch-ms");
171
+
172
+ var imminent = opts.imminentRiskToLife === true;
173
+ var windowMs = imminent ? IMMINENT_DEADLINE_MS : INCIDENT_DEADLINE_MS;
174
+
175
+ return {
176
+ type: opts.type,
177
+ typeDescription: CRITICAL_INCIDENT_TYPES[opts.type],
178
+ description: typeof opts.description === "string" ? opts.description : null,
179
+ imminentRiskToLife: imminent,
180
+ discoveredAt: new Date(discoveredMs).toISOString(),
181
+ dueAt: new Date(discoveredMs + windowMs).toISOString(),
182
+ deadlineHours: windowMs / C.TIME.hours(1),
183
+ // §22757.13: the routine report goes to OES within 15 days; an imminent
184
+ // risk of death or serious physical injury is reported to an applicable
185
+ // authority within 24 hours, which the operator selects for the incident.
186
+ recipient: imminent ? "An applicable authority with jurisdiction (e.g. law enforcement or a public-safety agency)" : "California Office of Emergency Services",
187
+ citation: "Cal. Bus. & Prof. Code §22757.13 (SB 53)",
188
+ };
189
+ }
190
+
191
+ frontierModelProtocol.incidentReport = incidentReport;
192
+ frontierModelProtocol.INCIDENT_TYPES = Object.keys(CRITICAL_INCIDENT_TYPES);
193
+ frontierModelProtocol.REQUIRED_FRAMEWORK_ELEMENTS = REQUIRED_FRAMEWORK_ELEMENTS;
194
+ frontierModelProtocol.FrontierProtocolError = FrontierProtocolError;
195
+
196
+ module.exports = frontierModelProtocol;
@@ -47,9 +47,11 @@ function _isGzipMagic(buf) {
47
47
  * `toAdapter(adapter)` / `digest()` — so gzip slots into the same
48
48
  * downstream sinks (object-store + filesystem + http adapters).
49
49
  *
50
- * Composes `b.archive.tar().toGzip(adapter)` / `b.archive.zip().
51
- * toGzip(adapter)` indirectly: those convenience methods call this
52
- * primitive after materializing their archive bytes.
50
+ * `b.archive.tar().toGzip(adapter)` composes this primitive after
51
+ * materializing the tar bytes (the canonical `.tar.gz`). There is no
52
+ * `zip().toGzip()` a ZIP is already DEFLATE-compressed per entry, so
53
+ * gzip-wrapping it would compress already-compressed data for no gain;
54
+ * gzip the uncompressed tar stream instead.
53
55
  *
54
56
  * @opts
55
57
  * level: number, // 0-9, default 6 (zlib default).