@grantex/sdk 0.5.1 → 0.7.0
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/LICENSE +185 -0
- package/NOTICE +16 -0
- package/README.md +34 -2
- package/dist/authorization-details.d.ts +86 -0
- package/dist/authorization-details.d.ts.map +1 -0
- package/dist/authorization-details.js +209 -0
- package/dist/authorization-details.js.map +1 -0
- package/dist/canonical.d.ts +47 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +163 -0
- package/dist/canonical.js.map +1 -0
- package/dist/caps/index.d.ts +19 -0
- package/dist/caps/index.d.ts.map +1 -0
- package/dist/caps/index.js +19 -0
- package/dist/caps/index.js.map +1 -0
- package/dist/caps/limits.d.ts +40 -0
- package/dist/caps/limits.d.ts.map +1 -0
- package/dist/caps/limits.js +109 -0
- package/dist/caps/limits.js.map +1 -0
- package/dist/caps/memory.d.ts +15 -0
- package/dist/caps/memory.d.ts.map +1 -0
- package/dist/caps/memory.js +57 -0
- package/dist/caps/memory.js.map +1 -0
- package/dist/caps/meter.d.ts +107 -0
- package/dist/caps/meter.d.ts.map +1 -0
- package/dist/caps/meter.js +200 -0
- package/dist/caps/meter.js.map +1 -0
- package/dist/caps/postgres.d.ts +64 -0
- package/dist/caps/postgres.d.ts.map +1 -0
- package/dist/caps/postgres.js +221 -0
- package/dist/caps/postgres.js.map +1 -0
- package/dist/caps/redis.d.ts +45 -0
- package/dist/caps/redis.d.ts.map +1 -0
- package/dist/caps/redis.js +173 -0
- package/dist/caps/redis.js.map +1 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +517 -57
- package/dist/client.js.map +1 -1
- package/dist/decisions/action.d.ts +51 -0
- package/dist/decisions/action.d.ts.map +1 -0
- package/dist/decisions/action.js +237 -0
- package/dist/decisions/action.js.map +1 -0
- package/dist/decisions/index.d.ts +11 -0
- package/dist/decisions/index.d.ts.map +1 -0
- package/dist/decisions/index.js +8 -0
- package/dist/decisions/index.js.map +1 -0
- package/dist/decisions/verify.d.ts +103 -0
- package/dist/decisions/verify.d.ts.map +1 -0
- package/dist/decisions/verify.js +277 -0
- package/dist/decisions/verify.js.map +1 -0
- package/dist/denials.d.ts +132 -0
- package/dist/denials.d.ts.map +1 -0
- package/dist/denials.js +129 -0
- package/dist/denials.js.map +1 -0
- package/dist/evidence/build.d.ts +63 -0
- package/dist/evidence/build.d.ts.map +1 -0
- package/dist/evidence/build.js +201 -0
- package/dist/evidence/build.js.map +1 -0
- package/dist/evidence/canonical.d.ts +3 -0
- package/dist/evidence/canonical.d.ts.map +1 -0
- package/dist/evidence/canonical.js +3 -0
- package/dist/evidence/canonical.js.map +1 -0
- package/dist/evidence/checks.d.ts +13 -0
- package/dist/evidence/checks.d.ts.map +1 -0
- package/dist/evidence/checks.js +406 -0
- package/dist/evidence/checks.js.map +1 -0
- package/dist/evidence/document.d.ts +8 -0
- package/dist/evidence/document.d.ts.map +1 -0
- package/dist/evidence/document.js +298 -0
- package/dist/evidence/document.js.map +1 -0
- package/dist/evidence/hashing.d.ts +37 -0
- package/dist/evidence/hashing.d.ts.map +1 -0
- package/dist/evidence/hashing.js +106 -0
- package/dist/evidence/hashing.js.map +1 -0
- package/dist/evidence/index.d.ts +24 -0
- package/dist/evidence/index.d.ts.map +1 -0
- package/dist/evidence/index.js +24 -0
- package/dist/evidence/index.js.map +1 -0
- package/dist/evidence/result.d.ts +88 -0
- package/dist/evidence/result.d.ts.map +1 -0
- package/dist/evidence/result.js +88 -0
- package/dist/evidence/result.js.map +1 -0
- package/dist/evidence/schema-1.0.d.ts +2 -0
- package/dist/evidence/schema-1.0.d.ts.map +1 -0
- package/dist/evidence/schema-1.0.js +1398 -0
- package/dist/evidence/schema-1.0.js.map +1 -0
- package/dist/evidence/schema.d.ts +13 -0
- package/dist/evidence/schema.d.ts.map +1 -0
- package/dist/evidence/schema.js +264 -0
- package/dist/evidence/schema.js.map +1 -0
- package/dist/evidence/signature.d.ts +23 -0
- package/dist/evidence/signature.d.ts.map +1 -0
- package/dist/evidence/signature.js +136 -0
- package/dist/evidence/signature.js.map +1 -0
- package/dist/evidence/trace.d.ts +23 -0
- package/dist/evidence/trace.d.ts.map +1 -0
- package/dist/evidence/trace.js +69 -0
- package/dist/evidence/trace.js.map +1 -0
- package/dist/evidence/verify.d.ts +29 -0
- package/dist/evidence/verify.d.ts.map +1 -0
- package/dist/evidence/verify.js +163 -0
- package/dist/evidence/verify.js.map +1 -0
- package/dist/http.d.ts +12 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +11 -5
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +178 -9
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +379 -16
- package/dist/manifest.js.map +1 -1
- package/dist/prepaid-wallets.d.ts +21 -0
- package/dist/prepaid-wallets.d.ts.map +1 -1
- package/dist/prepaid-wallets.js +6 -0
- package/dist/prepaid-wallets.js.map +1 -1
- package/dist/purpose.d.ts +29 -0
- package/dist/purpose.d.ts.map +1 -0
- package/dist/purpose.js +60 -0
- package/dist/purpose.js.map +1 -0
- package/dist/resources/agents.js +3 -3
- package/dist/resources/agents.js.map +1 -1
- package/dist/resources/anomalies.d.ts +3 -1
- package/dist/resources/anomalies.d.ts.map +1 -1
- package/dist/resources/anomalies.js +7 -1
- package/dist/resources/anomalies.js.map +1 -1
- package/dist/resources/audit.js +1 -1
- package/dist/resources/audit.js.map +1 -1
- package/dist/resources/budgets.js +2 -2
- package/dist/resources/budgets.js.map +1 -1
- package/dist/resources/credentials.js +1 -1
- package/dist/resources/credentials.js.map +1 -1
- package/dist/resources/decisions.d.ts +69 -0
- package/dist/resources/decisions.d.ts.map +1 -0
- package/dist/resources/decisions.js +115 -0
- package/dist/resources/decisions.js.map +1 -0
- package/dist/resources/domains.js +2 -2
- package/dist/resources/domains.js.map +1 -1
- package/dist/resources/dpdp.js +4 -4
- package/dist/resources/dpdp.js.map +1 -1
- package/dist/resources/grants.js +2 -2
- package/dist/resources/grants.js.map +1 -1
- package/dist/resources/policies.js +3 -3
- package/dist/resources/policies.js.map +1 -1
- package/dist/resources/scim.js +5 -5
- package/dist/resources/scim.js.map +1 -1
- package/dist/resources/tokens.d.ts.map +1 -1
- package/dist/resources/tokens.js +5 -1
- package/dist/resources/tokens.js.map +1 -1
- package/dist/resources/vault.js +2 -2
- package/dist/resources/vault.js.map +1 -1
- package/dist/resources/webauthn.d.ts +6 -1
- package/dist/resources/webauthn.d.ts.map +1 -1
- package/dist/resources/webauthn.js +5 -1
- package/dist/resources/webauthn.js.map +1 -1
- package/dist/resources/webhooks.js +1 -1
- package/dist/resources/webhooks.js.map +1 -1
- package/dist/revocations/feed.d.ts +61 -0
- package/dist/revocations/feed.d.ts.map +1 -0
- package/dist/revocations/feed.js +282 -0
- package/dist/revocations/feed.js.map +1 -0
- package/dist/revocations/index.d.ts +20 -0
- package/dist/revocations/index.d.ts.map +1 -0
- package/dist/revocations/index.js +19 -0
- package/dist/revocations/index.js.map +1 -0
- package/dist/revocations/set.d.ts +59 -0
- package/dist/revocations/set.d.ts.map +1 -0
- package/dist/revocations/set.js +87 -0
- package/dist/revocations/set.js.map +1 -0
- package/dist/scopes.d.ts +26 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +37 -0
- package/dist/scopes.js.map +1 -0
- package/dist/types.d.ts +121 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/dist/verify.d.ts +31 -2
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +236 -29
- package/dist/verify.js.map +1 -1
- package/package.json +13 -4
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Canonicalization Scheme (RFC 8785).
|
|
3
|
+
*
|
|
4
|
+
* `canonicalize` turns a JSON value into its canonical text: object members
|
|
5
|
+
* sorted by the UTF-16 code units of their names, no insignificant
|
|
6
|
+
* whitespace, strings escaped as `JSON.stringify` escapes them and numbers in
|
|
7
|
+
* the ECMAScript `Number` format. Two values that differ only in member order,
|
|
8
|
+
* whitespace or number spelling canonicalise to the same text.
|
|
9
|
+
*
|
|
10
|
+
* The input must be I-JSON (RFC 7493). Anything without a single canonical
|
|
11
|
+
* form throws `CanonicalizationError` instead of being coerced or dropped:
|
|
12
|
+
* `NaN` and infinities; strings or member names with an unpaired surrogate;
|
|
13
|
+
* `undefined`, functions, symbols, bigints, sparse arrays and objects that
|
|
14
|
+
* are not plain objects (such as `Date` or `Map`, whose `toJSON` is not
|
|
15
|
+
* consulted); nesting deeper than `MAX_DEPTH`.
|
|
16
|
+
*
|
|
17
|
+
* The Python SDK implements the same rules in `grantex.canonical`; both are
|
|
18
|
+
* tested against the RFC 8785 test vectors and the shared fixtures in
|
|
19
|
+
* `spec/examples/canonicalization/`. Python additionally refuses integers
|
|
20
|
+
* whose digits are not the canonical form of a double (such as 2^53 + 1);
|
|
21
|
+
* a JavaScript number has already been rounded by the parser.
|
|
22
|
+
*/
|
|
23
|
+
/** Deepest nesting of arrays and objects accepted. */
|
|
24
|
+
export const MAX_DEPTH = 64;
|
|
25
|
+
/** The value has no RFC 8785 canonical form. */
|
|
26
|
+
export class CanonicalizationError extends Error {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'CanonicalizationError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const LONE_SURROGATE = /[\uD800-\uDFFF]/u;
|
|
33
|
+
/** Returns the RFC 8785 canonical JSON text of `value`. */
|
|
34
|
+
export function canonicalize(value) {
|
|
35
|
+
const out = [];
|
|
36
|
+
write(value, out, 0);
|
|
37
|
+
return out.join('');
|
|
38
|
+
}
|
|
39
|
+
/** Returns the RFC 8785 canonical JSON of `value` as UTF-8 bytes. */
|
|
40
|
+
export function canonicalizeToBytes(value) {
|
|
41
|
+
return new TextEncoder().encode(canonicalize(value));
|
|
42
|
+
}
|
|
43
|
+
/** Serialises a finite number as `Number.prototype.toString` does (`-0` as `0`). */
|
|
44
|
+
export function serializeNumber(value) {
|
|
45
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
46
|
+
throw new CanonicalizationError('NaN and infinite numbers have no JSON form');
|
|
47
|
+
}
|
|
48
|
+
// JSON.stringify applies Number::toString and writes -0 as 0.
|
|
49
|
+
return JSON.stringify(value);
|
|
50
|
+
}
|
|
51
|
+
function write(value, out, depth) {
|
|
52
|
+
if (value === null) {
|
|
53
|
+
out.push('null');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
switch (typeof value) {
|
|
57
|
+
case 'boolean':
|
|
58
|
+
out.push(value ? 'true' : 'false');
|
|
59
|
+
return;
|
|
60
|
+
case 'number':
|
|
61
|
+
out.push(serializeNumber(value));
|
|
62
|
+
return;
|
|
63
|
+
case 'string':
|
|
64
|
+
out.push(serializeString(value));
|
|
65
|
+
return;
|
|
66
|
+
case 'object':
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
throw new CanonicalizationError(`value of type ${typeof value} has no JSON form`);
|
|
70
|
+
}
|
|
71
|
+
if (depth >= MAX_DEPTH) {
|
|
72
|
+
throw new CanonicalizationError(`nesting deeper than ${MAX_DEPTH} levels`);
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
out.push('[');
|
|
76
|
+
for (let index = 0; index < value.length; index++) {
|
|
77
|
+
if (!Object.prototype.hasOwnProperty.call(value, index)) {
|
|
78
|
+
throw new CanonicalizationError('sparse arrays have no JSON form');
|
|
79
|
+
}
|
|
80
|
+
if (index > 0)
|
|
81
|
+
out.push(',');
|
|
82
|
+
write(value[index], out, depth + 1);
|
|
83
|
+
}
|
|
84
|
+
out.push(']');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const prototype = Object.getPrototypeOf(value);
|
|
88
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
89
|
+
throw new CanonicalizationError('only plain objects have a JSON form');
|
|
90
|
+
}
|
|
91
|
+
const record = value;
|
|
92
|
+
// Array.prototype.sort without a comparator orders strings by UTF-16 code units.
|
|
93
|
+
const keys = Object.keys(record).sort();
|
|
94
|
+
out.push('{');
|
|
95
|
+
keys.forEach((key, index) => {
|
|
96
|
+
if (index > 0)
|
|
97
|
+
out.push(',');
|
|
98
|
+
out.push(serializeString(key));
|
|
99
|
+
out.push(':');
|
|
100
|
+
write(record[key], out, depth + 1);
|
|
101
|
+
});
|
|
102
|
+
out.push('}');
|
|
103
|
+
}
|
|
104
|
+
function serializeString(value) {
|
|
105
|
+
if (LONE_SURROGATE.test(value)) {
|
|
106
|
+
throw new CanonicalizationError('string contains an unpaired surrogate');
|
|
107
|
+
}
|
|
108
|
+
// For well-formed strings JSON.stringify escapes exactly as RFC 8785 requires:
|
|
109
|
+
// \b \t \n \f \r \" \\ as two-character escapes, other C0 controls as \u00xx.
|
|
110
|
+
return JSON.stringify(value);
|
|
111
|
+
}
|
|
112
|
+
/** A JSON text repeats a member name within one object. */
|
|
113
|
+
export class DuplicateKeyError extends CanonicalizationError {
|
|
114
|
+
key;
|
|
115
|
+
constructor(key) {
|
|
116
|
+
super(`duplicate member name ${JSON.stringify(key)}`);
|
|
117
|
+
this.name = 'DuplicateKeyError';
|
|
118
|
+
this.key = key;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parses JSON text like `JSON.parse`, but throws `DuplicateKeyError` when an
|
|
123
|
+
* object repeats a member name (after unescaping), instead of silently keeping
|
|
124
|
+
* the last value. Use it wherever the raw text of a value to canonicalise is
|
|
125
|
+
* available.
|
|
126
|
+
*/
|
|
127
|
+
export function parseJsonRejectingDuplicates(text) {
|
|
128
|
+
const value = JSON.parse(text);
|
|
129
|
+
const stack = [];
|
|
130
|
+
let i = 0;
|
|
131
|
+
while (i < text.length) {
|
|
132
|
+
const ch = text[i];
|
|
133
|
+
if (ch === '"') {
|
|
134
|
+
let j = i + 1;
|
|
135
|
+
while (j < text.length && text[j] !== '"')
|
|
136
|
+
j += text[j] === '\\' ? 2 : 1;
|
|
137
|
+
const top = stack[stack.length - 1];
|
|
138
|
+
if (top?.keys && top.expectKey) {
|
|
139
|
+
const key = JSON.parse(text.slice(i, j + 1));
|
|
140
|
+
if (top.keys.has(key))
|
|
141
|
+
throw new DuplicateKeyError(key);
|
|
142
|
+
top.keys.add(key);
|
|
143
|
+
top.expectKey = false;
|
|
144
|
+
}
|
|
145
|
+
i = j + 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (ch === '{')
|
|
149
|
+
stack.push({ keys: new Set(), expectKey: true });
|
|
150
|
+
else if (ch === '[')
|
|
151
|
+
stack.push({ keys: null, expectKey: false });
|
|
152
|
+
else if (ch === '}' || ch === ']')
|
|
153
|
+
stack.pop();
|
|
154
|
+
else if (ch === ',') {
|
|
155
|
+
const top = stack[stack.length - 1];
|
|
156
|
+
if (top?.keys)
|
|
157
|
+
top.expectKey = true;
|
|
158
|
+
}
|
|
159
|
+
i += 1;
|
|
160
|
+
}
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=canonical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call caps and cost-unit budgets for tool calls.
|
|
3
|
+
*
|
|
4
|
+
* `CapsMeter` reserves units against every applicable counter atomically,
|
|
5
|
+
* before the call that incurs cost. Backends: `RedisCapsBackend` (Lua script,
|
|
6
|
+
* tenant-scoped keys; preferred), `PostgresCapsBackend` (row locks and upserts,
|
|
7
|
+
* for deployments without Redis) and `InMemoryCapsBackend` (**tests only**).
|
|
8
|
+
*
|
|
9
|
+
* There is no automatic failover between backends: two stores would keep two
|
|
10
|
+
* sets of counters and concurrent calls could exceed a cap. If the configured
|
|
11
|
+
* backend is unavailable the meter throws `MeterUnavailableError` and
|
|
12
|
+
* `enforce()` denies. See docs/concepts/caps-and-metering.md.
|
|
13
|
+
*/
|
|
14
|
+
export { CAPS_MODES, CAP_ERROR_CODE, CapExceededError, CapsConfigurationError, CapsMeter, MAX_COUNT, MeterUnavailableError, WINDOW_MS, counterKey, resolveLimits, tenantHash, type CapLimit, type CapWindow, type CapsMode, type CapsBackend, type CounterUsage, type Reservation, type ResolvedCapLimit, } from './meter.js';
|
|
15
|
+
export { InMemoryCapsBackend } from './memory.js';
|
|
16
|
+
export { REFUND_SCRIPT, RESERVE_SCRIPT, USAGE_SCRIPT, RedisCapsBackend, ioredisRunner, type RedisScriptRunner, } from './redis.js';
|
|
17
|
+
export { CAPS_SCHEMA_SQL, PostgresCapsBackend, type PgClientLike, type PgPoolLike } from './postgres.js';
|
|
18
|
+
export { CASE_REQUIRED, INVALID_CASE_ID, INVALID_COST_COMPONENT, MALFORMED_GRANT_CAPS, buildCapLimits, counterId, parseGrantCaps, type BuildCapLimitsOptions, } from './limits.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/caps/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,SAAS,EACT,SAAS,EACT,qBAAqB,EACrB,SAAS,EACT,UAAU,EACV,aAAa,EACb,UAAU,EACV,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EACL,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,cAAc,EACd,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call caps and cost-unit budgets for tool calls.
|
|
3
|
+
*
|
|
4
|
+
* `CapsMeter` reserves units against every applicable counter atomically,
|
|
5
|
+
* before the call that incurs cost. Backends: `RedisCapsBackend` (Lua script,
|
|
6
|
+
* tenant-scoped keys; preferred), `PostgresCapsBackend` (row locks and upserts,
|
|
7
|
+
* for deployments without Redis) and `InMemoryCapsBackend` (**tests only**).
|
|
8
|
+
*
|
|
9
|
+
* There is no automatic failover between backends: two stores would keep two
|
|
10
|
+
* sets of counters and concurrent calls could exceed a cap. If the configured
|
|
11
|
+
* backend is unavailable the meter throws `MeterUnavailableError` and
|
|
12
|
+
* `enforce()` denies. See docs/concepts/caps-and-metering.md.
|
|
13
|
+
*/
|
|
14
|
+
export { CAPS_MODES, CAP_ERROR_CODE, CapExceededError, CapsConfigurationError, CapsMeter, MAX_COUNT, MeterUnavailableError, WINDOW_MS, counterKey, resolveLimits, tenantHash, } from './meter.js';
|
|
15
|
+
export { InMemoryCapsBackend } from './memory.js';
|
|
16
|
+
export { REFUND_SCRIPT, RESERVE_SCRIPT, USAGE_SCRIPT, RedisCapsBackend, ioredisRunner, } from './redis.js';
|
|
17
|
+
export { CAPS_SCHEMA_SQL, PostgresCapsBackend } from './postgres.js';
|
|
18
|
+
export { CASE_REQUIRED, INVALID_CASE_ID, INVALID_COST_COMPONENT, MALFORMED_GRANT_CAPS, buildCapLimits, counterId, parseGrantCaps, } from './limits.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/caps/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,SAAS,EACT,SAAS,EACT,qBAAqB,EACrB,SAAS,EACT,UAAU,EACV,aAAa,EACb,UAAU,GAQX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,aAAa,GAEd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAsC,MAAM,eAAe,CAAC;AACzG,OAAO,EACL,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,SAAS,EACT,cAAc,GAEf,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive the counters a tool call is metered against. Mirrors the Python SDK's
|
|
3
|
+
* `grantex.caps._limits`.
|
|
4
|
+
*
|
|
5
|
+
* - The **manifest** (`caps` on the tool) declares tenant-wide counters per tool.
|
|
6
|
+
* - The **grant** (`caps` in its `urn:grantex:tools:v1` entry) declares counters
|
|
7
|
+
* for that grant only: per tool (`{"verify_business": {"per_hour": 50}}`)
|
|
8
|
+
* and a connector cost-unit budget (`{"cost_units": {"per_day": 5000}}`).
|
|
9
|
+
*
|
|
10
|
+
* Every declared cap is its own counter and all of them must hold. A call's
|
|
11
|
+
* cost is the sum of the manifest `cost_units` for the components it incurs
|
|
12
|
+
* (all declared components unless the caller names them).
|
|
13
|
+
*/
|
|
14
|
+
import type { ToolSpec } from '../manifest.js';
|
|
15
|
+
import { type CapLimit, type CapWindow } from './meter.js';
|
|
16
|
+
export declare const CASE_REQUIRED = "case_required";
|
|
17
|
+
export declare const INVALID_CASE_ID = "invalid_case_id";
|
|
18
|
+
export declare const INVALID_COST_COMPONENT = "invalid_cost_component";
|
|
19
|
+
export declare const MALFORMED_GRANT_CAPS = "malformed_grant_caps";
|
|
20
|
+
/** Unambiguous counter identity; identical to the Python SDK's `counter_id`. */
|
|
21
|
+
export declare function counterId(...parts: string[]): string;
|
|
22
|
+
/** Validate a grant entry's `caps`; `cost_units` is the connector budget. */
|
|
23
|
+
export declare function parseGrantCaps(caps: unknown): Map<string, Partial<Record<CapWindow, number>>>;
|
|
24
|
+
export interface BuildCapLimitsOptions {
|
|
25
|
+
connector: string;
|
|
26
|
+
tool: string;
|
|
27
|
+
spec: ToolSpec;
|
|
28
|
+
grantId: string;
|
|
29
|
+
grantCaps?: unknown;
|
|
30
|
+
caseId?: string;
|
|
31
|
+
costComponents?: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The limits for one call of `tool`.
|
|
35
|
+
*
|
|
36
|
+
* @throws {CapsConfigurationError} with `subReason` `case_required`,
|
|
37
|
+
* `invalid_case_id`, `invalid_cost_component` or `malformed_grant_caps`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildCapLimits(options: BuildCapLimitsOptions): CapLimit[];
|
|
40
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/caps/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAqC,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAK9F,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAC7C,eAAO,MAAM,eAAe,oBAAoB,CAAC;AACjD,eAAO,MAAM,sBAAsB,2BAA2B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEpD;AAuBD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAM7F;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,QAAQ,EAAE,CAuDzE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive the counters a tool call is metered against. Mirrors the Python SDK's
|
|
3
|
+
* `grantex.caps._limits`.
|
|
4
|
+
*
|
|
5
|
+
* - The **manifest** (`caps` on the tool) declares tenant-wide counters per tool.
|
|
6
|
+
* - The **grant** (`caps` in its `urn:grantex:tools:v1` entry) declares counters
|
|
7
|
+
* for that grant only: per tool (`{"verify_business": {"per_hour": 50}}`)
|
|
8
|
+
* and a connector cost-unit budget (`{"cost_units": {"per_day": 5000}}`).
|
|
9
|
+
*
|
|
10
|
+
* Every declared cap is its own counter and all of them must hold. A call's
|
|
11
|
+
* cost is the sum of the manifest `cost_units` for the components it incurs
|
|
12
|
+
* (all declared components unless the caller names them).
|
|
13
|
+
*/
|
|
14
|
+
import { CapsConfigurationError, MAX_COUNT } from './meter.js';
|
|
15
|
+
const WINDOWS = ['per_hour', 'per_day', 'per_case'];
|
|
16
|
+
const MAX_ID = 256;
|
|
17
|
+
export const CASE_REQUIRED = 'case_required';
|
|
18
|
+
export const INVALID_CASE_ID = 'invalid_case_id';
|
|
19
|
+
export const INVALID_COST_COMPONENT = 'invalid_cost_component';
|
|
20
|
+
export const MALFORMED_GRANT_CAPS = 'malformed_grant_caps';
|
|
21
|
+
/** Unambiguous counter identity; identical to the Python SDK's `counter_id`. */
|
|
22
|
+
export function counterId(...parts) {
|
|
23
|
+
return JSON.stringify(parts);
|
|
24
|
+
}
|
|
25
|
+
function isPlainObject(value) {
|
|
26
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function windows(raw, where) {
|
|
29
|
+
if (!isPlainObject(raw) || Object.keys(raw).length === 0) {
|
|
30
|
+
throw new CapsConfigurationError(`${where} must be a non-empty object`, MALFORMED_GRANT_CAPS);
|
|
31
|
+
}
|
|
32
|
+
const out = {};
|
|
33
|
+
for (const [window, value] of Object.entries(raw)) {
|
|
34
|
+
if (!WINDOWS.includes(window)) {
|
|
35
|
+
throw new CapsConfigurationError(`${where} has unknown window ${JSON.stringify(window)}`, MALFORMED_GRANT_CAPS);
|
|
36
|
+
}
|
|
37
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0 || value > MAX_COUNT) {
|
|
38
|
+
throw new CapsConfigurationError(`${where}.${window} must be an integer between 0 and ${MAX_COUNT}`, MALFORMED_GRANT_CAPS);
|
|
39
|
+
}
|
|
40
|
+
out[window] = value;
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
/** Validate a grant entry's `caps`; `cost_units` is the connector budget. */
|
|
45
|
+
export function parseGrantCaps(caps) {
|
|
46
|
+
const out = new Map();
|
|
47
|
+
if (caps === undefined || caps === null)
|
|
48
|
+
return out;
|
|
49
|
+
if (!isPlainObject(caps))
|
|
50
|
+
throw new CapsConfigurationError('grant caps must be an object', MALFORMED_GRANT_CAPS);
|
|
51
|
+
for (const [name, value] of Object.entries(caps))
|
|
52
|
+
out.set(name, windows(value, `caps.${name}`));
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The limits for one call of `tool`.
|
|
57
|
+
*
|
|
58
|
+
* @throws {CapsConfigurationError} with `subReason` `case_required`,
|
|
59
|
+
* `invalid_case_id`, `invalid_cost_component` or `malformed_grant_caps`.
|
|
60
|
+
*/
|
|
61
|
+
export function buildCapLimits(options) {
|
|
62
|
+
const { connector, tool, spec, grantId, caseId, costComponents } = options;
|
|
63
|
+
if (caseId !== undefined && (typeof caseId !== 'string' || caseId.length === 0 || caseId.length > MAX_ID)) {
|
|
64
|
+
throw new CapsConfigurationError('caseId must be a non-empty string of at most 256 characters', INVALID_CASE_ID);
|
|
65
|
+
}
|
|
66
|
+
const declared = { ...(spec.costUnits ?? {}) };
|
|
67
|
+
const components = costComponents === undefined ? Object.keys(declared) : [...costComponents];
|
|
68
|
+
if (costComponents !== undefined
|
|
69
|
+
&& (new Set(components).size !== components.length || components.some((c) => !Object.prototype.hasOwnProperty.call(declared, c)))) {
|
|
70
|
+
throw new CapsConfigurationError(`cost components ${JSON.stringify(components)} are not all declared by tool ${JSON.stringify(tool)}`, INVALID_COST_COMPONENT);
|
|
71
|
+
}
|
|
72
|
+
if (costComponents !== undefined && Object.keys(declared).length > 0 && components.length === 0) {
|
|
73
|
+
// A call to a tool that declares cost units incurs at least one of them;
|
|
74
|
+
// an empty list would skip the budget entirely.
|
|
75
|
+
throw new CapsConfigurationError(`tool ${JSON.stringify(tool)} declares cost units, so costComponents cannot be empty`, INVALID_COST_COMPONENT);
|
|
76
|
+
}
|
|
77
|
+
const cost = components.reduce((sum, c) => sum + declared[c], 0);
|
|
78
|
+
if (cost > MAX_COUNT) {
|
|
79
|
+
throw new CapsConfigurationError(`the cost of this call (${cost} units) exceeds ${MAX_COUNT}`, INVALID_COST_COMPONENT);
|
|
80
|
+
}
|
|
81
|
+
const grant = parseGrantCaps(options.grantCaps);
|
|
82
|
+
const limits = [];
|
|
83
|
+
const add = (scopeParts, caps, units, scope, kind) => {
|
|
84
|
+
for (const window of WINDOWS) {
|
|
85
|
+
const limit = caps[window];
|
|
86
|
+
if (limit === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
const parts = [...scopeParts, kind, window];
|
|
89
|
+
if (window === 'per_case') {
|
|
90
|
+
if (caseId === undefined) {
|
|
91
|
+
throw new CapsConfigurationError(`${scope} per_case cap on ${connector}.${tool} needs a caseId`, CASE_REQUIRED);
|
|
92
|
+
}
|
|
93
|
+
parts.push(caseId);
|
|
94
|
+
}
|
|
95
|
+
limits.push({ counter: counterId(...parts), limit, window, units, scope, kind });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (spec.caps !== undefined) {
|
|
99
|
+
add(['manifest', connector, tool], { per_hour: spec.caps.perHour, per_day: spec.caps.perDay, per_case: spec.caps.perCase }, 1, 'manifest', 'calls');
|
|
100
|
+
}
|
|
101
|
+
const toolCaps = grant.get(tool);
|
|
102
|
+
if (toolCaps !== undefined && tool !== 'cost_units')
|
|
103
|
+
add(['grant', grantId, connector, tool], toolCaps, 1, 'grant', 'calls');
|
|
104
|
+
const budget = grant.get('cost_units');
|
|
105
|
+
if (cost > 0 && budget !== undefined)
|
|
106
|
+
add(['grant', grantId, connector], budget, cost, 'grant', 'cost_units');
|
|
107
|
+
return limits;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/caps/limits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process caps backend. **For tests only.**
|
|
3
|
+
*
|
|
4
|
+
* Counters live in one process's memory: they are not shared between workers,
|
|
5
|
+
* are lost on restart, and would let a multi-process deployment exceed every
|
|
6
|
+
* cap. Use the Redis or Postgres backend in any deployment.
|
|
7
|
+
*/
|
|
8
|
+
import { type CapsBackend, type ResolvedCapLimit } from './meter.js';
|
|
9
|
+
export declare class InMemoryCapsBackend implements CapsBackend {
|
|
10
|
+
#private;
|
|
11
|
+
reserve(tenantId: string, reservationId: string, limits: readonly ResolvedCapLimit[], nowMs: number | undefined): Promise<void>;
|
|
12
|
+
refund(tenantId: string, reservationId: string, limits: readonly ResolvedCapLimit[]): Promise<void>;
|
|
13
|
+
usage(tenantId: string, limit: ResolvedCapLimit, nowMs: number | undefined): Promise<number>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/caps/memory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA2C,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAQ9G,qBAAa,mBAAoB,YAAW,WAAW;;IAkB/C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBpI;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxG;IAEK,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjG;CACF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process caps backend. **For tests only.**
|
|
3
|
+
*
|
|
4
|
+
* Counters live in one process's memory: they are not shared between workers,
|
|
5
|
+
* are lost on restart, and would let a multi-process deployment exceed every
|
|
6
|
+
* cap. Use the Redis or Postgres backend in any deployment.
|
|
7
|
+
*/
|
|
8
|
+
import { CapExceededError, WINDOW_MS, tenantHash } from './meter.js';
|
|
9
|
+
export class InMemoryCapsBackend {
|
|
10
|
+
#counters = new Map();
|
|
11
|
+
async #entries(tenantId, limit, now) {
|
|
12
|
+
const id = `${await tenantHash(tenantId)}:${limit.key}`;
|
|
13
|
+
let entries = this.#counters.get(id);
|
|
14
|
+
if (entries === undefined) {
|
|
15
|
+
entries = [];
|
|
16
|
+
this.#counters.set(id, entries);
|
|
17
|
+
}
|
|
18
|
+
const window = WINDOW_MS[limit.window];
|
|
19
|
+
if (window > 0) {
|
|
20
|
+
const live = entries.filter((e) => e.at > now - window);
|
|
21
|
+
entries.splice(0, entries.length, ...live);
|
|
22
|
+
}
|
|
23
|
+
return entries;
|
|
24
|
+
}
|
|
25
|
+
async reserve(tenantId, reservationId, limits, nowMs) {
|
|
26
|
+
const now = nowMs ?? Date.now();
|
|
27
|
+
// Resolve every counter before checking, then check and record with no
|
|
28
|
+
// await in between: JavaScript runs this block without interleaving.
|
|
29
|
+
const all = [];
|
|
30
|
+
for (const limit of limits)
|
|
31
|
+
all.push(await this.#entries(tenantId, limit, now));
|
|
32
|
+
limits.forEach((limit, i) => {
|
|
33
|
+
const used = all[i].reduce((sum, e) => sum + e.units, 0);
|
|
34
|
+
if (used + limit.units > limit.limit) {
|
|
35
|
+
throw new CapExceededError({
|
|
36
|
+
limit: limit.limit, window: limit.window, used, requested: limit.units, scope: limit.scope, kind: limit.kind,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
limits.forEach((limit, i) => {
|
|
41
|
+
all[i].push({ at: now, reservationId, units: limit.units });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async refund(tenantId, reservationId, limits) {
|
|
45
|
+
for (const limit of limits) {
|
|
46
|
+
const entries = this.#counters.get(`${await tenantHash(tenantId)}:${limit.key}`);
|
|
47
|
+
if (entries === undefined)
|
|
48
|
+
continue;
|
|
49
|
+
const kept = entries.filter((e) => e.reservationId !== reservationId);
|
|
50
|
+
entries.splice(0, entries.length, ...kept);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async usage(tenantId, limit, nowMs) {
|
|
54
|
+
return (await this.#entries(tenantId, limit, nowMs ?? Date.now())).reduce((sum, e) => sum + e.units, 0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/caps/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caps meter: atomic reservation of call caps and cost-unit budgets.
|
|
3
|
+
* Mirrors the Python SDK's `grantex.caps`; both address the same counters.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CAP_ERROR_CODE = "E1008";
|
|
6
|
+
export type CapWindow = 'per_hour' | 'per_day' | 'per_case';
|
|
7
|
+
/** `off` skips caps, `warn` allows over-cap calls and reports them, `enforce` denies them. */
|
|
8
|
+
export type CapsMode = 'off' | 'warn' | 'enforce';
|
|
9
|
+
export declare const CAPS_MODES: readonly CapsMode[];
|
|
10
|
+
/** Rolling window length per window name; `per_case` has no time window. */
|
|
11
|
+
export declare const WINDOW_MS: Readonly<Record<CapWindow, number>>;
|
|
12
|
+
export declare const MAX_COUNT = 2147483647;
|
|
13
|
+
/** One counter a call is metered against. */
|
|
14
|
+
export interface CapLimit {
|
|
15
|
+
/** Counter identity within a tenant; per-case counters must include the case. */
|
|
16
|
+
counter: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
window: CapWindow;
|
|
19
|
+
/** Units this call consumes (default 1). */
|
|
20
|
+
units?: number;
|
|
21
|
+
/** `manifest` or `grant` (default `manifest`). */
|
|
22
|
+
scope?: string;
|
|
23
|
+
/** `calls` or `cost_units` (default `calls`). */
|
|
24
|
+
kind?: string;
|
|
25
|
+
}
|
|
26
|
+
/** A validated limit with defaults applied. */
|
|
27
|
+
export interface ResolvedCapLimit extends Required<CapLimit> {
|
|
28
|
+
/** Stable, fixed-length storage key (SHA-256 hex). */
|
|
29
|
+
key: string;
|
|
30
|
+
}
|
|
31
|
+
/** Units reserved for one call. Keep it to refund a call proven unsent. */
|
|
32
|
+
export interface Reservation {
|
|
33
|
+
reservationId: string;
|
|
34
|
+
tenantId: string;
|
|
35
|
+
limits: readonly ResolvedCapLimit[];
|
|
36
|
+
}
|
|
37
|
+
export interface CounterUsage {
|
|
38
|
+
limit: ResolvedCapLimit;
|
|
39
|
+
used: number;
|
|
40
|
+
remaining: number;
|
|
41
|
+
}
|
|
42
|
+
/** A reservation would exceed a cap. `code` is `E1008`. */
|
|
43
|
+
export declare class CapExceededError extends Error {
|
|
44
|
+
readonly code = "E1008";
|
|
45
|
+
readonly reason = "cap_exceeded";
|
|
46
|
+
readonly limit: number;
|
|
47
|
+
readonly window: CapWindow;
|
|
48
|
+
readonly used: number;
|
|
49
|
+
readonly requested: number;
|
|
50
|
+
readonly scope: string;
|
|
51
|
+
readonly kind: string;
|
|
52
|
+
constructor(args: {
|
|
53
|
+
limit: number;
|
|
54
|
+
window: CapWindow;
|
|
55
|
+
used: number;
|
|
56
|
+
requested: number;
|
|
57
|
+
scope: string;
|
|
58
|
+
kind: string;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** The caps backend could not answer. Callers must treat the call as denied. */
|
|
62
|
+
export declare class MeterUnavailableError extends Error {
|
|
63
|
+
constructor(message: string, options?: {
|
|
64
|
+
cause?: unknown;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Limits or arguments are invalid. `subReason` names why. */
|
|
68
|
+
export declare class CapsConfigurationError extends Error {
|
|
69
|
+
readonly subReason: string;
|
|
70
|
+
constructor(message: string, subReason?: string);
|
|
71
|
+
}
|
|
72
|
+
/** Storage for counters. Implementations must be atomic across all limits. */
|
|
73
|
+
export interface CapsBackend {
|
|
74
|
+
/** Reserve every limit or none; throw `CapExceededError` naming the first limit exceeded. */
|
|
75
|
+
reserve(tenantId: string, reservationId: string, limits: readonly ResolvedCapLimit[], nowMs: number | undefined): Promise<void>;
|
|
76
|
+
/** Remove a reservation. Idempotent. */
|
|
77
|
+
refund(tenantId: string, reservationId: string, limits: readonly ResolvedCapLimit[], nowMs: number | undefined): Promise<void>;
|
|
78
|
+
/** Units currently held by the counter within its window. */
|
|
79
|
+
usage(tenantId: string, limit: ResolvedCapLimit, nowMs: number | undefined): Promise<number>;
|
|
80
|
+
}
|
|
81
|
+
/** Storage key for a counter; identical to the Python SDK's `CapLimit.key()`. */
|
|
82
|
+
export declare function counterKey(window: CapWindow, counter: string): Promise<string>;
|
|
83
|
+
/** Fixed-length tenant component for storage keys. */
|
|
84
|
+
export declare function tenantHash(tenantId: string): Promise<string>;
|
|
85
|
+
/** Validate a reservation's arguments and apply defaults. */
|
|
86
|
+
export declare function resolveLimits(tenantId: unknown, limits: readonly CapLimit[]): Promise<ResolvedCapLimit[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Reserve call caps and cost-unit budgets before the call that incurs cost.
|
|
89
|
+
*
|
|
90
|
+
* `reserve()` checks every limit and records the call atomically, so
|
|
91
|
+
* concurrent callers can never exceed a cap. A failed call is **not** refunded
|
|
92
|
+
* by default: a timeout or error does not prove the provider did no work. Call
|
|
93
|
+
* `refundUnsent()` only when the provider call is known not to have been sent.
|
|
94
|
+
* Any backend failure throws `MeterUnavailableError`; callers must deny.
|
|
95
|
+
*/
|
|
96
|
+
export declare class CapsMeter {
|
|
97
|
+
#private;
|
|
98
|
+
constructor(backend: CapsBackend, options?: {
|
|
99
|
+
clock?: () => number;
|
|
100
|
+
});
|
|
101
|
+
reserve(tenantId: string, limits: readonly CapLimit[]): Promise<Reservation>;
|
|
102
|
+
/** Release a reservation whose provider call was proven not sent. */
|
|
103
|
+
refundUnsent(reservation: Reservation): Promise<void>;
|
|
104
|
+
/** Current usage of each counter, for display (consent page, case view). */
|
|
105
|
+
usage(tenantId: string, limits: readonly CapLimit[]): Promise<CounterUsage[]>;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.d.ts","sourceRoot":"","sources":["../../src/caps/meter.ts"],"names":[],"mappings":"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"}
|