@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.
- package/CHANGELOG.md +4 -0
- package/lib/admin.js +42 -9
- package/lib/asset-manifest.json +1 -1
- package/lib/cart.js +24 -9
- package/lib/storefront.js +190 -38
- package/lib/vendor/MANIFEST.json +3 -3
- package/lib/vendor/blamejs/CHANGELOG.md +4 -0
- package/lib/vendor/blamejs/GOVERNANCE.md +2 -3
- package/lib/vendor/blamejs/LTS-CALENDAR.md +6 -2
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +2 -2
- package/lib/vendor/blamejs/lib/a2a.js +11 -11
- package/lib/vendor/blamejs/lib/agent-snapshot.js +1 -1
- package/lib/vendor/blamejs/lib/ai-capability.js +20 -20
- package/lib/vendor/blamejs/lib/ai-content-detect.js +4 -3
- package/lib/vendor/blamejs/lib/ai-dp.js +17 -17
- package/lib/vendor/blamejs/lib/ai-input.js +3 -3
- package/lib/vendor/blamejs/lib/ai-pref.js +9 -9
- package/lib/vendor/blamejs/lib/ai-quota.js +17 -17
- package/lib/vendor/blamejs/lib/archive-read.js +10 -7
- package/lib/vendor/blamejs/lib/arg-parser.js +38 -38
- package/lib/vendor/blamejs/lib/audit-sign.js +4 -4
- package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +4 -4
- package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +1 -1
- package/lib/vendor/blamejs/lib/auth/elevation-grant.js +10 -10
- package/lib/vendor/blamejs/lib/auth/step-up-policy.js +12 -12
- package/lib/vendor/blamejs/lib/auth/step-up.js +15 -15
- package/lib/vendor/blamejs/lib/boot-gates.js +6 -6
- package/lib/vendor/blamejs/lib/break-glass.js +1 -1
- package/lib/vendor/blamejs/lib/budr.js +6 -6
- package/lib/vendor/blamejs/lib/cms-codec.js +10 -9
- package/lib/vendor/blamejs/lib/content-credentials.js +13 -13
- package/lib/vendor/blamejs/lib/dark-patterns.js +15 -15
- package/lib/vendor/blamejs/lib/ddl-change-control.js +37 -37
- package/lib/vendor/blamejs/lib/dr-runbook.js +7 -7
- package/lib/vendor/blamejs/lib/fapi2.js +9 -9
- package/lib/vendor/blamejs/lib/fdx.js +7 -7
- package/lib/vendor/blamejs/lib/graphql-federation.js +2 -2
- package/lib/vendor/blamejs/lib/iab-mspa.js +5 -5
- package/lib/vendor/blamejs/lib/iab-tcf.js +18 -18
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +10 -6
- package/lib/vendor/blamejs/lib/mcp.js +13 -13
- package/lib/vendor/blamejs/lib/middleware/require-step-up.js +3 -3
- package/lib/vendor/blamejs/lib/mtls-ca.js +2 -2
- package/lib/vendor/blamejs/lib/safe-archive.js +8 -7
- package/lib/vendor/blamejs/lib/sec-cyber.js +3 -3
- package/lib/vendor/blamejs/lib/sse.js +14 -14
- package/lib/vendor/blamejs/lib/tcpa-10dlc.js +5 -5
- package/lib/vendor/blamejs/lib/tenant-quota.js +18 -18
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.13.43.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.13.44.json +31 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-capability.test.js +15 -15
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-dp.test.js +16 -16
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-quota.test.js +19 -19
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +52 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +6 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +7 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +5 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +4 -1
- 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
|
|
43
|
-
|
|
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
|
-
**
|
|
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 +
|
|
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
|
|
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
|
|---|---|
|
|
@@ -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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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 "
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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, "
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
|
35
|
-
* decision
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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, "
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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, "
|
|
356
|
+
"ai.quota.consume: tenantId", AiQuotaError, "ai-quota/bad-tenant");
|
|
357
357
|
validateOpts.requireNonEmptyString(model,
|
|
358
|
-
"ai.quota.consume: model", AiQuotaError, "
|
|
358
|
+
"ai.quota.consume: model", AiQuotaError, "ai-quota/bad-model");
|
|
359
359
|
if (typeof amount !== "number" || !isFinite(amount) || amount < 0) {
|
|
360
|
-
throw new AiQuotaError("
|
|
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("
|
|
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("
|
|
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, "
|
|
435
|
+
"ai.quota.check: tenantId", AiQuotaError, "ai-quota/bad-tenant");
|
|
436
436
|
validateOpts.requireNonEmptyString(model,
|
|
437
|
-
"ai.quota.check: model", AiQuotaError, "
|
|
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, "
|
|
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, "
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
}
|