@blamejs/blamejs-shop 0.2.23 → 0.2.25

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 (76) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/admin.js +42 -9
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/cart.js +24 -9
  5. package/lib/storefront.js +190 -38
  6. package/lib/vendor/MANIFEST.json +3 -3
  7. package/lib/vendor/blamejs/CHANGELOG.md +4 -0
  8. package/lib/vendor/blamejs/GOVERNANCE.md +2 -3
  9. package/lib/vendor/blamejs/LTS-CALENDAR.md +6 -2
  10. package/lib/vendor/blamejs/SECURITY.md +1 -1
  11. package/lib/vendor/blamejs/api-snapshot.json +2 -2
  12. package/lib/vendor/blamejs/lib/a2a.js +11 -11
  13. package/lib/vendor/blamejs/lib/agent-snapshot.js +1 -1
  14. package/lib/vendor/blamejs/lib/ai-capability.js +20 -20
  15. package/lib/vendor/blamejs/lib/ai-content-detect.js +4 -3
  16. package/lib/vendor/blamejs/lib/ai-dp.js +17 -17
  17. package/lib/vendor/blamejs/lib/ai-input.js +3 -3
  18. package/lib/vendor/blamejs/lib/ai-pref.js +9 -9
  19. package/lib/vendor/blamejs/lib/ai-quota.js +17 -17
  20. package/lib/vendor/blamejs/lib/archive-read.js +10 -7
  21. package/lib/vendor/blamejs/lib/arg-parser.js +38 -38
  22. package/lib/vendor/blamejs/lib/audit-sign.js +4 -4
  23. package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +4 -4
  24. package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +1 -1
  25. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +10 -10
  26. package/lib/vendor/blamejs/lib/auth/step-up-policy.js +12 -12
  27. package/lib/vendor/blamejs/lib/auth/step-up.js +15 -15
  28. package/lib/vendor/blamejs/lib/boot-gates.js +6 -6
  29. package/lib/vendor/blamejs/lib/break-glass.js +1 -1
  30. package/lib/vendor/blamejs/lib/budr.js +6 -6
  31. package/lib/vendor/blamejs/lib/cms-codec.js +10 -9
  32. package/lib/vendor/blamejs/lib/content-credentials.js +13 -13
  33. package/lib/vendor/blamejs/lib/dark-patterns.js +15 -15
  34. package/lib/vendor/blamejs/lib/ddl-change-control.js +37 -37
  35. package/lib/vendor/blamejs/lib/dr-runbook.js +7 -7
  36. package/lib/vendor/blamejs/lib/fapi2.js +9 -9
  37. package/lib/vendor/blamejs/lib/fdx.js +7 -7
  38. package/lib/vendor/blamejs/lib/graphql-federation.js +2 -2
  39. package/lib/vendor/blamejs/lib/iab-mspa.js +5 -5
  40. package/lib/vendor/blamejs/lib/iab-tcf.js +18 -18
  41. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +10 -6
  42. package/lib/vendor/blamejs/lib/mcp.js +13 -13
  43. package/lib/vendor/blamejs/lib/middleware/require-step-up.js +3 -3
  44. package/lib/vendor/blamejs/lib/mtls-ca.js +2 -2
  45. package/lib/vendor/blamejs/lib/safe-archive.js +8 -7
  46. package/lib/vendor/blamejs/lib/sec-cyber.js +3 -3
  47. package/lib/vendor/blamejs/lib/sse.js +14 -14
  48. package/lib/vendor/blamejs/lib/tcpa-10dlc.js +5 -5
  49. package/lib/vendor/blamejs/lib/tenant-quota.js +18 -18
  50. package/lib/vendor/blamejs/package.json +1 -1
  51. package/lib/vendor/blamejs/release-notes/v0.13.43.json +39 -0
  52. package/lib/vendor/blamejs/release-notes/v0.13.44.json +31 -0
  53. package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +1 -1
  54. package/lib/vendor/blamejs/test/layer-0-primitives/ai-capability.test.js +15 -15
  55. package/lib/vendor/blamejs/test/layer-0-primitives/ai-dp.test.js +16 -16
  56. package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +2 -2
  57. package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +2 -2
  58. package/lib/vendor/blamejs/test/layer-0-primitives/ai-quota.test.js +19 -19
  59. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +1 -1
  60. package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +1 -1
  61. package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +3 -3
  62. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +52 -0
  63. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +3 -3
  64. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +2 -2
  65. package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +1 -1
  66. package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +6 -6
  67. package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +2 -2
  68. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +1 -1
  69. package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +3 -3
  70. package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +7 -7
  71. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +5 -5
  72. package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +1 -1
  73. package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +3 -3
  74. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +3 -3
  75. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +4 -1
  76. package/package.json +1 -1
@@ -39,9 +39,8 @@ then, the maintainer is final on technical direction.
39
39
  - **Operator-impacting changes.** Pre-1.0 the framework reserves the
40
40
  right to break operator-facing surface in any minor version; major
41
41
  versions ship deprecation warnings at least one minor before
42
- removal (per project rule §6 in `CLAUDE.md`). Post-1.0 the same
43
- contract applies across majors with a 24-month LTS window per
44
- [LTS-CALENDAR.md](LTS-CALENDAR.md).
42
+ removal. Post-1.0 the same contract applies across majors with a
43
+ 24-month LTS window per [LTS-CALENDAR.md](LTS-CALENDAR.md).
45
44
  - **Releases.** Patch (`0.0.x`) is the default; minor (`0.x.0`)
46
45
  requires an explicit decision the maintainer documents in the
47
46
  release notes; major (`x.0.0`) requires a deprecation cycle. The
@@ -1,13 +1,13 @@
1
1
  # LTS calendar
2
2
 
3
3
  `@blamejs/core` ships on a published major cadence. Each major receives
4
- **18 months of security-only patches** starting the day the next major is
4
+ **24 months of security-only patches** starting the day the next major is
5
5
  published. Feature backports are not promised.
6
6
 
7
7
  | Version | First release | Security patches through | Node minimum | KEM | Cipher | KDF | Sigs |
8
8
  |---------------|---------------|-----------------------------|---------------|----------------------|-----------------------|----------|-----------------------|
9
9
  | `v0.x` (pre-1.0) | 2026-04-25 | until v1.0 ships | 24 | ML-KEM-1024 + P-384 | XChaCha20-Poly1305 | SHAKE256 | SLH-DSA-SHAKE-256f |
10
- | `v1.x` | TBD | first release + 18 months | current LTS | ML-KEM-1024 + P-384 | XChaCha20-Poly1305 | SHAKE256 | SLH-DSA-SHAKE-256f |
10
+ | `v1.x` | TBD | first release + 24 months | current LTS | ML-KEM-1024 + P-384 | XChaCha20-Poly1305 | SHAKE256 | SLH-DSA-SHAKE-256f |
11
11
 
12
12
  ## What "security patches" means
13
13
 
@@ -27,3 +27,7 @@ The "Node minimum" column is the lowest Node major the framework supports for th
27
27
  ## Pre-1.0 caveat
28
28
 
29
29
  `v0.x` has no LTS commitment. Every release may change something operators depend on; the algorithm posture is intentionally evolving. Read [CHANGELOG.md](CHANGELOG.md) before upgrading across more than a few patches at a time. The LTS calendar takes effect at v1.0.
30
+
31
+ ## Experimental primitives are exempt
32
+
33
+ Primitives documented `@status experimental` (shown as "experimental" on each wiki page, and via the `experimental` segment in namespaces such as `b.jose.jwe.experimental`) are **not** covered by the stability contract or the LTS window. They may change signature, behavior, or wire format — or be removed — in any minor, without the deprecation cycle that stable primitives get. This applies on the LTS line too. The exemption exists so the framework can ship primitives that track in-flight standards (draft RFCs, pre-IANA codepoints, newly published W3C surfaces) without freezing an unsettled format for a major's full support window. A primitive graduates to stable by dropping the `@status experimental` marker in a release whose notes call out the graduation.
@@ -178,7 +178,7 @@ We coordinate with the reporter on disclosure — typical embargo is 14 days pos
178
178
 
179
179
  Pre-1.0, the supported version is the most-recent published patch on the most-recent minor. Older minors do not receive security backports unless the issue is critical AND the operator base on the older minor is non-trivial.
180
180
 
181
- Once 1.0 ships, the LTS calendar takes effect: each major gets 18 months of security-only patches after the next major's release.
181
+ Once 1.0 ships, the LTS calendar takes effect: each major gets 24 months of security-only patches after the next major's release.
182
182
 
183
183
  | Version range | Security patches |
184
184
  |---|---|
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
- "frameworkVersion": "0.13.42",
4
- "createdAt": "2026-05-29T18:46:45.251Z",
3
+ "frameworkVersion": "0.13.44",
4
+ "createdAt": "2026-05-30T00:31:59.364Z",
5
5
  "exports": {
6
6
  "a2a": {
7
7
  "type": "object",
@@ -48,52 +48,52 @@ var SEMVER_RE = /^[0-9]+\.[0-9]+(?:\.[0-9]+)?(?:-[A-Za-z0-9.-]+)?$/;
48
48
 
49
49
  function _validateCardShape(card, errorClass) {
50
50
  if (!card || typeof card !== "object" || Array.isArray(card)) {
51
- throw errorClass.factory("BAD_CARD",
51
+ throw errorClass.factory("a2a/bad-card",
52
52
  "a2a: card must be an object");
53
53
  }
54
54
  for (var i = 0; i < REQUIRED_CARD_FIELDS.length; i += 1) {
55
55
  var f = REQUIRED_CARD_FIELDS[i];
56
56
  if (typeof card[f] === "undefined" || card[f] === null) {
57
- throw errorClass.factory("MISSING_FIELD",
57
+ throw errorClass.factory("a2a/missing-field",
58
58
  "a2a: card." + f + " is required");
59
59
  }
60
60
  }
61
61
  if (typeof card.issuer !== "string" || card.issuer.length > ID_MAX || !ID_RE.test(card.issuer)) {
62
- throw errorClass.factory("BAD_FIELD",
62
+ throw errorClass.factory("a2a/bad-field",
63
63
  "a2a: card.issuer shape (must match " + ID_RE + ")");
64
64
  }
65
65
  if (typeof card.agentId !== "string" || card.agentId.length > ID_MAX || !ID_RE.test(card.agentId)) {
66
- throw errorClass.factory("BAD_FIELD",
66
+ throw errorClass.factory("a2a/bad-field",
67
67
  "a2a: card.agentId shape");
68
68
  }
69
69
  if (typeof card.version !== "string" || card.version.length > SEMVER_MAX || !SEMVER_RE.test(card.version)) {
70
- throw errorClass.factory("BAD_FIELD",
70
+ throw errorClass.factory("a2a/bad-field",
71
71
  "a2a: card.version must be semver");
72
72
  }
73
73
  if (!Array.isArray(card.capabilities)) {
74
- throw errorClass.factory("BAD_FIELD",
74
+ throw errorClass.factory("a2a/bad-field",
75
75
  "a2a: card.capabilities must be an array");
76
76
  }
77
77
  for (var c = 0; c < card.capabilities.length; c += 1) {
78
78
  var cap = card.capabilities[c];
79
79
  if (typeof cap !== "string" || cap.length === 0 || cap.length > CAP_NAME_MAX) {
80
- throw errorClass.factory("BAD_FIELD",
80
+ throw errorClass.factory("a2a/bad-field",
81
81
  "a2a: card.capabilities[" + c + "] must be 1-128 char string");
82
82
  }
83
83
  }
84
84
  if (card.endpoints !== undefined) {
85
85
  if (!Array.isArray(card.endpoints)) {
86
- throw errorClass.factory("BAD_FIELD",
86
+ throw errorClass.factory("a2a/bad-field",
87
87
  "a2a: card.endpoints must be an array");
88
88
  }
89
89
  for (var e = 0; e < card.endpoints.length; e += 1) {
90
90
  var ep = card.endpoints[e];
91
91
  if (!ep || typeof ep !== "object" || typeof ep.url !== "string") {
92
- throw errorClass.factory("BAD_FIELD",
92
+ throw errorClass.factory("a2a/bad-field",
93
93
  "a2a: card.endpoints[" + e + "] must have a string url");
94
94
  }
95
95
  if (!/^https:\/\//.test(ep.url) && !/^http:\/\/(localhost|127\.0\.0\.1|\[::1\])/.test(ep.url)) {
96
- throw errorClass.factory("INSECURE_ENDPOINT",
96
+ throw errorClass.factory("a2a/insecure-endpoint",
97
97
  "a2a: card.endpoints[" + e + "].url must be HTTPS (or localhost)");
98
98
  }
99
99
  }
@@ -226,7 +226,7 @@ function signCard(card, privateKeyPem, opts) {
226
226
  _validateCardShape(card, errorClass);
227
227
 
228
228
  if (typeof privateKeyPem !== "string" || privateKeyPem.length === 0) {
229
- throw errorClass.factory("BAD_KEY",
229
+ throw errorClass.factory("a2a/bad-key",
230
230
  "a2a.signCard: privateKeyPem required");
231
231
  }
232
232
 
@@ -170,7 +170,7 @@ function _resolveSigner(ctx) {
170
170
  var as;
171
171
  try { as = auditSign(); } catch (_e) { as = null; }
172
172
  if (as && typeof as.sign === "function" && typeof as.verify === "function") {
173
- // b.auditSign.sign throws "auditSign/not-initialized" when called
173
+ // b.auditSign.sign throws "audit-sign/not-initialized" when called
174
174
  // pre-init — surface that here as the snapshot's signer-not-wired
175
175
  // error so the caller's message is consistent regardless of which
176
176
  // dependency landed unwired.
@@ -108,31 +108,31 @@ function _isStringArray(a) {
108
108
  // surfaces at config time rather than as a silent mis-route.
109
109
  function _normalizeDescriptor(modelId, d) {
110
110
  if (!d || typeof d !== "object" || Array.isArray(d)) {
111
- throw new AiCapabilityError("aiCapability/bad-descriptor",
111
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
112
112
  "ai.capability: descriptor for '" + modelId + "' must be a plain object");
113
113
  }
114
114
  validateOpts(d, DESCRIPTOR_KEYS, "ai.capability descriptor['" + modelId + "']");
115
115
 
116
116
  if (!_isPositiveInt(d.maxContextTokens)) {
117
- throw new AiCapabilityError("aiCapability/bad-descriptor",
117
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
118
118
  "ai.capability: '" + modelId + "'.maxContextTokens must be a positive integer");
119
119
  }
120
120
  var maxOut = (d.maxOutputTokens == null) ? d.maxContextTokens : d.maxOutputTokens;
121
121
  if (!_isPositiveInt(maxOut)) {
122
- throw new AiCapabilityError("aiCapability/bad-descriptor",
122
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
123
123
  "ai.capability: '" + modelId + "'.maxOutputTokens must be a positive integer");
124
124
  }
125
125
 
126
126
  var modIn = (d.modalitiesIn == null) ? ["text"] : d.modalitiesIn;
127
127
  var modOut = (d.modalitiesOut == null) ? ["text"] : d.modalitiesOut;
128
128
  if (!_isStringArray(modIn) || !_isStringArray(modOut)) {
129
- throw new AiCapabilityError("aiCapability/bad-descriptor",
129
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
130
130
  "ai.capability: '" + modelId + "'.modalitiesIn / modalitiesOut must be arrays of non-empty strings");
131
131
  }
132
132
 
133
133
  var tier = (d.reasoningTier == null) ? "standard" : d.reasoningTier;
134
134
  if (REASONING_TIERS.indexOf(tier) === -1) {
135
- throw new AiCapabilityError("aiCapability/bad-descriptor",
135
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
136
136
  "ai.capability: '" + modelId + "'.reasoningTier must be one of " + REASONING_TIERS.join(" / "));
137
137
  }
138
138
 
@@ -140,7 +140,7 @@ function _normalizeDescriptor(modelId, d) {
140
140
  var costIn = (d.costPer1kInputTokens == null) ? 0 : d.costPer1kInputTokens;
141
141
  var costOut = (d.costPer1kOutputTokens == null) ? 0 : d.costPer1kOutputTokens;
142
142
  if (!_isNonNegFinite(cachingMax) || !_isNonNegFinite(costIn) || !_isNonNegFinite(costOut)) {
143
- throw new AiCapabilityError("aiCapability/bad-descriptor",
143
+ throw new AiCapabilityError("ai-capability/bad-descriptor",
144
144
  "ai.capability: '" + modelId + "'.promptCachingMaxTokens / costPer1kInputTokens / " +
145
145
  "costPer1kOutputTokens must be non-negative finite numbers");
146
146
  }
@@ -224,12 +224,12 @@ function create(opts) {
224
224
  validateOpts(opts, ["models", "audit"], "ai.capability.create");
225
225
 
226
226
  if (!opts.models || typeof opts.models !== "object" || Array.isArray(opts.models)) {
227
- throw new AiCapabilityError("aiCapability/bad-models",
227
+ throw new AiCapabilityError("ai-capability/bad-models",
228
228
  "ai.capability.create: models must be a plain object { modelId: descriptor }");
229
229
  }
230
230
  var ids = Object.keys(opts.models);
231
231
  if (ids.length === 0) {
232
- throw new AiCapabilityError("aiCapability/bad-models",
232
+ throw new AiCapabilityError("ai-capability/bad-models",
233
233
  "ai.capability.create: models must declare at least one model");
234
234
  }
235
235
 
@@ -249,7 +249,7 @@ function create(opts) {
249
249
  function describe(modelId) {
250
250
  var d = registry.get(modelId);
251
251
  if (!d) {
252
- throw new AiCapabilityError("aiCapability/unknown-model",
252
+ throw new AiCapabilityError("ai-capability/unknown-model",
253
253
  "ai.capability.describe: unknown model '" + modelId + "'");
254
254
  }
255
255
  return d;
@@ -261,7 +261,7 @@ function create(opts) {
261
261
 
262
262
  function register(modelId, descriptor) {
263
263
  validateOpts.requireNonEmptyString(modelId,
264
- "ai.capability.register: modelId", AiCapabilityError, "aiCapability/bad-model");
264
+ "ai.capability.register: modelId", AiCapabilityError, "ai-capability/bad-model");
265
265
  registry.set(modelId, _normalizeDescriptor(modelId, descriptor));
266
266
  return registry.get(modelId);
267
267
  }
@@ -323,21 +323,21 @@ function create(opts) {
323
323
  function _validateRequirements(requirements) {
324
324
  if (requirements == null) return {};
325
325
  if (typeof requirements !== "object" || Array.isArray(requirements)) {
326
- throw new AiCapabilityError("aiCapability/bad-requirements",
326
+ throw new AiCapabilityError("ai-capability/bad-requirements",
327
327
  "ai.capability: requirements must be a plain object");
328
328
  }
329
329
  validateOpts(requirements, REQUIREMENT_KEYS, "ai.capability requirements");
330
330
  if (requirements.minReasoningTier != null &&
331
331
  REASONING_TIERS.indexOf(requirements.minReasoningTier) === -1) {
332
- throw new AiCapabilityError("aiCapability/bad-requirements",
332
+ throw new AiCapabilityError("ai-capability/bad-requirements",
333
333
  "ai.capability: minReasoningTier must be one of " + REASONING_TIERS.join(" / "));
334
334
  }
335
335
  if (requirements.modalitiesIn != null && !_isStringArray(requirements.modalitiesIn)) {
336
- throw new AiCapabilityError("aiCapability/bad-requirements",
336
+ throw new AiCapabilityError("ai-capability/bad-requirements",
337
337
  "ai.capability: requirements.modalitiesIn must be an array of non-empty strings");
338
338
  }
339
339
  if (requirements.modalitiesOut != null && !_isStringArray(requirements.modalitiesOut)) {
340
- throw new AiCapabilityError("aiCapability/bad-requirements",
340
+ throw new AiCapabilityError("ai-capability/bad-requirements",
341
341
  "ai.capability: requirements.modalitiesOut must be an array of non-empty strings");
342
342
  }
343
343
  // Numeric minimums are compared with `<` against the descriptor; a
@@ -349,7 +349,7 @@ function create(opts) {
349
349
  for (var ni = 0; ni < numericMins.length; ni++) {
350
350
  var nk = numericMins[ni];
351
351
  if (requirements[nk] != null && !_isNonNegFinite(requirements[nk])) {
352
- throw new AiCapabilityError("aiCapability/bad-requirements",
352
+ throw new AiCapabilityError("ai-capability/bad-requirements",
353
353
  "ai.capability: requirements." + nk + " must be a non-negative finite number");
354
354
  }
355
355
  }
@@ -360,7 +360,7 @@ function create(opts) {
360
360
  for (var bi = 0; bi < booleanReqs.length; bi++) {
361
361
  var bk = booleanReqs[bi];
362
362
  if (requirements[bk] != null && typeof requirements[bk] !== "boolean") {
363
- throw new AiCapabilityError("aiCapability/bad-requirements",
363
+ throw new AiCapabilityError("ai-capability/bad-requirements",
364
364
  "ai.capability: requirements." + bk + " must be a boolean");
365
365
  }
366
366
  }
@@ -392,7 +392,7 @@ function create(opts) {
392
392
  var costBasis = null;
393
393
  if (routeOpts.costBasis != null) {
394
394
  if (typeof routeOpts.costBasis !== "object" || Array.isArray(routeOpts.costBasis)) {
395
- throw new AiCapabilityError("aiCapability/bad-requirements",
395
+ throw new AiCapabilityError("ai-capability/bad-requirements",
396
396
  "ai.capability.route: costBasis must be a plain object { inputTokens, outputTokens }");
397
397
  }
398
398
  validateOpts(routeOpts.costBasis, ["inputTokens", "outputTokens"],
@@ -405,7 +405,7 @@ function create(opts) {
405
405
  for (var ci = 0; ci < cbFields.length; ci++) {
406
406
  var ck = cbFields[ci];
407
407
  if (routeOpts.costBasis[ck] != null && !_isNonNegFinite(routeOpts.costBasis[ck])) {
408
- throw new AiCapabilityError("aiCapability/bad-requirements",
408
+ throw new AiCapabilityError("ai-capability/bad-requirements",
409
409
  "ai.capability.route: costBasis." + ck + " must be a non-negative finite number");
410
410
  }
411
411
  }
@@ -446,7 +446,7 @@ function create(opts) {
446
446
  if (routeOpts.fallback != null) {
447
447
  var fb = registry.get(routeOpts.fallback);
448
448
  if (!fb) {
449
- throw new AiCapabilityError("aiCapability/unknown-model",
449
+ throw new AiCapabilityError("ai-capability/unknown-model",
450
450
  "ai.capability.route: fallback '" + routeOpts.fallback + "' is not a registered model");
451
451
  }
452
452
  _emitAudit("ai/capability-fallback", "allowed", {
@@ -461,7 +461,7 @@ function create(opts) {
461
461
  }
462
462
 
463
463
  _emitAudit("ai/capability-no-candidate", "denied", { requirements: requirements });
464
- throw new AiCapabilityError("aiCapability/no-candidate",
464
+ throw new AiCapabilityError("ai-capability/no-candidate",
465
465
  "ai.capability.route: no registered model satisfies the requirements " +
466
466
  "and no fallback was supplied");
467
467
  }
@@ -30,9 +30,10 @@
30
30
  * IPTC `digitalSourceType` PhotoMetadata reading is forward-watch —
31
31
  * the framework ships no XMP / EXIF parser yet, so operators that
32
32
  * want IPTC detection pre-parse with their tool of choice and pass
33
- * the field via `opts.ipmd`. AB-853 names C2PA as "widely adopted";
34
- * IPTC PhotoMetadata reader lands in v0.10.9 once a vendoring
35
- * decision is made.
33
+ * the field via `opts.ipmd`. AB-853 names C2PA as "widely adopted".
34
+ * A built-in IPTC PhotoMetadata reader is deferred pending a vendoring
35
+ * decision for an XMP/EXIF parser; the `opts.ipmd` escape hatch covers
36
+ * the gap until then.
36
37
  *
37
38
  * @card
38
39
  * Inbound provenance detector — composes C2PA verify + CAC implicit-label parser + operator-supplied IPTC field, returns a normalized report for AB-853 / EU AI Act Art. 50 / CAC disclosure UIs.
@@ -102,7 +102,7 @@ function _frGt(a, b) { return a.num * b.den > b.num * a.den; } // a > b
102
102
  // Uniform BigInt in [0, m) via rejection sampling on CSPRNG bytes —
103
103
  // no modulo bias.
104
104
  function _uniformBelow(m) {
105
- if (m <= 0n) throw new AiDpError("aiDp/internal", "ai.dp: _uniformBelow needs m > 0");
105
+ if (m <= 0n) throw new AiDpError("ai-dp/internal", "ai.dp: _uniformBelow needs m > 0");
106
106
  if (m === 1n) return 0n;
107
107
  var bits = m.toString(2).length;
108
108
  var bytes = Math.ceil(bits / 8); // allow:raw-byte-literal — bits-per-byte divisor, not a size
@@ -296,23 +296,23 @@ function mechanism(opts) {
296
296
  validateOpts(opts, ["type", "sensitivity", "epsilon", "delta", "bound"], "ai.dp.mechanism");
297
297
 
298
298
  if (MECHANISMS.indexOf(opts.type) === -1) {
299
- throw new AiDpError("aiDp/bad-mechanism",
299
+ throw new AiDpError("ai-dp/bad-mechanism",
300
300
  "ai.dp.mechanism: type must be one of " + MECHANISMS.join(" / ") +
301
301
  " (exponential / sparse-vector are deferred — their float-safe constructions " +
302
302
  "re-open on demand)");
303
303
  }
304
304
  if (typeof opts.sensitivity !== "number" || !isFinite(opts.sensitivity) || opts.sensitivity <= 0) {
305
- throw new AiDpError("aiDp/bad-sensitivity",
305
+ throw new AiDpError("ai-dp/bad-sensitivity",
306
306
  "ai.dp.mechanism: sensitivity must be a positive finite number");
307
307
  }
308
308
  if (typeof opts.epsilon !== "number" || !isFinite(opts.epsilon) || opts.epsilon <= 0) {
309
- throw new AiDpError("aiDp/bad-epsilon",
309
+ throw new AiDpError("ai-dp/bad-epsilon",
310
310
  "ai.dp.mechanism: epsilon must be a positive finite number");
311
311
  }
312
312
 
313
313
  if (opts.type === "laplace") {
314
314
  if (typeof opts.bound !== "number" || !isFinite(opts.bound) || opts.bound <= 0) {
315
- throw new AiDpError("aiDp/bad-bound",
315
+ throw new AiDpError("ai-dp/bad-bound",
316
316
  "ai.dp.mechanism: laplace requires bound > 0 (the snapping clamp; the " +
317
317
  "privacy guarantee depends on it)");
318
318
  }
@@ -325,11 +325,11 @@ function mechanism(opts) {
325
325
 
326
326
  // gaussian
327
327
  if (typeof opts.delta !== "number" || !isFinite(opts.delta) || opts.delta <= 0 || opts.delta >= 1) {
328
- throw new AiDpError("aiDp/bad-delta",
328
+ throw new AiDpError("ai-dp/bad-delta",
329
329
  "ai.dp.mechanism: gaussian requires 0 < delta < 1");
330
330
  }
331
331
  if (opts.epsilon > 1) {
332
- throw new AiDpError("aiDp/epsilon-too-large",
332
+ throw new AiDpError("ai-dp/epsilon-too-large",
333
333
  "ai.dp.mechanism: the classic Gaussian calibration is proven for epsilon <= 1; " +
334
334
  "split into multiple releases under an rdp budget, or the analytic Gaussian " +
335
335
  "mechanism (Balle-Wang 2018) re-opens this path on demand");
@@ -346,7 +346,7 @@ function mechanism(opts) {
346
346
  // budget wraps this).
347
347
  function _applyMechanism(m, value) {
348
348
  if (typeof value !== "number" || !isFinite(value)) {
349
- throw new AiDpError("aiDp/bad-value", "ai.dp: value must be a finite number");
349
+ throw new AiDpError("ai-dp/bad-value", "ai.dp: value must be a finite number");
350
350
  }
351
351
  if (m.type === "laplace") {
352
352
  return _snappingLaplace(value, m.scale, m.bound);
@@ -403,22 +403,22 @@ function budget(opts) {
403
403
  validateOpts(opts, ["scope", "epsilon", "delta", "accounting", "audit"], "ai.dp.budget");
404
404
 
405
405
  validateOpts.requireNonEmptyString(opts.scope,
406
- "ai.dp.budget: scope", AiDpError, "aiDp/bad-scope");
406
+ "ai.dp.budget: scope", AiDpError, "ai-dp/bad-scope");
407
407
  if (typeof opts.epsilon !== "number" || !isFinite(opts.epsilon) || opts.epsilon <= 0) {
408
- throw new AiDpError("aiDp/bad-epsilon", "ai.dp.budget: epsilon must be a positive finite number");
408
+ throw new AiDpError("ai-dp/bad-epsilon", "ai.dp.budget: epsilon must be a positive finite number");
409
409
  }
410
410
  var totalEpsilon = opts.epsilon;
411
411
  var totalDelta = (opts.delta == null) ? 0 : opts.delta;
412
412
  if (typeof totalDelta !== "number" || !isFinite(totalDelta) || totalDelta < 0 || totalDelta >= 1) {
413
- throw new AiDpError("aiDp/bad-delta", "ai.dp.budget: delta must be in [0, 1)");
413
+ throw new AiDpError("ai-dp/bad-delta", "ai.dp.budget: delta must be in [0, 1)");
414
414
  }
415
415
  var accounting = (opts.accounting == null) ? "basic" : opts.accounting;
416
416
  if (ACCOUNTINGS.indexOf(accounting) === -1) {
417
- throw new AiDpError("aiDp/bad-accounting",
417
+ throw new AiDpError("ai-dp/bad-accounting",
418
418
  "ai.dp.budget: accounting must be one of " + ACCOUNTINGS.join(" / "));
419
419
  }
420
420
  if (accounting === "rdp" && totalDelta <= 0) {
421
- throw new AiDpError("aiDp/bad-accounting",
421
+ throw new AiDpError("ai-dp/bad-accounting",
422
422
  "ai.dp.budget: rdp accounting requires delta > 0 (the RDP→(ε,δ) conversion is " +
423
423
  "undefined at delta = 0; use basic accounting for pure-ε budgets)");
424
424
  }
@@ -457,11 +457,11 @@ function budget(opts) {
457
457
 
458
458
  function consume(m, value) {
459
459
  if (!m || typeof m !== "object" || MECHANISMS.indexOf(m.type) === -1) {
460
- throw new AiDpError("aiDp/bad-mechanism",
460
+ throw new AiDpError("ai-dp/bad-mechanism",
461
461
  "ai.dp.budget.consume: first argument must be a b.ai.dp.mechanism");
462
462
  }
463
463
  if (m.type === "gaussian" && totalDelta <= 0) {
464
- throw new AiDpError("aiDp/bad-delta",
464
+ throw new AiDpError("ai-dp/bad-delta",
465
465
  "ai.dp.budget.consume: a gaussian mechanism needs a scope delta > 0");
466
466
  }
467
467
 
@@ -475,7 +475,7 @@ function budget(opts) {
475
475
  requestEpsilon: m.epsilon, requestDelta: m.delta,
476
476
  spentEpsilon: spentEpsilon, totalEpsilon: totalEpsilon,
477
477
  });
478
- throw new AiDpError("aiDp/budget-exhausted",
478
+ throw new AiDpError("ai-dp/budget-exhausted",
479
479
  "ai.dp.budget.consume: scope '" + scope + "' would spend ε=" +
480
480
  (spentEpsilon + m.epsilon) + "/" + totalEpsilon + ", δ=" +
481
481
  (spentDelta + m.delta) + "/" + totalDelta + "; refused");
@@ -489,7 +489,7 @@ function budget(opts) {
489
489
  scope: scope, accounting: accounting, mechanism: m.type,
490
490
  projectedEpsilon: trialEps, totalEpsilon: totalEpsilon,
491
491
  });
492
- throw new AiDpError("aiDp/budget-exhausted",
492
+ throw new AiDpError("ai-dp/budget-exhausted",
493
493
  "ai.dp.budget.consume: scope '" + scope + "' would reach ε=" +
494
494
  trialEps.toFixed(4) + " of " + totalEpsilon + " at δ=" + totalDelta + "; refused");
495
495
  }
@@ -104,12 +104,12 @@ function classify(input, opts) {
104
104
  var auditOn = opts.audit !== false;
105
105
 
106
106
  if (typeof input !== "string") {
107
- throw errorClass.factory("BAD_INPUT",
107
+ throw errorClass.factory("ai-input/bad-input",
108
108
  "aiInput.classify: input must be a string");
109
109
  }
110
110
  var byteLen = Buffer.byteLength(input, "utf8");
111
111
  if (byteLen > maxBytes) {
112
- throw errorClass.factory("INPUT_TOO_LARGE",
112
+ throw errorClass.factory("ai-input/input-too-large",
113
113
  "aiInput.classify: input exceeds " + maxBytes + " bytes (got " + byteLen + ")");
114
114
  }
115
115
 
@@ -187,7 +187,7 @@ function refuseIfMalicious(input, opts) {
187
187
  var errorClass = opts.errorClass || AiInputError;
188
188
  var result = classify(input, opts);
189
189
  if (result.verdict === "malicious") {
190
- throw errorClass.factory("MALICIOUS_INPUT",
190
+ throw errorClass.factory("ai-input/malicious-input",
191
191
  "aiInput: input flagged as malicious (signals: " +
192
192
  result.signals.map(function (s) { return s.id; }).join(", ") + ")");
193
193
  }
@@ -38,25 +38,25 @@ var SNIPPET_VALUES = ["allow", "deny"];
38
38
 
39
39
  function _validate(opts) {
40
40
  if (!opts || typeof opts !== "object") {
41
- throw AiPrefError.factory("BAD_OPTS",
41
+ throw AiPrefError.factory("ai-pref/bad-opts",
42
42
  "aiPref: opts required");
43
43
  }
44
44
  var train = opts.train || "deny";
45
45
  var infer = opts.infer || "allow";
46
46
  var snippet = opts.snippet || "allow";
47
47
  if (TRAIN_VALUES.indexOf(train) === -1) {
48
- throw AiPrefError.factory("BAD_TRAIN", "aiPref: train must be one of " + TRAIN_VALUES.join(", "));
48
+ throw AiPrefError.factory("ai-pref/bad-train", "aiPref: train must be one of " + TRAIN_VALUES.join(", "));
49
49
  }
50
50
  if (INFER_VALUES.indexOf(infer) === -1) {
51
- throw AiPrefError.factory("BAD_INFER", "aiPref: infer must be one of " + INFER_VALUES.join(", "));
51
+ throw AiPrefError.factory("ai-pref/bad-infer", "aiPref: infer must be one of " + INFER_VALUES.join(", "));
52
52
  }
53
53
  if (SNIPPET_VALUES.indexOf(snippet) === -1) {
54
- throw AiPrefError.factory("BAD_SNIPPET", "aiPref: snippet must be one of " + SNIPPET_VALUES.join(", "));
54
+ throw AiPrefError.factory("ai-pref/bad-snippet", "aiPref: snippet must be one of " + SNIPPET_VALUES.join(", "));
55
55
  }
56
56
  if ((train === "paid" || infer === "paid") &&
57
57
  (!opts.price || typeof opts.price.amountUsd !== "number" ||
58
58
  !isFinite(opts.price.amountUsd) || opts.price.amountUsd <= 0)) {
59
- throw AiPrefError.factory("BAD_PRICE",
59
+ throw AiPrefError.factory("ai-pref/bad-price",
60
60
  "aiPref: price.amountUsd (positive finite number) required when train or infer is 'paid'");
61
61
  }
62
62
  return { train: train, infer: infer, snippet: snippet, price: opts.price || null };
@@ -140,10 +140,10 @@ function serializeHeader(opts) {
140
140
  */
141
141
  function parseHeader(value) {
142
142
  if (typeof value !== "string" || value.length === 0) {
143
- throw AiPrefError.factory("BAD_HEADER", "aiPref.parseHeader: value required");
143
+ throw AiPrefError.factory("ai-pref/bad-header", "aiPref.parseHeader: value required");
144
144
  }
145
145
  if (value.length > 1024) { // allow:raw-byte-literal — header value cap, not bytes
146
- throw AiPrefError.factory("HEADER_TOO_LARGE",
146
+ throw AiPrefError.factory("ai-pref/header-too-large",
147
147
  "aiPref.parseHeader: value exceeds 1024 chars");
148
148
  }
149
149
  structuredFields.refuseControlBytes(value, {
@@ -206,7 +206,7 @@ function robotsBlock(opts) {
206
206
  var v = _validate(opts);
207
207
  var ua = opts.userAgent || "*";
208
208
  if (typeof ua !== "string" || ua.length === 0 || ua.length > 256) { // allow:raw-byte-literal — UA-string cap, not bytes
209
- throw AiPrefError.factory("BAD_USER_AGENT",
209
+ throw AiPrefError.factory("ai-pref/bad-user-agent",
210
210
  "aiPref.robotsBlock: userAgent must be 1-256 char string (or omit for *)");
211
211
  }
212
212
  return "User-agent: " + ua + "\n" +
@@ -298,7 +298,7 @@ function middleware(opts) {
298
298
  */
299
299
  function refusePaidCrawl(req, res, opts) {
300
300
  if (!opts || !opts.price || typeof opts.price.amountUsd !== "number") {
301
- throw AiPrefError.factory("BAD_PRICE",
301
+ throw AiPrefError.factory("ai-pref/bad-price",
302
302
  "aiPref.refusePaidCrawl: opts.price.amountUsd required");
303
303
  }
304
304
  var body = JSON.stringify({
@@ -257,20 +257,20 @@ function create(opts) {
257
257
 
258
258
  var dimension = opts.dimension;
259
259
  if (DIMENSIONS.indexOf(dimension) === -1) {
260
- throw new AiQuotaError("aiQuota/bad-dimension",
260
+ throw new AiQuotaError("ai-quota/bad-dimension",
261
261
  "ai.quota.create: dimension must be one of " + DIMENSIONS.join(" / ") +
262
262
  " (got " + JSON.stringify(dimension) + ")");
263
263
  }
264
264
 
265
265
  var period = opts.period;
266
266
  if (PERIODS.indexOf(period) === -1) {
267
- throw new AiQuotaError("aiQuota/bad-period",
267
+ throw new AiQuotaError("ai-quota/bad-period",
268
268
  "ai.quota.create: period must be one of " + PERIODS.join(" / ") +
269
269
  " (got " + JSON.stringify(period) + ")");
270
270
  }
271
271
 
272
272
  if (typeof opts.limit !== "number" || !isFinite(opts.limit) || opts.limit <= 0) {
273
- throw new AiQuotaError("aiQuota/bad-limit",
273
+ throw new AiQuotaError("ai-quota/bad-limit",
274
274
  "ai.quota.create: limit must be a positive finite number");
275
275
  }
276
276
  var defaultLimit = opts.limit;
@@ -281,7 +281,7 @@ function create(opts) {
281
281
 
282
282
  var enforcement = (opts.enforcement == null) ? "hard" : opts.enforcement;
283
283
  if (ENFORCEMENTS.indexOf(enforcement) === -1) {
284
- throw new AiQuotaError("aiQuota/bad-enforcement",
284
+ throw new AiQuotaError("ai-quota/bad-enforcement",
285
285
  "ai.quota.create: enforcement must be one of " + ENFORCEMENTS.join(" / ") +
286
286
  " (got " + JSON.stringify(enforcement) + ")");
287
287
  }
@@ -353,11 +353,11 @@ function create(opts) {
353
353
 
354
354
  function consume(tenantId, model, amount, consumeOpts) {
355
355
  validateOpts.requireNonEmptyString(tenantId,
356
- "ai.quota.consume: tenantId", AiQuotaError, "aiQuota/bad-tenant");
356
+ "ai.quota.consume: tenantId", AiQuotaError, "ai-quota/bad-tenant");
357
357
  validateOpts.requireNonEmptyString(model,
358
- "ai.quota.consume: model", AiQuotaError, "aiQuota/bad-model");
358
+ "ai.quota.consume: model", AiQuotaError, "ai-quota/bad-model");
359
359
  if (typeof amount !== "number" || !isFinite(amount) || amount < 0) {
360
- throw new AiQuotaError("aiQuota/bad-amount",
360
+ throw new AiQuotaError("ai-quota/bad-amount",
361
361
  "ai.quota.consume: amount must be a non-negative finite number");
362
362
  }
363
363
  consumeOpts = consumeOpts || {};
@@ -366,7 +366,7 @@ function create(opts) {
366
366
  // enforcer; still validated against the allowlist.
367
367
  var mode = (consumeOpts.enforcement == null) ? enforcement : consumeOpts.enforcement;
368
368
  if (ENFORCEMENTS.indexOf(mode) === -1) {
369
- throw new AiQuotaError("aiQuota/bad-enforcement",
369
+ throw new AiQuotaError("ai-quota/bad-enforcement",
370
370
  "ai.quota.consume: enforcement override must be one of " + ENFORCEMENTS.join(" / "));
371
371
  }
372
372
 
@@ -401,7 +401,7 @@ function create(opts) {
401
401
  enforcement: mode, nodeId: _nodeId(),
402
402
  });
403
403
  _emitMetric("ai.quota.exceeded", 1);
404
- throw new AiQuotaError("aiQuota/exceeded",
404
+ throw new AiQuotaError("ai-quota/exceeded",
405
405
  "ai.quota.consume: tenant '" + tenantId + "' model '" + model +
406
406
  "' is at " + rv.used + " of " + limit + " " + dimension +
407
407
  " this " + period + "; consuming " + amount + " would exceed the budget — call refused");
@@ -432,9 +432,9 @@ function create(opts) {
432
432
 
433
433
  function check(tenantId, model) {
434
434
  validateOpts.requireNonEmptyString(tenantId,
435
- "ai.quota.check: tenantId", AiQuotaError, "aiQuota/bad-tenant");
435
+ "ai.quota.check: tenantId", AiQuotaError, "ai-quota/bad-tenant");
436
436
  validateOpts.requireNonEmptyString(model,
437
- "ai.quota.check: model", AiQuotaError, "aiQuota/bad-model");
437
+ "ai.quota.check: model", AiQuotaError, "ai-quota/bad-model");
438
438
  var now = Date.now();
439
439
  var windowStart = _windowStartFor(period, now);
440
440
  var resetsAt = _resetsAtFor(period, windowStart);
@@ -454,10 +454,10 @@ function create(opts) {
454
454
  return;
455
455
  }
456
456
  validateOpts.requireNonEmptyString(tenantId,
457
- "ai.quota.reset: tenantId", AiQuotaError, "aiQuota/bad-tenant");
457
+ "ai.quota.reset: tenantId", AiQuotaError, "ai-quota/bad-tenant");
458
458
  if (model !== undefined) {
459
459
  validateOpts.requireNonEmptyString(model,
460
- "ai.quota.reset: model", AiQuotaError, "aiQuota/bad-model");
460
+ "ai.quota.reset: model", AiQuotaError, "ai-quota/bad-model");
461
461
  store.reset(_keyFor(tenantId, model, windowStart));
462
462
  return;
463
463
  }
@@ -470,7 +470,7 @@ function create(opts) {
470
470
  for (var i = 0; i < keys.length; i++) store.reset(keys[i]);
471
471
  return;
472
472
  }
473
- throw new AiQuotaError("aiQuota/reset-unsupported",
473
+ throw new AiQuotaError("ai-quota/reset-unsupported",
474
474
  "ai.quota.reset: tenant-wide reset with an external store requires " +
475
475
  "an explicit model argument (per-key) or a store-side prefix delete");
476
476
  }
@@ -491,14 +491,14 @@ function create(opts) {
491
491
  function _validateLimitMap(map, label) {
492
492
  if (map == null) return {};
493
493
  if (typeof map !== "object" || Array.isArray(map)) {
494
- throw new AiQuotaError("aiQuota/bad-override",
494
+ throw new AiQuotaError("ai-quota/bad-override",
495
495
  "ai.quota.create: " + label + " must be a plain object { key: limit }");
496
496
  }
497
497
  var keys = Object.keys(map);
498
498
  for (var i = 0; i < keys.length; i++) {
499
499
  var v = map[keys[i]];
500
500
  if (typeof v !== "number" || !isFinite(v) || v <= 0) {
501
- throw new AiQuotaError("aiQuota/bad-override",
501
+ throw new AiQuotaError("ai-quota/bad-override",
502
502
  "ai.quota.create: " + label + "['" + keys[i] +
503
503
  "'] must be a positive finite number");
504
504
  }
@@ -512,7 +512,7 @@ function _validateStore(store) {
512
512
  typeof store.add !== "function" ||
513
513
  typeof store.get !== "function" ||
514
514
  typeof store.reset !== "function") {
515
- throw new AiQuotaError("aiQuota/bad-store",
515
+ throw new AiQuotaError("ai-quota/bad-store",
516
516
  "ai.quota.create: store must expose reserve / add / get / reset functions");
517
517
  }
518
518
  }