@grantex/sdk 0.6.0 → 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 -185
- package/NOTICE +16 -16
- package/README.md +974 -947
- 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/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 +92 -84
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `urn:grantex:tools:v1` entries from a grant's `authorization_details`.
|
|
3
|
+
*
|
|
4
|
+
* This is the only place that interprets the `authorization_details` claim
|
|
5
|
+
* (RFC 9396), so a change to the token format touches one function. Shape:
|
|
6
|
+
*
|
|
7
|
+
* ```json
|
|
8
|
+
* "authorization_details": [{
|
|
9
|
+
* "type": "urn:grantex:tools:v1",
|
|
10
|
+
* "connector": "acme_kyb",
|
|
11
|
+
* "purpose": "aml.cdd.onboarding",
|
|
12
|
+
* "data_region": "eu",
|
|
13
|
+
* "tools": ["resolve_business", "verify_business", "screen_*"],
|
|
14
|
+
* "caps": {"verify_business": {"per_hour": 50, "per_case": 3},
|
|
15
|
+
* "cost_units": {"per_day": 5000}}
|
|
16
|
+
* }]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Entries of other types are separate authorizations and are ignored here. A
|
|
20
|
+
* tools entry constrains calls on its connector in addition to the grant's
|
|
21
|
+
* scopes. Anything ambiguous (a claim that is not an array, an entry without
|
|
22
|
+
* a string `type`, a tools entry with an unknown key, a value of the wrong
|
|
23
|
+
* type, or two tools entries for one connector) throws
|
|
24
|
+
* `AuthorizationDetailsError`, and `enforce()` denies. Mirrors the Python
|
|
25
|
+
* SDK's `grantex._authorization_details`.
|
|
26
|
+
*
|
|
27
|
+
* `caps` is validated here, for every call on the connector: each key is an
|
|
28
|
+
* exact tool name or `cost_units` (the connector's cost-unit budget), never a
|
|
29
|
+
* wildcard, and each value is a non-empty object of `per_hour`, `per_day` and
|
|
30
|
+
* `per_case` counts from 0 to 2147483647.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TOOLS_DETAIL_TYPE = "urn:grantex:tools:v1";
|
|
33
|
+
declare const CAP_WINDOWS: readonly ['per_hour', 'per_day', 'per_case'];
|
|
34
|
+
/** Key of the connector cost-unit budget in `caps`; reserved as a tool name. */
|
|
35
|
+
export declare const COST_UNITS_KEY = "cost_units";
|
|
36
|
+
export type CapWindowCounts = Partial<Record<(typeof CAP_WINDOWS)[number], number>>;
|
|
37
|
+
export declare class AuthorizationDetailsError extends Error {
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
/** One `urn:grantex:tools:v1` entry. */
|
|
41
|
+
export interface ToolsAuthorization {
|
|
42
|
+
connector: string;
|
|
43
|
+
/** The grant's purpose for this connector, exactly as issued (not validated). */
|
|
44
|
+
purpose?: string;
|
|
45
|
+
dataRegion?: string;
|
|
46
|
+
/** Tool names, or prefixes ending in `*`; absent means every tool. */
|
|
47
|
+
tools?: readonly string[];
|
|
48
|
+
/** Validated caps: tool name (or `cost_units`) to window to count. */
|
|
49
|
+
caps?: Readonly<Record<string, Readonly<CapWindowCounts>>>;
|
|
50
|
+
}
|
|
51
|
+
/** Whether an entry's `tools` list (when present) names `tool`. */
|
|
52
|
+
export declare function toolsAuthorizationAllows(entry: ToolsAuthorization, tool: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Return the tools entries of an `authorization_details` claim by connector.
|
|
55
|
+
* `undefined` or `null` (no claim) yields an empty map.
|
|
56
|
+
*
|
|
57
|
+
* @throws {AuthorizationDetailsError} the claim is malformed or ambiguous.
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseToolsAuthorization(claim: unknown): Map<string, ToolsAuthorization>;
|
|
60
|
+
export declare const DECISION_DETAIL_TYPE = "urn:grantex:decision:v1";
|
|
61
|
+
/**
|
|
62
|
+
* One `urn:grantex:decision:v1` entry: tools on a connector that need a
|
|
63
|
+
* decision grant, and the decisions on each that need two approvers.
|
|
64
|
+
*
|
|
65
|
+
* ```json
|
|
66
|
+
* {"type": "urn:grantex:decision:v1", "connector": "acme_kyb",
|
|
67
|
+
* "tools": ["case_decision"], "four_eyes_on": {"case_decision": ["decline"]}}
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export interface DecisionReference {
|
|
71
|
+
connector: string;
|
|
72
|
+
tools: readonly string[];
|
|
73
|
+
fourEyesOn: Readonly<Record<string, readonly string[]>>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Return the decision references of an `authorization_details` claim by
|
|
77
|
+
* connector. `undefined` or `null` yields an empty map. Entries of other types
|
|
78
|
+
* are ignored.
|
|
79
|
+
*
|
|
80
|
+
* @throws {AuthorizationDetailsError} an entry is malformed: an unknown key, no
|
|
81
|
+
* tools, a tool name that is not a name, `four_eyes_on` naming a tool not in
|
|
82
|
+
* `tools`, or two entries for one connector.
|
|
83
|
+
*/
|
|
84
|
+
export declare function parseDecisionReferences(claim: unknown): Map<string, DecisionReference>;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=authorization-details.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization-details.d.ts","sourceRoot":"","sources":["../src/authorization-details.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,eAAO,MAAM,iBAAiB,yBAAyB,CAAC;AAKxD,QAAA,MAAM,WAAW,YAAI,UAAU,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAGjE,gFAAgF;AAChF,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpF,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,sEAAsE;IACtE,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAC5D;AAmCD,mEAAmE;AACnE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGzF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAmDvF;AAED,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAM9D;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;CACzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAsDtF"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `urn:grantex:tools:v1` entries from a grant's `authorization_details`.
|
|
3
|
+
*
|
|
4
|
+
* This is the only place that interprets the `authorization_details` claim
|
|
5
|
+
* (RFC 9396), so a change to the token format touches one function. Shape:
|
|
6
|
+
*
|
|
7
|
+
* ```json
|
|
8
|
+
* "authorization_details": [{
|
|
9
|
+
* "type": "urn:grantex:tools:v1",
|
|
10
|
+
* "connector": "acme_kyb",
|
|
11
|
+
* "purpose": "aml.cdd.onboarding",
|
|
12
|
+
* "data_region": "eu",
|
|
13
|
+
* "tools": ["resolve_business", "verify_business", "screen_*"],
|
|
14
|
+
* "caps": {"verify_business": {"per_hour": 50, "per_case": 3},
|
|
15
|
+
* "cost_units": {"per_day": 5000}}
|
|
16
|
+
* }]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Entries of other types are separate authorizations and are ignored here. A
|
|
20
|
+
* tools entry constrains calls on its connector in addition to the grant's
|
|
21
|
+
* scopes. Anything ambiguous (a claim that is not an array, an entry without
|
|
22
|
+
* a string `type`, a tools entry with an unknown key, a value of the wrong
|
|
23
|
+
* type, or two tools entries for one connector) throws
|
|
24
|
+
* `AuthorizationDetailsError`, and `enforce()` denies. Mirrors the Python
|
|
25
|
+
* SDK's `grantex._authorization_details`.
|
|
26
|
+
*
|
|
27
|
+
* `caps` is validated here, for every call on the connector: each key is an
|
|
28
|
+
* exact tool name or `cost_units` (the connector's cost-unit budget), never a
|
|
29
|
+
* wildcard, and each value is a non-empty object of `per_hour`, `per_day` and
|
|
30
|
+
* `per_case` counts from 0 to 2147483647.
|
|
31
|
+
*/
|
|
32
|
+
export const TOOLS_DETAIL_TYPE = 'urn:grantex:tools:v1';
|
|
33
|
+
const ENTRY_KEYS = new Set(['type', 'connector', 'purpose', 'data_region', 'tools', 'caps']);
|
|
34
|
+
const NAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
35
|
+
const TOOL_PATTERN_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}\*?$/;
|
|
36
|
+
const CAP_WINDOWS = ['per_hour', 'per_day', 'per_case'];
|
|
37
|
+
const MAX_COUNT = 2147483647;
|
|
38
|
+
/** Key of the connector cost-unit budget in `caps`; reserved as a tool name. */
|
|
39
|
+
export const COST_UNITS_KEY = 'cost_units';
|
|
40
|
+
export class AuthorizationDetailsError extends Error {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = 'AuthorizationDetailsError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isPlainObject(value) {
|
|
47
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
function parseCaps(raw, where) {
|
|
50
|
+
if (!isPlainObject(raw) || Object.keys(raw).length === 0) {
|
|
51
|
+
throw new AuthorizationDetailsError(`${where} must be a non-empty object`);
|
|
52
|
+
}
|
|
53
|
+
const parsed = {};
|
|
54
|
+
for (const [name, windows] of Object.entries(raw)) {
|
|
55
|
+
if (!(name === COST_UNITS_KEY || NAME_RE.test(name))) {
|
|
56
|
+
throw new AuthorizationDetailsError(`${where} key "${name}" must be an exact tool name or cost_units; wildcards are not allowed`);
|
|
57
|
+
}
|
|
58
|
+
if (!isPlainObject(windows) || Object.keys(windows).length === 0) {
|
|
59
|
+
throw new AuthorizationDetailsError(`${where}.${name} must be a non-empty object`);
|
|
60
|
+
}
|
|
61
|
+
const counts = {};
|
|
62
|
+
for (const [window, value] of Object.entries(windows)) {
|
|
63
|
+
if (!CAP_WINDOWS.includes(window)) {
|
|
64
|
+
throw new AuthorizationDetailsError(`${where}.${name} has unknown window "${window}"`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0 || value > MAX_COUNT) {
|
|
67
|
+
throw new AuthorizationDetailsError(`${where}.${name}.${window} must be an integer between 0 and ${MAX_COUNT}`);
|
|
68
|
+
}
|
|
69
|
+
counts[window] = value;
|
|
70
|
+
}
|
|
71
|
+
Object.defineProperty(parsed, name, { value: Object.freeze(counts), enumerable: true, writable: false, configurable: false });
|
|
72
|
+
}
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
/** Whether an entry's `tools` list (when present) names `tool`. */
|
|
76
|
+
export function toolsAuthorizationAllows(entry, tool) {
|
|
77
|
+
if (entry.tools === undefined)
|
|
78
|
+
return true;
|
|
79
|
+
return entry.tools.some((t) => (t.endsWith('*') ? tool.startsWith(t.slice(0, -1)) : t === tool));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Return the tools entries of an `authorization_details` claim by connector.
|
|
83
|
+
* `undefined` or `null` (no claim) yields an empty map.
|
|
84
|
+
*
|
|
85
|
+
* @throws {AuthorizationDetailsError} the claim is malformed or ambiguous.
|
|
86
|
+
*/
|
|
87
|
+
export function parseToolsAuthorization(claim) {
|
|
88
|
+
const entries = new Map();
|
|
89
|
+
if (claim === undefined || claim === null)
|
|
90
|
+
return entries;
|
|
91
|
+
if (!Array.isArray(claim))
|
|
92
|
+
throw new AuthorizationDetailsError('authorization_details must be an array');
|
|
93
|
+
claim.forEach((raw, index) => {
|
|
94
|
+
const where = `authorization_details[${index}]`;
|
|
95
|
+
if (!isPlainObject(raw))
|
|
96
|
+
throw new AuthorizationDetailsError(`${where} must be an object`);
|
|
97
|
+
const type = raw['type'];
|
|
98
|
+
if (typeof type !== 'string' || type.length === 0) {
|
|
99
|
+
throw new AuthorizationDetailsError(`${where}.type must be a non-empty string`);
|
|
100
|
+
}
|
|
101
|
+
if (type !== TOOLS_DETAIL_TYPE)
|
|
102
|
+
return;
|
|
103
|
+
const unknown = Object.keys(raw).filter((k) => !ENTRY_KEYS.has(k)).sort();
|
|
104
|
+
if (unknown.length > 0)
|
|
105
|
+
throw new AuthorizationDetailsError(`${where} has unknown key "${unknown[0]}"`);
|
|
106
|
+
const connector = raw['connector'];
|
|
107
|
+
if (typeof connector !== 'string' || !NAME_RE.test(connector)) {
|
|
108
|
+
throw new AuthorizationDetailsError(`${where}.connector must be a connector name`);
|
|
109
|
+
}
|
|
110
|
+
if (entries.has(connector)) {
|
|
111
|
+
throw new AuthorizationDetailsError(`${where} repeats connector "${connector}"; a grant carries one tools entry per connector`);
|
|
112
|
+
}
|
|
113
|
+
const entry = { connector };
|
|
114
|
+
const purpose = raw['purpose'];
|
|
115
|
+
if (purpose !== undefined && purpose !== null) {
|
|
116
|
+
if (typeof purpose !== 'string')
|
|
117
|
+
throw new AuthorizationDetailsError(`${where}.purpose must be a string`);
|
|
118
|
+
entry.purpose = purpose;
|
|
119
|
+
}
|
|
120
|
+
const dataRegion = raw['data_region'];
|
|
121
|
+
if (dataRegion !== undefined && dataRegion !== null) {
|
|
122
|
+
if (typeof dataRegion !== 'string')
|
|
123
|
+
throw new AuthorizationDetailsError(`${where}.data_region must be a string`);
|
|
124
|
+
entry.dataRegion = dataRegion;
|
|
125
|
+
}
|
|
126
|
+
if ('tools' in raw) {
|
|
127
|
+
const tools = raw['tools'];
|
|
128
|
+
if (!Array.isArray(tools) || !tools.every((t) => typeof t === 'string' && TOOL_PATTERN_RE.test(t))) {
|
|
129
|
+
throw new AuthorizationDetailsError(`${where}.tools must be an array of tool names or name prefixes ending in *`);
|
|
130
|
+
}
|
|
131
|
+
entry.tools = Object.freeze([...tools]);
|
|
132
|
+
}
|
|
133
|
+
if ('caps' in raw) {
|
|
134
|
+
entry.caps = Object.freeze(parseCaps(raw['caps'], `${where}.caps`));
|
|
135
|
+
}
|
|
136
|
+
entries.set(connector, entry);
|
|
137
|
+
});
|
|
138
|
+
return entries;
|
|
139
|
+
}
|
|
140
|
+
export const DECISION_DETAIL_TYPE = 'urn:grantex:decision:v1';
|
|
141
|
+
const DECISION_ENTRY_KEYS = new Set(['type', 'connector', 'tools', 'four_eyes_on']);
|
|
142
|
+
const TOOL_NAME_RE = NAME_RE;
|
|
143
|
+
const DECISION_RE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
144
|
+
/**
|
|
145
|
+
* Return the decision references of an `authorization_details` claim by
|
|
146
|
+
* connector. `undefined` or `null` yields an empty map. Entries of other types
|
|
147
|
+
* are ignored.
|
|
148
|
+
*
|
|
149
|
+
* @throws {AuthorizationDetailsError} an entry is malformed: an unknown key, no
|
|
150
|
+
* tools, a tool name that is not a name, `four_eyes_on` naming a tool not in
|
|
151
|
+
* `tools`, or two entries for one connector.
|
|
152
|
+
*/
|
|
153
|
+
export function parseDecisionReferences(claim) {
|
|
154
|
+
const entries = new Map();
|
|
155
|
+
if (claim === undefined || claim === null)
|
|
156
|
+
return entries;
|
|
157
|
+
if (!Array.isArray(claim))
|
|
158
|
+
throw new AuthorizationDetailsError('authorization_details must be an array');
|
|
159
|
+
claim.forEach((raw, index) => {
|
|
160
|
+
const where = `authorization_details[${index}]`;
|
|
161
|
+
if (!isPlainObject(raw))
|
|
162
|
+
throw new AuthorizationDetailsError(`${where} must be an object`);
|
|
163
|
+
const type = raw['type'];
|
|
164
|
+
if (typeof type !== 'string' || type.length === 0) {
|
|
165
|
+
throw new AuthorizationDetailsError(`${where}.type must be a non-empty string`);
|
|
166
|
+
}
|
|
167
|
+
if (type !== DECISION_DETAIL_TYPE)
|
|
168
|
+
return;
|
|
169
|
+
const unknown = Object.keys(raw).filter((k) => !DECISION_ENTRY_KEYS.has(k)).sort();
|
|
170
|
+
if (unknown.length > 0)
|
|
171
|
+
throw new AuthorizationDetailsError(`${where} has unknown key "${unknown[0]}"`);
|
|
172
|
+
const connector = raw['connector'];
|
|
173
|
+
if (typeof connector !== 'string' || !NAME_RE.test(connector)) {
|
|
174
|
+
throw new AuthorizationDetailsError(`${where}.connector must be a connector name`);
|
|
175
|
+
}
|
|
176
|
+
if (entries.has(connector)) {
|
|
177
|
+
throw new AuthorizationDetailsError(`${where} repeats connector "${connector}"; a grant carries one decision entry per connector`);
|
|
178
|
+
}
|
|
179
|
+
const tools = raw['tools'];
|
|
180
|
+
if (!Array.isArray(tools) || tools.length === 0
|
|
181
|
+
|| !tools.every((t) => typeof t === 'string' && TOOL_NAME_RE.test(t))) {
|
|
182
|
+
throw new AuthorizationDetailsError(`${where}.tools must be a non-empty array of tool names`);
|
|
183
|
+
}
|
|
184
|
+
const fourEyesOn = {};
|
|
185
|
+
if ('four_eyes_on' in raw) {
|
|
186
|
+
const rawFourEyes = raw['four_eyes_on'];
|
|
187
|
+
if (!isPlainObject(rawFourEyes)) {
|
|
188
|
+
throw new AuthorizationDetailsError(`${where}.four_eyes_on must be an object`);
|
|
189
|
+
}
|
|
190
|
+
for (const [tool, decisions] of Object.entries(rawFourEyes)) {
|
|
191
|
+
if (!tools.includes(tool)) {
|
|
192
|
+
throw new AuthorizationDetailsError(`${where}.four_eyes_on names "${tool}", which is not in tools`);
|
|
193
|
+
}
|
|
194
|
+
if (!Array.isArray(decisions) || decisions.length === 0
|
|
195
|
+
|| !decisions.every((d) => typeof d === 'string' && DECISION_RE.test(d))) {
|
|
196
|
+
throw new AuthorizationDetailsError(`${where}.four_eyes_on.${tool} must be a non-empty array of decisions`);
|
|
197
|
+
}
|
|
198
|
+
fourEyesOn[tool] = Object.freeze([...decisions]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
entries.set(connector, {
|
|
202
|
+
connector,
|
|
203
|
+
tools: Object.freeze([...tools]),
|
|
204
|
+
fourEyesOn: Object.freeze(fourEyesOn),
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
return entries;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=authorization-details.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization-details.js","sourceRoot":"","sources":["../src/authorization-details.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
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 declare const MAX_DEPTH = 64;
|
|
25
|
+
/** The value has no RFC 8785 canonical form. */
|
|
26
|
+
export declare class CanonicalizationError extends Error {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
/** Returns the RFC 8785 canonical JSON text of `value`. */
|
|
30
|
+
export declare function canonicalize(value: unknown): string;
|
|
31
|
+
/** Returns the RFC 8785 canonical JSON of `value` as UTF-8 bytes. */
|
|
32
|
+
export declare function canonicalizeToBytes(value: unknown): Uint8Array;
|
|
33
|
+
/** Serialises a finite number as `Number.prototype.toString` does (`-0` as `0`). */
|
|
34
|
+
export declare function serializeNumber(value: number): string;
|
|
35
|
+
/** A JSON text repeats a member name within one object. */
|
|
36
|
+
export declare class DuplicateKeyError extends CanonicalizationError {
|
|
37
|
+
readonly key: string;
|
|
38
|
+
constructor(key: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parses JSON text like `JSON.parse`, but throws `DuplicateKeyError` when an
|
|
42
|
+
* object repeats a member name (after unescaping), instead of silently keeping
|
|
43
|
+
* the last value. Use it wherever the raw text of a value to canonicalise is
|
|
44
|
+
* available.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseJsonRejectingDuplicates(text: string): unknown;
|
|
47
|
+
//# sourceMappingURL=canonical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,sDAAsD;AACtD,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,gDAAgD;AAChD,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAID,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAInD;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAE9D;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMrD;AA+DD,2DAA2D;AAC3D,qBAAa,iBAAkB,SAAQ,qBAAqB;IAC1D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,YAAY,GAAG,EAAE,MAAM,EAItB;CACF;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA6BlE"}
|
|
@@ -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"}
|