@vielzeug/ward 2.3.0 → 3.0.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/README.md +30 -35
- package/dist/_compile.cjs +1 -1
- package/dist/_compile.cjs.map +1 -1
- package/dist/_compile.d.ts +5 -23
- package/dist/_compile.d.ts.map +1 -1
- package/dist/_compile.js +40 -35
- package/dist/_compile.js.map +1 -1
- package/dist/_match.cjs +1 -1
- package/dist/_match.cjs.map +1 -1
- package/dist/_match.d.ts +9 -19
- package/dist/_match.d.ts.map +1 -1
- package/dist/_match.js +40 -37
- package/dist/_match.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +3 -16
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -3
- package/dist/errors.js.map +1 -1
- package/dist/factory.cjs +1 -1
- package/dist/factory.cjs.map +1 -1
- package/dist/factory.d.ts +14 -10
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +71 -119
- package/dist/factory.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/rules.cjs +2 -0
- package/dist/rules.cjs.map +1 -0
- package/dist/rules.d.ts +15 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +26 -0
- package/dist/rules.js.map +1 -0
- package/dist/types.d.ts +63 -132
- package/dist/types.d.ts.map +1 -1
- package/dist/ward.cjs +1 -1
- package/dist/ward.cjs.map +1 -1
- package/dist/ward.iife.js +1 -1
- package/dist/ward.iife.js.map +1 -1
- package/dist/ward.js +1 -1
- package/dist/ward.js.map +1 -1
- package/package.json +7 -7
- package/dist/_conflict.cjs +0 -2
- package/dist/_conflict.cjs.map +0 -1
- package/dist/_conflict.d.ts +0 -10
- package/dist/_conflict.d.ts.map +0 -1
- package/dist/_conflict.js +0 -51
- package/dist/_conflict.js.map +0 -1
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.d.ts +0 -8
- package/dist/_dev.d.ts.map +0 -1
- package/dist/_dev.js +0 -6
- package/dist/_dev.js.map +0 -1
- package/dist/builder.cjs +0 -2
- package/dist/builder.cjs.map +0 -1
- package/dist/builder.d.ts +0 -71
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -39
- package/dist/builder.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @vielzeug/ward
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Ordered authorization rules with immutable policies and typed decisions
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
pnpm add @vielzeug/ward
|
|
9
|
+
npm install @vielzeug/ward
|
|
10
|
+
yarn add @vielzeug/ward
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Quick Start
|
|
@@ -13,47 +15,40 @@ pnpm add @vielzeug/ward
|
|
|
13
15
|
```ts
|
|
14
16
|
import { ANONYMOUS, WILDCARD, allow, createWard, deny, predicate } from '@vielzeug/ward';
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
type Action = 'read' | 'update';
|
|
19
|
+
type Resource = 'posts';
|
|
20
|
+
type Attributes = { authorId: string };
|
|
21
|
+
|
|
22
|
+
const ward = createWard<Action, Resource, Attributes>([
|
|
23
|
+
deny('blocked', WILDCARD, [WILDCARD]),
|
|
24
|
+
allow(ANONYMOUS, 'posts', ['read']),
|
|
25
|
+
allow('viewer', 'posts', ['read']),
|
|
26
|
+
allow<Action, Resource, Attributes>('editor', 'posts', ['update'], {
|
|
27
|
+
when: predicate.owns<Attributes>('authorId'),
|
|
28
|
+
}),
|
|
20
29
|
]);
|
|
21
30
|
|
|
22
31
|
const principal = { id: 'u1', roles: ['editor'] };
|
|
23
|
-
|
|
24
|
-
const decision = ward.explain({
|
|
25
|
-
principal,
|
|
26
|
-
resource: 'posts',
|
|
32
|
+
const decision = ward.decide({
|
|
27
33
|
action: 'update',
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const batch = ward.checkAll(principal, [
|
|
32
|
-
{ resource: 'posts', action: 'read' },
|
|
33
|
-
{ resource: 'posts', action: 'update', data: { authorId: 'u1' } },
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
const trace = ward.trace({
|
|
34
|
+
attributes: { authorId: 'u1' },
|
|
37
35
|
principal,
|
|
38
36
|
resource: 'posts',
|
|
39
|
-
action: 'update',
|
|
40
|
-
data: { authorId: 'u2' },
|
|
41
37
|
});
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
if (decision.effect === 'allow') {
|
|
40
|
+
console.log('update permitted');
|
|
41
|
+
}
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
##
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
- [Overview](https://vielzeug.dev/ward/)
|
|
47
|
+
- [Usage Guide](https://vielzeug.dev/ward/usage)
|
|
48
|
+
- [API Reference](https://vielzeug.dev/ward/api)
|
|
49
|
+
- [Examples](https://vielzeug.dev/ward/examples)
|
|
50
|
+
- [Migration Guide](https://vielzeug.dev/ward/migration)
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
|
package/dist/_compile.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e=require("./errors.cjs");function t(n,r,i){if(n===null||typeof n==`boolean`||typeof n==`string`)return n;if(typeof n==`number`){if(!Number.isFinite(n))throw new e.WardConfigError(`${r} must contain only finite numbers`);return n}if(typeof n!=`object`)throw new e.WardConfigError(`${r} must contain only JSON-compatible values`);if(i.has(n))throw new e.WardConfigError(`${r} must not contain circular references`);let a=Object.getPrototypeOf(n);if(!Array.isArray(n)&&a!==Object.prototype&&a!==null)throw new e.WardConfigError(`${r} must contain only arrays and plain objects`);i.add(n);try{return Object.freeze(Array.isArray(n)?Array.from(n,(e,n)=>t(e,`${r}[${n}]`,i)):Object.fromEntries(Object.keys(n).map(e=>[e,t(n[e],`${r}.${e}`,i)])))}finally{i.delete(n)}}function n(n,r){if(n!==void 0){if(typeof n!=`object`||!n||Array.isArray(n))throw new e.WardConfigError(`${r} must be a plain object`);return t(n,r,new Set)}}function r(t,r){let i=`Rule[${r}]`;if(typeof t!=`object`||!t||Array.isArray(t))throw new e.WardConfigError(`${i} must be a plain object`);let a=Object.getPrototypeOf(t);if(a!==Object.prototype&&a!==null)throw new e.WardConfigError(`${i} must be a plain object`);if(typeof t.action!=`string`||!t.action.trim())throw new e.WardConfigError(`${i}.action must be a non-empty string`);if(t.action.endsWith(`:`))throw new e.WardConfigError(`${i}.action '${t.action}' ends with ':' — did you mean '${t.action}*'?`);if(typeof t.resource!=`string`||!t.resource.trim())throw new e.WardConfigError(`${i}.resource must be a non-empty string`);if(t.resource.endsWith(`:`))throw new e.WardConfigError(`${i}.resource '${t.resource}' ends with ':' — did you mean '${t.resource}*'?`);if(t.effect!==`allow`&&t.effect!==`deny`)throw new e.WardConfigError(`${i}.effect must be "allow" or "deny"`);if(t.condition!==void 0&&typeof t.condition!=`function`)throw new e.WardConfigError(`${i}.condition must be a function`);return Object.freeze({action:t.action,...t.attributes===void 0?{}:{attributes:n(t.attributes,`${i}.attributes`)},...t.condition===void 0?{}:{condition:t.condition},effect:t.effect,resource:t.resource})}exports.compileRule=r,exports.snapshotAttributes=n;
|
|
2
2
|
//# sourceMappingURL=_compile.cjs.map
|
package/dist/_compile.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_compile.cjs","names":[],"sources":["../src/_compile.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"_compile.cjs","names":[],"sources":["../src/_compile.ts"],"sourcesContent":["import { WardConfigError } from './errors';\nimport type { WardAttributes, WardAttributeValue, WardRule } from './types';\n\nfunction snapshotValue(value: unknown, path: string, ancestors: Set<object>): WardAttributeValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return value;\n\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) throw new WardConfigError(`${path} must contain only finite numbers`);\n return value;\n }\n\n if (typeof value !== 'object') {\n throw new WardConfigError(`${path} must contain only JSON-compatible values`);\n }\n\n if (ancestors.has(value)) throw new WardConfigError(`${path} must not contain circular references`);\n\n const prototype = Object.getPrototypeOf(value);\n if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n throw new WardConfigError(`${path} must contain only arrays and plain objects`);\n }\n\n ancestors.add(value);\n\n try {\n if (Array.isArray(value)) {\n return Object.freeze(Array.from(value, (item, index) => snapshotValue(item, `${path}[${index}]`, ancestors)));\n }\n\n return Object.freeze(\n Object.fromEntries(\n Object.keys(value).map((key) => [\n key,\n snapshotValue((value as Record<string, unknown>)[key], `${path}.${key}`, ancestors),\n ]),\n ),\n );\n } finally {\n ancestors.delete(value);\n }\n}\n\nexport function snapshotAttributes(value: unknown, path: string): WardAttributes | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new WardConfigError(`${path} must be a plain object`);\n }\n\n return snapshotValue(value, path, new Set()) as WardAttributes;\n}\n\n/**\n * Validates and snapshots a single rule definition at ward-creation time.\n * Throws {@link WardConfigError} with a `Rule[index]`-prefixed message on failure.\n */\nexport function compileRule<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(\n rule: WardRule<TAction, TResource, TAttributes>,\n index: number,\n): WardRule<TAction, TResource, TAttributes> {\n const at = `Rule[${index}]`;\n\n if (typeof rule !== 'object' || rule === null || Array.isArray(rule)) {\n throw new WardConfigError(`${at} must be a plain object`);\n }\n const prototype = Object.getPrototypeOf(rule);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new WardConfigError(`${at} must be a plain object`);\n }\n\n if (typeof rule.action !== 'string' || !rule.action.trim()) {\n throw new WardConfigError(`${at}.action must be a non-empty string`);\n }\n\n if (rule.action.endsWith(':')) {\n throw new WardConfigError(`${at}.action '${rule.action}' ends with ':' — did you mean '${rule.action}*'?`);\n }\n\n if (typeof rule.resource !== 'string' || !rule.resource.trim()) {\n throw new WardConfigError(`${at}.resource must be a non-empty string`);\n }\n\n if (rule.resource.endsWith(':')) {\n throw new WardConfigError(`${at}.resource '${rule.resource}' ends with ':' — did you mean '${rule.resource}*'?`);\n }\n\n if (rule.effect !== 'allow' && rule.effect !== 'deny') {\n throw new WardConfigError(`${at}.effect must be \"allow\" or \"deny\"`);\n }\n\n if (rule.condition !== undefined && typeof rule.condition !== 'function') {\n throw new WardConfigError(`${at}.condition must be a function`);\n }\n\n return Object.freeze({\n action: rule.action,\n ...(rule.attributes === undefined ? {} : { attributes: snapshotAttributes(rule.attributes, `${at}.attributes`) }),\n ...(rule.condition === undefined ? {} : { condition: rule.condition }),\n effect: rule.effect,\n resource: rule.resource,\n }) as WardRule<TAction, TResource, TAttributes>;\n}\n"],"mappings":"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"}
|
package/dist/_compile.d.ts
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export type CompiledEntry<TAction extends string, TData> = {
|
|
4
|
-
/** Deny bonus (1 for deny, 0 for allow): tiebreaker when priority and score match. */
|
|
5
|
-
denyBonus: 0 | 1;
|
|
6
|
-
/** Original rule index, used in predicate error messages. */
|
|
7
|
-
index: number;
|
|
8
|
-
/** Resolved priority (defaults to 0 when not authored). */
|
|
9
|
-
priority: number;
|
|
10
|
-
/** Normalized roles array (always an array). */
|
|
11
|
-
roles: readonly string[];
|
|
12
|
-
/** The normalized, frozen compiled rule. */
|
|
13
|
-
rule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
14
|
-
/** Specificity score (0–5): roleScore(0|1) + resourceScore(0|1|2) + actionScore(0|1|2). */
|
|
15
|
-
score: number;
|
|
16
|
-
};
|
|
17
|
-
export declare function validateRuleInput<TAction extends string, TData>(rule: WardRule<TAction, TData>, index: number): void;
|
|
1
|
+
import type { WardAttributes, WardRule } from './types';
|
|
2
|
+
export declare function snapshotAttributes(value: unknown, path: string): WardAttributes | undefined;
|
|
18
3
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* - 1: namespace wildcard (e.g. `posts:*` or `read:*`)
|
|
22
|
-
* - 2: exact string
|
|
4
|
+
* Validates and snapshots a single rule definition at ward-creation time.
|
|
5
|
+
* Throws {@link WardConfigError} with a `Rule[index]`-prefixed message on failure.
|
|
23
6
|
*/
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function compileEntry<TAction extends string, TData>(input: WardRule<TAction, TData>, index: number): CompiledEntry<TAction, TData>;
|
|
7
|
+
export declare function compileRule<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(rule: WardRule<TAction, TResource, TAttributes>, index: number): WardRule<TAction, TResource, TAttributes>;
|
|
26
8
|
//# sourceMappingURL=_compile.d.ts.map
|
package/dist/_compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_compile.d.ts","sourceRoot":"","sources":["../src/_compile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_compile.d.ts","sourceRoot":"","sources":["../src/_compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAsB,QAAQ,EAAE,MAAM,SAAS,CAAC;AAyC5E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAO3F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,EAAE,WAAW,SAAS,cAAc,EAC9G,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,EAC/C,KAAK,EAAE,MAAM,GACZ,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,CA0C3C"}
|
package/dist/_compile.js
CHANGED
|
@@ -1,43 +1,48 @@
|
|
|
1
|
-
import "./constants.js";
|
|
2
1
|
import { WardConfigError as e } from "./errors.js";
|
|
3
2
|
//#region src/_compile.ts
|
|
4
|
-
function t(
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
if (
|
|
3
|
+
function t(n, r, i) {
|
|
4
|
+
if (n === null || typeof n == "boolean" || typeof n == "string") return n;
|
|
5
|
+
if (typeof n == "number") {
|
|
6
|
+
if (!Number.isFinite(n)) throw new e(`${r} must contain only finite numbers`);
|
|
7
|
+
return n;
|
|
8
|
+
}
|
|
9
|
+
if (typeof n != "object") throw new e(`${r} must contain only JSON-compatible values`);
|
|
10
|
+
if (i.has(n)) throw new e(`${r} must not contain circular references`);
|
|
11
|
+
let a = Object.getPrototypeOf(n);
|
|
12
|
+
if (!Array.isArray(n) && a !== Object.prototype && a !== null) throw new e(`${r} must contain only arrays and plain objects`);
|
|
13
|
+
i.add(n);
|
|
14
|
+
try {
|
|
15
|
+
return Object.freeze(Array.isArray(n) ? Array.from(n, (e, n) => t(e, `${r}[${n}]`, i)) : Object.fromEntries(Object.keys(n).map((e) => [e, t(n[e], `${r}.${e}`, i)])));
|
|
16
|
+
} finally {
|
|
17
|
+
i.delete(n);
|
|
18
|
+
}
|
|
14
19
|
}
|
|
15
|
-
function n(
|
|
16
|
-
|
|
20
|
+
function n(n, r) {
|
|
21
|
+
if (n !== void 0) {
|
|
22
|
+
if (typeof n != "object" || !n || Array.isArray(n)) throw new e(`${r} must be a plain object`);
|
|
23
|
+
return t(n, r, /* @__PURE__ */ new Set());
|
|
24
|
+
}
|
|
17
25
|
}
|
|
18
|
-
function r(
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
rule: o,
|
|
37
|
-
score: s
|
|
38
|
-
};
|
|
26
|
+
function r(t, r) {
|
|
27
|
+
let i = `Rule[${r}]`;
|
|
28
|
+
if (typeof t != "object" || !t || Array.isArray(t)) throw new e(`${i} must be a plain object`);
|
|
29
|
+
let a = Object.getPrototypeOf(t);
|
|
30
|
+
if (a !== Object.prototype && a !== null) throw new e(`${i} must be a plain object`);
|
|
31
|
+
if (typeof t.action != "string" || !t.action.trim()) throw new e(`${i}.action must be a non-empty string`);
|
|
32
|
+
if (t.action.endsWith(":")) throw new e(`${i}.action '${t.action}' ends with ':' — did you mean '${t.action}*'?`);
|
|
33
|
+
if (typeof t.resource != "string" || !t.resource.trim()) throw new e(`${i}.resource must be a non-empty string`);
|
|
34
|
+
if (t.resource.endsWith(":")) throw new e(`${i}.resource '${t.resource}' ends with ':' — did you mean '${t.resource}*'?`);
|
|
35
|
+
if (t.effect !== "allow" && t.effect !== "deny") throw new e(`${i}.effect must be "allow" or "deny"`);
|
|
36
|
+
if (t.condition !== void 0 && typeof t.condition != "function") throw new e(`${i}.condition must be a function`);
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
action: t.action,
|
|
39
|
+
...t.attributes === void 0 ? {} : { attributes: n(t.attributes, `${i}.attributes`) },
|
|
40
|
+
...t.condition === void 0 ? {} : { condition: t.condition },
|
|
41
|
+
effect: t.effect,
|
|
42
|
+
resource: t.resource
|
|
43
|
+
});
|
|
39
44
|
}
|
|
40
45
|
//#endregion
|
|
41
|
-
export {
|
|
46
|
+
export { r as compileRule, n as snapshotAttributes };
|
|
42
47
|
|
|
43
48
|
//# sourceMappingURL=_compile.js.map
|
package/dist/_compile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_compile.js","names":[],"sources":["../src/_compile.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"_compile.js","names":[],"sources":["../src/_compile.ts"],"sourcesContent":["import { WardConfigError } from './errors';\nimport type { WardAttributes, WardAttributeValue, WardRule } from './types';\n\nfunction snapshotValue(value: unknown, path: string, ancestors: Set<object>): WardAttributeValue {\n if (value === null || typeof value === 'boolean' || typeof value === 'string') return value;\n\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) throw new WardConfigError(`${path} must contain only finite numbers`);\n return value;\n }\n\n if (typeof value !== 'object') {\n throw new WardConfigError(`${path} must contain only JSON-compatible values`);\n }\n\n if (ancestors.has(value)) throw new WardConfigError(`${path} must not contain circular references`);\n\n const prototype = Object.getPrototypeOf(value);\n if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n throw new WardConfigError(`${path} must contain only arrays and plain objects`);\n }\n\n ancestors.add(value);\n\n try {\n if (Array.isArray(value)) {\n return Object.freeze(Array.from(value, (item, index) => snapshotValue(item, `${path}[${index}]`, ancestors)));\n }\n\n return Object.freeze(\n Object.fromEntries(\n Object.keys(value).map((key) => [\n key,\n snapshotValue((value as Record<string, unknown>)[key], `${path}.${key}`, ancestors),\n ]),\n ),\n );\n } finally {\n ancestors.delete(value);\n }\n}\n\nexport function snapshotAttributes(value: unknown, path: string): WardAttributes | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new WardConfigError(`${path} must be a plain object`);\n }\n\n return snapshotValue(value, path, new Set()) as WardAttributes;\n}\n\n/**\n * Validates and snapshots a single rule definition at ward-creation time.\n * Throws {@link WardConfigError} with a `Rule[index]`-prefixed message on failure.\n */\nexport function compileRule<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(\n rule: WardRule<TAction, TResource, TAttributes>,\n index: number,\n): WardRule<TAction, TResource, TAttributes> {\n const at = `Rule[${index}]`;\n\n if (typeof rule !== 'object' || rule === null || Array.isArray(rule)) {\n throw new WardConfigError(`${at} must be a plain object`);\n }\n const prototype = Object.getPrototypeOf(rule);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new WardConfigError(`${at} must be a plain object`);\n }\n\n if (typeof rule.action !== 'string' || !rule.action.trim()) {\n throw new WardConfigError(`${at}.action must be a non-empty string`);\n }\n\n if (rule.action.endsWith(':')) {\n throw new WardConfigError(`${at}.action '${rule.action}' ends with ':' — did you mean '${rule.action}*'?`);\n }\n\n if (typeof rule.resource !== 'string' || !rule.resource.trim()) {\n throw new WardConfigError(`${at}.resource must be a non-empty string`);\n }\n\n if (rule.resource.endsWith(':')) {\n throw new WardConfigError(`${at}.resource '${rule.resource}' ends with ':' — did you mean '${rule.resource}*'?`);\n }\n\n if (rule.effect !== 'allow' && rule.effect !== 'deny') {\n throw new WardConfigError(`${at}.effect must be \"allow\" or \"deny\"`);\n }\n\n if (rule.condition !== undefined && typeof rule.condition !== 'function') {\n throw new WardConfigError(`${at}.condition must be a function`);\n }\n\n return Object.freeze({\n action: rule.action,\n ...(rule.attributes === undefined ? {} : { attributes: snapshotAttributes(rule.attributes, `${at}.attributes`) }),\n ...(rule.condition === undefined ? {} : { condition: rule.condition }),\n effect: rule.effect,\n resource: rule.resource,\n }) as WardRule<TAction, TResource, TAttributes>;\n}\n"],"mappings":";;AAGA,SAAS,EAAc,GAAgB,GAAc,GAA4C;CAC/F,IAAI,MAAU,QAAQ,OAAO,KAAU,aAAa,OAAO,KAAU,UAAU,OAAO;CAEtF,IAAI,OAAO,KAAU,UAAU;EAC7B,IAAI,CAAC,OAAO,SAAS,CAAK,GAAG,MAAM,IAAI,EAAgB,GAAG,EAAK,kCAAkC;EACjG,OAAO;CACT;CAEA,IAAI,OAAO,KAAU,UACnB,MAAM,IAAI,EAAgB,GAAG,EAAK,0CAA0C;CAG9E,IAAI,EAAU,IAAI,CAAK,GAAG,MAAM,IAAI,EAAgB,GAAG,EAAK,sCAAsC;CAElG,IAAM,IAAY,OAAO,eAAe,CAAK;CAC7C,IAAI,CAAC,MAAM,QAAQ,CAAK,KAAK,MAAc,OAAO,aAAa,MAAc,MAC3E,MAAM,IAAI,EAAgB,GAAG,EAAK,4CAA4C;CAGhF,EAAU,IAAI,CAAK;CAEnB,IAAI;EAKF,OAHS,OAAO,OADZ,MAAM,QAAQ,CAAK,IACA,MAAM,KAAK,IAAQ,GAAM,MAAU,EAAc,GAAM,GAAG,EAAK,GAAG,EAAM,IAAI,CAAS,CAAC,IAI3G,OAAO,YACL,OAAO,KAAK,CAAK,CAAC,CAAC,KAAK,MAAQ,CAC9B,GACA,EAAe,EAAkC,IAAM,GAAG,EAAK,GAAG,KAAO,CAAS,CACpF,CAAC,CACH,CAT4G;CAWhH,UAAU;EACR,EAAU,OAAO,CAAK;CACxB;AACF;AAEA,SAAgB,EAAmB,GAAgB,GAA0C;CACvF,UAAU,KAAA,GACd;MAAI,OAAO,KAAU,aAAY,KAAkB,MAAM,QAAQ,CAAK,GACpE,MAAM,IAAI,EAAgB,GAAG,EAAK,wBAAwB;EAG5D,OAAO,EAAc,GAAO,mBAAM,IAAI,IAAI,CAAC;CAHiB;AAI9D;AAMA,SAAgB,EACd,GACA,GAC2C;CAC3C,IAAM,IAAK,QAAQ,EAAM;CAEzB,IAAI,OAAO,KAAS,aAAY,KAAiB,MAAM,QAAQ,CAAI,GACjE,MAAM,IAAI,EAAgB,GAAG,EAAG,wBAAwB;CAE1D,IAAM,IAAY,OAAO,eAAe,CAAI;CAC5C,IAAI,MAAc,OAAO,aAAa,MAAc,MAClD,MAAM,IAAI,EAAgB,GAAG,EAAG,wBAAwB;CAG1D,IAAI,OAAO,EAAK,UAAW,YAAY,CAAC,EAAK,OAAO,KAAK,GACvD,MAAM,IAAI,EAAgB,GAAG,EAAG,mCAAmC;CAGrE,IAAI,EAAK,OAAO,SAAS,GAAG,GAC1B,MAAM,IAAI,EAAgB,GAAG,EAAG,WAAW,EAAK,OAAO,kCAAkC,EAAK,OAAO,IAAI;CAG3G,IAAI,OAAO,EAAK,YAAa,YAAY,CAAC,EAAK,SAAS,KAAK,GAC3D,MAAM,IAAI,EAAgB,GAAG,EAAG,qCAAqC;CAGvE,IAAI,EAAK,SAAS,SAAS,GAAG,GAC5B,MAAM,IAAI,EAAgB,GAAG,EAAG,aAAa,EAAK,SAAS,kCAAkC,EAAK,SAAS,IAAI;CAGjH,IAAI,EAAK,WAAW,WAAW,EAAK,WAAW,QAC7C,MAAM,IAAI,EAAgB,GAAG,EAAG,kCAAkC;CAGpE,IAAI,EAAK,cAAc,KAAA,KAAa,OAAO,EAAK,aAAc,YAC5D,MAAM,IAAI,EAAgB,GAAG,EAAG,8BAA8B;CAGhE,OAAO,OAAO,OAAO;EACnB,QAAQ,EAAK;EACb,GAAI,EAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,EAAmB,EAAK,YAAY,GAAG,EAAG,YAAY,EAAE;EAC/G,GAAI,EAAK,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,EAAK,UAAU;EACpE,QAAQ,EAAK;EACb,UAAU,EAAK;CACjB,CAAC;AACH"}
|
package/dist/_match.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_compile.cjs"),n=require("./resource.cjs");function r(t){if(typeof t!=`object`||!t)throw new e.WardConfigError(`Invalid principal: expected { id: string, roles: string[] }`);let n=t;if(typeof n.id!=`string`||!n.id.trim())throw new e.WardConfigError(`Invalid principal: id must be a non-empty string`);if(!Array.isArray(n.roles)||n.roles.some(e=>typeof e!=`string`||!e.trim()))throw new e.WardConfigError(`Invalid principal: roles must be an array of non-empty strings`)}function i(e){return e==null?null:(r(e),Object.freeze({...e.attributes===void 0?{}:{attributes:t.snapshotAttributes(e.attributes,`Principal.attributes`)},id:e.id,roles:Object.freeze([...e.roles])}))}function a(n){if(typeof n.action!=`string`||!n.action.trim())throw new e.WardConfigError(`Decision.action must be a non-empty string`);if(typeof n.resource!=`string`||!n.resource.trim())throw new e.WardConfigError(`Decision.resource must be a non-empty string`);return Object.freeze({action:n.action,...n.attributes===void 0?{}:{attributes:t.snapshotAttributes(n.attributes,`Decision.attributes`)},principal:i(n.principal),resource:n.resource})}function o(e,t){if(Object.is(e,t))return!0;if(typeof e!=`object`||typeof t!=`object`||e===null||t===null)return!1;if(Array.isArray(e)||Array.isArray(t))return Array.isArray(e)&&Array.isArray(t)&&e.length===t.length&&e.every((e,n)=>o(e,t[n]));let n=e,r=t,i=Object.keys(n),a=Object.keys(r);return i.length===a.length&&i.every(e=>Object.hasOwn(r,e)&&o(n[e],r[e]))}function s(e,t){return e?t?Object.keys(e).every(n=>Object.hasOwn(t,n)&&o(e[n],t[n])):!1:!0}function c(e){return typeof e==`object`&&!!e&&`then`in e&&typeof e.then==`function`||typeof e==`function`&&`then`in e&&typeof e.then==`function`}function l(t,r,i){if(!n.matchesPattern(t.action,i.action)||!n.matchesPattern(t.resource,i.resource)||!s(t.attributes,i.attributes))return!1;if(!t.condition)return!0;try{let e=t.condition({attributes:i.attributes,principal:i.principal});if(c(e))throw TypeError(`Async conditions are not supported; return a boolean`);if(typeof e!=`boolean`)throw TypeError(`Conditions must return a boolean`);return e}catch(t){throw new e.WardConditionError(r,t)}}exports.assertUserPrincipal=r,exports.attributesMatch=s,exports.ruleMatches=l,exports.snapshotDecisionInput=a,exports.snapshotPrincipal=i;
|
|
2
2
|
//# sourceMappingURL=_match.cjs.map
|
package/dist/_match.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_match.cjs","names":[],"sources":["../src/_match.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"_match.cjs","names":[],"sources":["../src/_match.ts"],"sourcesContent":["import { snapshotAttributes } from './_compile';\nimport { WardConditionError, WardConfigError } from './errors';\nimport { matchesPattern } from './resource';\nimport type {\n Principal,\n UserPrincipal,\n WardAttributes,\n WardAttributeValue,\n WardDecisionInput,\n WardRule,\n} from './types';\n\n/** Asserts that `input` is a valid `UserPrincipal`. Throws with a clear message otherwise. */\nexport function assertUserPrincipal(input: unknown): asserts input is UserPrincipal {\n if (typeof input !== 'object' || !input) {\n throw new WardConfigError('Invalid principal: expected { id: string, roles: string[] }');\n }\n\n const principal = input as Record<string, unknown>;\n\n if (typeof principal.id !== 'string' || !principal.id.trim()) {\n throw new WardConfigError('Invalid principal: id must be a non-empty string');\n }\n\n if (!Array.isArray(principal.roles) || principal.roles.some((role) => typeof role !== 'string' || !role.trim())) {\n throw new WardConfigError('Invalid principal: roles must be an array of non-empty strings');\n }\n}\n\nexport function snapshotPrincipal(principal: Principal | undefined): Principal {\n if (principal === undefined || principal === null) return null;\n\n assertUserPrincipal(principal);\n\n return Object.freeze({\n ...(principal.attributes === undefined\n ? {}\n : { attributes: snapshotAttributes(principal.attributes, 'Principal.attributes') }),\n id: principal.id,\n roles: Object.freeze([...principal.roles]),\n });\n}\n\nexport function snapshotDecisionInput<\n TAction extends string,\n TResource extends string,\n TAttributes extends WardAttributes,\n>(\n input: WardDecisionInput<TAction, TResource, TAttributes>,\n): WardDecisionInput<TAction, TResource, TAttributes> & {\n principal: Principal;\n} {\n if (typeof input.action !== 'string' || !input.action.trim()) {\n throw new WardConfigError('Decision.action must be a non-empty string');\n }\n if (typeof input.resource !== 'string' || !input.resource.trim()) {\n throw new WardConfigError('Decision.resource must be a non-empty string');\n }\n\n return Object.freeze({\n action: input.action,\n ...(input.attributes === undefined\n ? {}\n : { attributes: snapshotAttributes(input.attributes, 'Decision.attributes') as TAttributes }),\n principal: snapshotPrincipal(input.principal),\n resource: input.resource,\n });\n}\n\nfunction deepEqual(left: WardAttributeValue, right: WardAttributeValue): boolean {\n if (Object.is(left, right)) return true;\n if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) return false;\n if (Array.isArray(left) || Array.isArray(right)) {\n return (\n Array.isArray(left) &&\n Array.isArray(right) &&\n left.length === right.length &&\n left.every((value, index) => deepEqual(value, right[index]))\n );\n }\n\n const leftRecord = left as Record<string, WardAttributeValue>;\n const rightRecord = right as Record<string, WardAttributeValue>;\n const leftKeys = Object.keys(leftRecord);\n const rightKeys = Object.keys(rightRecord);\n\n return (\n leftKeys.length === rightKeys.length &&\n leftKeys.every((key) => Object.hasOwn(rightRecord, key) && deepEqual(leftRecord[key], rightRecord[key]))\n );\n}\n\nexport function attributesMatch(expected: WardAttributes | undefined, actual: WardAttributes | undefined): boolean {\n if (!expected) return true;\n if (!actual) return false;\n\n return Object.keys(expected).every((key) => Object.hasOwn(actual, key) && deepEqual(expected[key], actual[key]));\n}\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n return (\n (typeof value === 'object' && value !== null && 'then' in value && typeof value.then === 'function') ||\n (typeof value === 'function' && 'then' in value && typeof value.then === 'function')\n );\n}\n\nexport function ruleMatches<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(\n rule: WardRule<TAction, TResource, TAttributes>,\n index: number,\n input: WardDecisionInput<TAction, TResource, TAttributes> & { principal: Principal },\n): boolean {\n if (!matchesPattern(rule.action, input.action)) return false;\n if (!matchesPattern(rule.resource, input.resource)) return false;\n if (!attributesMatch(rule.attributes, input.attributes)) return false;\n if (!rule.condition) return true;\n\n try {\n const result: unknown = rule.condition({ attributes: input.attributes, principal: input.principal });\n\n if (isThenable(result)) {\n throw new TypeError('Async conditions are not supported; return a boolean');\n }\n if (typeof result !== 'boolean') {\n throw new TypeError('Conditions must return a boolean');\n }\n\n return result;\n } catch (error) {\n throw new WardConditionError(index, error);\n }\n}\n"],"mappings":"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"}
|
package/dist/_match.d.ts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Principal, UserPrincipal, WardDecision } from './types';
|
|
1
|
+
import type { Principal, UserPrincipal, WardAttributes, WardDecisionInput, WardRule } from './types';
|
|
3
2
|
/** Asserts that `input` is a valid `UserPrincipal`. Throws with a clear message otherwise. */
|
|
4
3
|
export declare function assertUserPrincipal(input: unknown): asserts input is UserPrincipal;
|
|
5
|
-
export declare function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* @param skipPredicate - When true, omit the `when` evaluation even when data is present.
|
|
14
|
-
*/
|
|
15
|
-
export declare function matchesRule<TAction extends string, TData>(entry: CompiledEntry<TAction, TData>, principal: Principal, resource: string, action: TAction | undefined, data: TData | undefined, skipPredicate?: boolean): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Returns true if `challenger` would displace `current` as the pickWinner result.
|
|
18
|
-
*/
|
|
19
|
-
export declare function isOverriddenBy<TAction extends string, TData>(current: CompiledEntry<TAction, TData>, challenger: CompiledEntry<TAction, TData>): boolean;
|
|
20
|
-
export declare function pickWinner<TAction extends string, TData>(entries: CompiledEntry<TAction, TData>[], principal: Principal, resource: string, action: TAction, data: TData | undefined): CompiledEntry<TAction, TData> | undefined;
|
|
21
|
-
export declare function toDecision<TAction extends string, TData>(winner: CompiledEntry<TAction, TData> | undefined): WardDecision<TAction, TData>;
|
|
4
|
+
export declare function snapshotPrincipal(principal: Principal | undefined): Principal;
|
|
5
|
+
export declare function snapshotDecisionInput<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(input: WardDecisionInput<TAction, TResource, TAttributes>): WardDecisionInput<TAction, TResource, TAttributes> & {
|
|
6
|
+
principal: Principal;
|
|
7
|
+
};
|
|
8
|
+
export declare function attributesMatch(expected: WardAttributes | undefined, actual: WardAttributes | undefined): boolean;
|
|
9
|
+
export declare function ruleMatches<TAction extends string, TResource extends string, TAttributes extends WardAttributes>(rule: WardRule<TAction, TResource, TAttributes>, index: number, input: WardDecisionInput<TAction, TResource, TAttributes> & {
|
|
10
|
+
principal: Principal;
|
|
11
|
+
}): boolean;
|
|
22
12
|
//# sourceMappingURL=_match.d.ts.map
|
package/dist/_match.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_match.d.ts","sourceRoot":"","sources":["../src/_match.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_match.d.ts","sourceRoot":"","sources":["../src/_match.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,cAAc,EAEd,iBAAiB,EACjB,QAAQ,EACT,MAAM,SAAS,CAAC;AAEjB,8FAA8F;AAC9F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAclF;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAY7E;AAED,wBAAgB,qBAAqB,CACnC,OAAO,SAAS,MAAM,EACtB,SAAS,SAAS,MAAM,EACxB,WAAW,SAAS,cAAc,EAElC,KAAK,EAAE,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,GACxD,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,GAAG;IACtD,SAAS,EAAE,SAAS,CAAC;CACtB,CAgBA;AAyBD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,SAAS,GAAG,OAAO,CAKjH;AASD,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,EAAE,WAAW,SAAS,cAAc,EAC9G,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,EAC/C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,GACnF,OAAO,CAoBT"}
|
package/dist/_match.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { WardConditionError as e, WardConfigError as t } from "./errors.js";
|
|
2
|
+
import { snapshotAttributes as n } from "./_compile.js";
|
|
3
3
|
import { matchesPattern as r } from "./resource.js";
|
|
4
4
|
//#region src/_match.ts
|
|
5
5
|
function i(e) {
|
|
@@ -9,48 +9,51 @@ function i(e) {
|
|
|
9
9
|
if (!Array.isArray(n.roles) || n.roles.some((e) => typeof e != "string" || !e.trim())) throw new t("Invalid principal: roles must be an array of non-empty strings");
|
|
10
10
|
}
|
|
11
11
|
function a(e) {
|
|
12
|
-
e
|
|
12
|
+
return e == null ? null : (i(e), Object.freeze({
|
|
13
|
+
...e.attributes === void 0 ? {} : { attributes: n(e.attributes, "Principal.attributes") },
|
|
14
|
+
id: e.id,
|
|
15
|
+
roles: Object.freeze([...e.roles])
|
|
16
|
+
}));
|
|
13
17
|
}
|
|
14
|
-
function o(
|
|
15
|
-
|
|
18
|
+
function o(e) {
|
|
19
|
+
if (typeof e.action != "string" || !e.action.trim()) throw new t("Decision.action must be a non-empty string");
|
|
20
|
+
if (typeof e.resource != "string" || !e.resource.trim()) throw new t("Decision.resource must be a non-empty string");
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
action: e.action,
|
|
23
|
+
...e.attributes === void 0 ? {} : { attributes: n(e.attributes, "Decision.attributes") },
|
|
24
|
+
principal: a(e.principal),
|
|
25
|
+
resource: e.resource
|
|
26
|
+
});
|
|
16
27
|
}
|
|
17
|
-
function s(e, t
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
20
|
-
if (t ===
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
data: s,
|
|
24
|
-
principal: t
|
|
25
|
-
});
|
|
26
|
-
if (n instanceof Promise) throw TypeError(`Rule[${e.index}] when() returned a Promise. Async predicates are not supported — use a synchronous predicate.`);
|
|
27
|
-
return n;
|
|
28
|
-
} catch (t) {
|
|
29
|
-
throw new n(e.index, t);
|
|
30
|
-
}
|
|
28
|
+
function s(e, t) {
|
|
29
|
+
if (Object.is(e, t)) return !0;
|
|
30
|
+
if (typeof e != "object" || typeof t != "object" || e === null || t === null) return !1;
|
|
31
|
+
if (Array.isArray(e) || Array.isArray(t)) return Array.isArray(e) && Array.isArray(t) && e.length === t.length && e.every((e, n) => s(e, t[n]));
|
|
32
|
+
let n = e, r = t, i = Object.keys(n), a = Object.keys(r);
|
|
33
|
+
return i.length === a.length && i.every((e) => Object.hasOwn(r, e) && s(n[e], r[e]));
|
|
31
34
|
}
|
|
32
35
|
function c(e, t) {
|
|
33
|
-
return
|
|
36
|
+
return e ? t ? Object.keys(e).every((n) => Object.hasOwn(t, n) && s(e[n], t[n])) : !1 : !0;
|
|
34
37
|
}
|
|
35
|
-
function l(e
|
|
36
|
-
|
|
37
|
-
for (let o of e) s(o, t, n, r, i) && (!a || c(a, o)) && (a = o);
|
|
38
|
-
return a;
|
|
38
|
+
function l(e) {
|
|
39
|
+
return typeof e == "object" && !!e && "then" in e && typeof e.then == "function" || typeof e == "function" && "then" in e && typeof e.then == "function";
|
|
39
40
|
}
|
|
40
|
-
function u(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
41
|
+
function u(t, n, i) {
|
|
42
|
+
if (!r(t.action, i.action) || !r(t.resource, i.resource) || !c(t.attributes, i.attributes)) return !1;
|
|
43
|
+
if (!t.condition) return !0;
|
|
44
|
+
try {
|
|
45
|
+
let e = t.condition({
|
|
46
|
+
attributes: i.attributes,
|
|
47
|
+
principal: i.principal
|
|
48
|
+
});
|
|
49
|
+
if (l(e)) throw TypeError("Async conditions are not supported; return a boolean");
|
|
50
|
+
if (typeof e != "boolean") throw TypeError("Conditions must return a boolean");
|
|
51
|
+
return e;
|
|
52
|
+
} catch (t) {
|
|
53
|
+
throw new e(n, t);
|
|
54
|
+
}
|
|
52
55
|
}
|
|
53
56
|
//#endregion
|
|
54
|
-
export { i as assertUserPrincipal, c as
|
|
57
|
+
export { i as assertUserPrincipal, c as attributesMatch, u as ruleMatches, o as snapshotDecisionInput, a as snapshotPrincipal };
|
|
55
58
|
|
|
56
59
|
//# sourceMappingURL=_match.js.map
|