@vielzeug/ward 2.0.0 → 2.2.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 +8 -4
- package/dist/_compile.cjs.map +1 -1
- package/dist/_compile.d.ts +1 -1
- package/dist/_compile.d.ts.map +1 -1
- package/dist/_compile.js.map +1 -1
- package/dist/_conflict.cjs.map +1 -1
- package/dist/_conflict.d.ts.map +1 -1
- package/dist/_conflict.js.map +1 -1
- package/dist/_match.cjs.map +1 -1
- package/dist/_match.d.ts.map +1 -1
- package/dist/_match.js.map +1 -1
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.ts +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -4
- 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 +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +58 -41
- package/dist/factory.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/types.d.ts +36 -16
- 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 +22 -28
- package/dist/devtools.cjs +0 -2
- package/dist/devtools.cjs.map +0 -1
- package/dist/devtools.d.ts +0 -36
- package/dist/devtools.d.ts.map +0 -1
- package/dist/devtools.js +0 -16
- package/dist/devtools.js.map +0 -1
package/dist/factory.js
CHANGED
|
@@ -16,51 +16,56 @@ function f(e, t, n, r) {
|
|
|
16
16
|
return o;
|
|
17
17
|
}
|
|
18
18
|
function p(l = [], p = {}) {
|
|
19
|
-
if (p.logger !== void 0 && typeof p.logger != "function") throw new e("logger must be a function.");
|
|
20
19
|
if (p.maxConflicts !== void 0 && (!Number.isFinite(p.maxConflicts) || p.maxConflicts < 0)) throw new e("maxConflicts must be a finite non-negative number.");
|
|
21
20
|
if (p.onConflict !== void 0 && typeof p.onConflict != "function") throw new e("onConflict must be a function.");
|
|
22
|
-
let {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
let { maxConflicts: m = Infinity } = p, h = /* @__PURE__ */ new Set(), g = [];
|
|
22
|
+
for (let e of l) if (Array.isArray(e)) for (let t of e) g.push(t);
|
|
23
|
+
else g.push(e);
|
|
24
|
+
let _ = g.map((e, n) => t(e, n));
|
|
25
|
+
u(_);
|
|
26
|
+
function v(e) {
|
|
27
|
+
if (h.size !== 0) for (let t of h) try {
|
|
28
|
+
t(e);
|
|
29
|
+
} catch {}
|
|
30
|
+
}
|
|
31
|
+
function y(e, t, n, r) {
|
|
32
|
+
let i = a(_, e, t, n, r), s = o(i);
|
|
33
|
+
return v({
|
|
27
34
|
action: n,
|
|
28
35
|
data: r,
|
|
36
|
+
decision: s,
|
|
29
37
|
principal: e,
|
|
30
|
-
resource: t
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function v(e, t, n, r) {
|
|
34
|
-
let i = a(g, e, t, n, r), s = o(i);
|
|
35
|
-
return _(e, t, n, r, s), s;
|
|
38
|
+
resource: t,
|
|
39
|
+
type: "decision"
|
|
40
|
+
}), s;
|
|
36
41
|
}
|
|
37
|
-
function
|
|
42
|
+
function b(e) {
|
|
38
43
|
let { action: t, data: n, principal: r, resource: i } = e;
|
|
39
|
-
return s(r),
|
|
44
|
+
return s(r), y(r, i, t, n);
|
|
40
45
|
}
|
|
41
|
-
function
|
|
46
|
+
function x(e, t) {
|
|
42
47
|
return t.map((t) => ({
|
|
43
|
-
...
|
|
48
|
+
...y(e, t.resource, t.action, t.data),
|
|
44
49
|
action: t.action,
|
|
45
50
|
resource: t.resource
|
|
46
51
|
}));
|
|
47
52
|
}
|
|
48
|
-
function
|
|
49
|
-
return t.length === 0 ? [] : (s(e),
|
|
53
|
+
function S(e, t) {
|
|
54
|
+
return t.length === 0 ? [] : (s(e), x(e, t));
|
|
50
55
|
}
|
|
51
|
-
function
|
|
56
|
+
function C(e) {
|
|
52
57
|
let { data: t, knownActions: n, principal: r, resource: i } = e;
|
|
53
|
-
return s(r), d(
|
|
58
|
+
return s(r), d(_, r, i, n, t);
|
|
54
59
|
}
|
|
55
|
-
function
|
|
60
|
+
function w(e) {
|
|
56
61
|
let { data: t, principal: n, resource: r } = e;
|
|
57
|
-
return s(n), f(
|
|
62
|
+
return s(n), f(_, n, r, t);
|
|
58
63
|
}
|
|
59
|
-
function
|
|
64
|
+
function T(e) {
|
|
60
65
|
let { action: t, data: n, principal: a, resource: c } = e;
|
|
61
66
|
s(a);
|
|
62
67
|
let l = [];
|
|
63
|
-
for (let e of
|
|
68
|
+
for (let e of _) i(e, a, c, t, n) && l.push(e);
|
|
64
69
|
let u;
|
|
65
70
|
for (let e of l) (!u || r(u, e)) && (u = e);
|
|
66
71
|
let d = o(u);
|
|
@@ -75,7 +80,7 @@ function p(l = [], p = {}) {
|
|
|
75
80
|
decision: d
|
|
76
81
|
};
|
|
77
82
|
}
|
|
78
|
-
function
|
|
83
|
+
function E(e) {
|
|
79
84
|
n(e);
|
|
80
85
|
let t = {
|
|
81
86
|
attributes: e.attributes ? structuredClone(e.attributes) : void 0,
|
|
@@ -83,11 +88,11 @@ function p(l = [], p = {}) {
|
|
|
83
88
|
roles: [...e.roles]
|
|
84
89
|
};
|
|
85
90
|
return {
|
|
86
|
-
allowedActions: (e) => d(
|
|
87
|
-
checkAll: (e) => e.length === 0 ? [] :
|
|
88
|
-
explain: (e) =>
|
|
89
|
-
rulesInScope: (e) => f(
|
|
90
|
-
trace: (e) =>
|
|
91
|
+
allowedActions: (e) => d(_, t, e.resource, e.knownActions, e.data),
|
|
92
|
+
checkAll: (e) => e.length === 0 ? [] : x(t, e),
|
|
93
|
+
explain: (e) => y(t, e.resource, e.action, e.data),
|
|
94
|
+
rulesInScope: (e) => f(_, t, e.resource, e.data),
|
|
95
|
+
trace: (e) => T({
|
|
91
96
|
action: e.action,
|
|
92
97
|
data: e.data,
|
|
93
98
|
principal: t,
|
|
@@ -95,25 +100,37 @@ function p(l = [], p = {}) {
|
|
|
95
100
|
})
|
|
96
101
|
};
|
|
97
102
|
}
|
|
98
|
-
let
|
|
99
|
-
function
|
|
100
|
-
return
|
|
103
|
+
let D;
|
|
104
|
+
function O() {
|
|
105
|
+
return D ??= Object.freeze(c(_, m));
|
|
101
106
|
}
|
|
102
107
|
if (p.strict || p.onConflict) {
|
|
103
|
-
let t =
|
|
108
|
+
let t = O();
|
|
104
109
|
if (t.length > 0 && (p.onConflict && t.forEach(p.onConflict), p.strict)) {
|
|
105
110
|
let n = t.map((e) => e.kind === "duplicate" ? `Rule[${e.indexB}] ${e.kind} of Rule[${e.indexA}]` : `Rule[${e.shadowedIndex}] ${e.kind} by Rule[${e.shadowingIndex}]`).join("; ");
|
|
106
111
|
throw new e(`${t.length} rule conflict(s) detected: ${n}`);
|
|
107
112
|
}
|
|
108
113
|
}
|
|
114
|
+
function k(e, t) {
|
|
115
|
+
h.add(e);
|
|
116
|
+
let n = () => h.delete(e);
|
|
117
|
+
if (t?.signal) {
|
|
118
|
+
if (t.signal.aborted) return h.delete(e), () => {};
|
|
119
|
+
t.signal.addEventListener("abort", n, { once: !0 });
|
|
120
|
+
}
|
|
121
|
+
return () => {
|
|
122
|
+
h.delete(e), t?.signal?.removeEventListener("abort", n);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
109
125
|
return {
|
|
110
|
-
allowedActions:
|
|
111
|
-
checkAll:
|
|
112
|
-
detectConflicts:
|
|
113
|
-
explain:
|
|
114
|
-
forUser:
|
|
115
|
-
rulesInScope:
|
|
116
|
-
|
|
126
|
+
allowedActions: C,
|
|
127
|
+
checkAll: S,
|
|
128
|
+
detectConflicts: O,
|
|
129
|
+
explain: b,
|
|
130
|
+
forUser: E,
|
|
131
|
+
rulesInScope: w,
|
|
132
|
+
tap: k,
|
|
133
|
+
trace: T
|
|
117
134
|
};
|
|
118
135
|
}
|
|
119
136
|
//#endregion
|
package/dist/factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","names":[],"sources":["../src/factory.ts"],"sourcesContent":["import type { CompiledEntry } from './_compile';\nimport type {\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n BoundWard,\n Principal,\n UserPrincipal,\n WardAllowedActionsInput,\n Ward,\n WardCheck,\n WardConflict,\n WardDecisionInput,\n WardDecision,\n WardDecisionResult,\n WardOptions,\n WardRulesInScopeInput,\n WardRule,\n WardTrace,\n WardTraceCandidate,\n} from './types';\n\nimport { compileEntry } from './_compile';\nimport { computeConflicts } from './_conflict';\nimport { warnAnonymousPredicates } from './_dev';\nimport { assertUserPrincipal, isOverriddenBy, matchesRule, pickWinner, toDecision, validatePrincipal } from './_match';\nimport { WardConfigError } from './errors';\n\nconst warn = warnAnonymousPredicates;\n\n// ---------------------------------------------------------------------------\n// Shared loop cores (validation-free; used by both public API and forUser)\n// ---------------------------------------------------------------------------\n\nfunction coreAllowedActions<TAction extends string, TData>(\n entries: CompiledEntry<TAction, TData>[],\n principal: Principal,\n resource: string,\n knownActions: readonly TAction[],\n data: TData | undefined,\n): TAction[] {\n const seen = new Set<TAction>();\n const result: TAction[] = [];\n\n for (const action of knownActions) {\n if (seen.has(action)) continue;\n\n seen.add(action);\n\n const winner = pickWinner(entries, principal, resource, action, data);\n\n if (winner?.rule.effect === 'allow') result.push(action);\n }\n\n return result;\n}\n\nfunction coreRulesInScope<TAction extends string, TData>(\n entries: CompiledEntry<TAction, TData>[],\n principal: Principal,\n resource: string,\n data: TData | undefined,\n): CompiledEntry<TAction, TData>['rule'][] {\n const skipPredicate = data === undefined;\n const result: CompiledEntry<TAction, TData>['rule'][] = [];\n\n for (const entry of entries) {\n if (!matchesRule(entry, principal, resource, undefined, data, skipPredicate)) continue;\n\n result.push(entry.rule);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an authorization ward from a set of rules.\n *\n * **Winner selection** when multiple rules match a request:\n * 1. Higher `priority` wins.\n * 2. On priority tie, higher specificity score wins (exact > namespace-wildcard > global-wildcard,\n * applied independently to role, resource, and action).\n * 3. On specificity tie, `deny` beats `allow` (denyBonus tiebreaker).\n * 4. On absolute tie (identical priority, specificity, and effect), the rule declared\n * **first in the input array** wins.\n */\nexport function createWard<TAction extends string = string, TData = unknown>(\n rules: readonly WardRule<TAction, TData>[] = [],\n options: WardOptions<TAction, TData> = {},\n): Ward<TAction, TData> {\n if (options.logger !== undefined && typeof options.logger !== 'function') {\n throw new WardConfigError('logger must be a function.');\n }\n\n if (options.maxConflicts !== undefined) {\n if (!Number.isFinite(options.maxConflicts) || options.maxConflicts < 0) {\n throw new WardConfigError('maxConflicts must be a finite non-negative number.');\n }\n }\n\n if (options.onConflict !== undefined && typeof options.onConflict !== 'function') {\n throw new WardConfigError('onConflict must be a function.');\n }\n\n const { logger, maxConflicts = Infinity } = options;\n const entries = rules.map((rule, i) => compileEntry(rule, i));\n\n // Warn in development when an ANONYMOUS-role rule has a predicate.\n warn(entries);\n\n // -------------------------------------------------------------------------\n // Core decision + logging\n // -------------------------------------------------------------------------\n\n function fireLogger(\n principal: Principal,\n resource: string,\n action: TAction,\n data: TData | undefined,\n decision: WardDecision<TAction, TData>,\n ): void {\n if (!logger) return;\n\n logger({ ...decision, action, data, principal, resource } as Parameters<typeof logger>[0]);\n }\n\n function evaluateAndLog(\n principal: Principal,\n resource: string,\n action: TAction,\n data: TData | undefined,\n ): WardDecision<TAction, TData> {\n const winner = pickWinner(entries, principal, resource, action, data);\n const decision = toDecision(winner);\n\n fireLogger(principal, resource, action, data, decision);\n\n return decision;\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n // Request objects avoid call-site ambiguity between resource/action/data and\n // make later API growth additive instead of positional-breaking.\n\n function explain(input: WardDecisionInput<TAction, TData>): WardDecision<TAction, TData> {\n const { action, data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return evaluateAndLog(principal, resource, action, data);\n }\n\n function runCheckAll(\n principal: Principal,\n checks: readonly WardCheck<TAction, TData>[],\n ): WardDecisionResult<TAction, TData>[] {\n return checks.map((check) => ({\n ...evaluateAndLog(principal, check.resource, check.action, check.data),\n action: check.action,\n resource: check.resource,\n }));\n }\n\n function checkAll(\n principal: Principal,\n checks: readonly WardCheck<TAction, TData>[],\n ): WardDecisionResult<TAction, TData>[] {\n if (checks.length === 0) return [];\n\n validatePrincipal(principal);\n\n return runCheckAll(principal, checks);\n }\n\n function allowedActions(input: WardAllowedActionsInput<TAction, TData>): TAction[] {\n const { data, knownActions, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return coreAllowedActions(entries, principal, resource, knownActions, data);\n }\n\n function rulesInScope(input: WardRulesInScopeInput<TData>): ReadonlyArray<Readonly<WardRule<TAction, TData>>> {\n const { data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return coreRulesInScope(entries, principal, resource, data);\n }\n\n function trace(input: WardDecisionInput<TAction, TData>): WardTrace<TAction, TData> {\n const { action, data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n const matching: CompiledEntry<TAction, TData>[] = [];\n\n for (const entry of entries) {\n if (matchesRule(entry, principal, resource, action, data)) {\n matching.push(entry);\n }\n }\n\n let winner: CompiledEntry<TAction, TData> | undefined;\n\n for (const entry of matching) {\n if (!winner || isOverriddenBy(winner, entry)) winner = entry;\n }\n\n const decision = toDecision(winner);\n\n const candidates: WardTraceCandidate<TAction, TData>[] = matching.map((entry) => ({\n index: entry.index,\n priority: entry.priority,\n rule: entry.rule,\n score: entry.score,\n won: entry === winner,\n }));\n\n return { candidates, decision };\n }\n\n function forUser(principal: UserPrincipal): BoundWard<TAction, TData> {\n assertUserPrincipal(principal);\n\n const snap: UserPrincipal = {\n attributes: principal.attributes ? structuredClone(principal.attributes) : undefined,\n id: principal.id,\n roles: [...principal.roles],\n };\n\n return {\n allowedActions: (input: BoundWardAllowedActionsInput<TAction, TData>) =>\n coreAllowedActions(entries, snap, input.resource, input.knownActions, input.data),\n checkAll: (checks) => (checks.length === 0 ? [] : runCheckAll(snap, checks)),\n explain: (input: BoundWardDecisionInput<TAction, TData>) =>\n evaluateAndLog(snap, input.resource, input.action, input.data),\n rulesInScope: (input: BoundWardRulesInScopeInput<TData>) =>\n coreRulesInScope(entries, snap, input.resource, input.data),\n trace: (input: BoundWardDecisionInput<TAction, TData>) =>\n trace({ action: input.action, data: input.data, principal: snap, resource: input.resource }),\n };\n }\n\n // -------------------------------------------------------------------------\n // Conflict detection (lazy, cached)\n // -------------------------------------------------------------------------\n\n let conflictsCache: readonly WardConflict<TAction, TData>[] | undefined;\n\n function detectConflicts(): readonly WardConflict<TAction, TData>[] {\n return (conflictsCache ??= Object.freeze(computeConflicts(entries, maxConflicts)));\n }\n\n if (options.strict || options.onConflict) {\n const conflicts = detectConflicts();\n\n if (conflicts.length > 0) {\n if (options.onConflict) conflicts.forEach(options.onConflict);\n\n if (options.strict) {\n const details = conflicts\n .map((c) =>\n c.kind === 'duplicate'\n ? `Rule[${c.indexB}] ${c.kind} of Rule[${c.indexA}]`\n : `Rule[${c.shadowedIndex}] ${c.kind} by Rule[${c.shadowingIndex}]`,\n )\n .join('; ');\n\n throw new WardConfigError(`${conflicts.length} rule conflict(s) detected: ${details}`);\n }\n }\n }\n\n return { allowedActions, checkAll, detectConflicts, explain, forUser, rulesInScope, trace };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"factory.js","names":[],"sources":["../src/factory.ts"],"sourcesContent":["import type { CompiledEntry } from './_compile';\nimport { compileEntry } from './_compile';\nimport { computeConflicts } from './_conflict';\nimport { warnAnonymousPredicates } from './_dev';\nimport { assertUserPrincipal, isOverriddenBy, matchesRule, pickWinner, toDecision, validatePrincipal } from './_match';\nimport { WardConfigError } from './errors';\nimport type {\n BoundWard,\n BoundWardAllowedActionsInput,\n BoundWardDecisionInput,\n BoundWardRulesInScopeInput,\n NormalizedWardRule,\n Principal,\n UserPrincipal,\n Ward,\n WardAllowedActionsInput,\n WardCheck,\n WardConflict,\n WardDecision,\n WardDecisionInput,\n WardDecisionResult,\n WardEvent,\n WardOptions,\n WardRule,\n WardRulesInScopeInput,\n WardTrace,\n WardTraceCandidate,\n} from './types';\n\nconst warn = warnAnonymousPredicates;\n\n// ---------------------------------------------------------------------------\n// Shared loop cores (validation-free; used by both public API and forUser)\n// ---------------------------------------------------------------------------\n\nfunction coreAllowedActions<TAction extends string, TData>(\n entries: CompiledEntry<TAction, TData>[],\n principal: Principal,\n resource: string,\n knownActions: readonly TAction[],\n data: TData | undefined,\n): TAction[] {\n const seen = new Set<TAction>();\n const result: TAction[] = [];\n\n for (const action of knownActions) {\n if (seen.has(action)) continue;\n\n seen.add(action);\n\n const winner = pickWinner(entries, principal, resource, action, data);\n\n if (winner?.rule.effect === 'allow') result.push(action);\n }\n\n return result;\n}\n\nfunction coreRulesInScope<TAction extends string, TData>(\n entries: CompiledEntry<TAction, TData>[],\n principal: Principal,\n resource: string,\n data: TData | undefined,\n): NormalizedWardRule<TAction, TData>[] {\n const skipPredicate = data === undefined;\n const result: NormalizedWardRule<TAction, TData>[] = [];\n\n for (const entry of entries) {\n if (!matchesRule(entry, principal, resource, undefined, data, skipPredicate)) continue;\n\n result.push(entry.rule as NormalizedWardRule<TAction, TData>);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an authorization ward from a set of rules.\n *\n * **Winner selection** when multiple rules match a request:\n * 1. Higher `priority` wins.\n * 2. On priority tie, higher specificity score wins (exact > namespace-wildcard > global-wildcard,\n * applied independently to role, resource, and action).\n * 3. On specificity tie, `deny` beats `allow` (denyBonus tiebreaker).\n * 4. On absolute tie (identical priority, specificity, and effect), the rule declared\n * **first in the input array** wins.\n */\nexport function createWard<TAction extends string = string, TData = unknown>(\n rules: readonly (WardRule<TAction, TData> | readonly WardRule<TAction, TData>[])[] = [],\n options: WardOptions<TAction, TData> = {},\n): Ward<TAction, TData> {\n if (options.maxConflicts !== undefined) {\n if (!Number.isFinite(options.maxConflicts) || options.maxConflicts < 0) {\n throw new WardConfigError('maxConflicts must be a finite non-negative number.');\n }\n }\n\n if (options.onConflict !== undefined && typeof options.onConflict !== 'function') {\n throw new WardConfigError('onConflict must be a function.');\n }\n\n const { maxConflicts = Infinity } = options;\n const tappers = new Set<(event: WardEvent<TAction, TData>) => void>();\n const flat: WardRule<TAction, TData>[] = [];\n\n for (const entry of rules) {\n if (Array.isArray(entry)) {\n for (const rule of entry) flat.push(rule);\n } else {\n flat.push(entry as WardRule<TAction, TData>);\n }\n }\n\n const entries = flat.map((rule, i) => compileEntry(rule, i));\n\n // Warn in development when an ANONYMOUS-role rule has a predicate.\n warn(entries);\n\n // -------------------------------------------------------------------------\n // Core decision + logging\n // -------------------------------------------------------------------------\n\n function emitTap(event: WardEvent<TAction, TData>): void {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect ward behavior.\n }\n }\n }\n\n function evaluateAndLog(\n principal: Principal,\n resource: string,\n action: TAction,\n data: TData | undefined,\n ): WardDecision<TAction, TData> {\n const winner = pickWinner(entries, principal, resource, action, data);\n const decision = toDecision(winner);\n\n emitTap({ action, data, decision, principal, resource, type: 'decision' });\n\n return decision;\n }\n\n // -------------------------------------------------------------------------\n // Public API\n // -------------------------------------------------------------------------\n // Request objects avoid call-site ambiguity between resource/action/data and\n // make later API growth additive instead of positional-breaking.\n\n function explain(input: WardDecisionInput<TAction, TData>): WardDecision<TAction, TData> {\n const { action, data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return evaluateAndLog(principal, resource, action, data);\n }\n\n function runCheckAll(\n principal: Principal,\n checks: readonly WardCheck<TAction, TData>[],\n ): WardDecisionResult<TAction, TData>[] {\n return checks.map((check) => ({\n ...evaluateAndLog(principal, check.resource, check.action, check.data),\n action: check.action,\n resource: check.resource,\n }));\n }\n\n function checkAll(\n principal: Principal,\n checks: readonly WardCheck<TAction, TData>[],\n ): WardDecisionResult<TAction, TData>[] {\n if (checks.length === 0) return [];\n\n validatePrincipal(principal);\n\n return runCheckAll(principal, checks);\n }\n\n function allowedActions(input: WardAllowedActionsInput<TAction, TData>): TAction[] {\n const { data, knownActions, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return coreAllowedActions(entries, principal, resource, knownActions, data);\n }\n\n function rulesInScope(\n input: WardRulesInScopeInput<TData>,\n ): ReadonlyArray<Readonly<NormalizedWardRule<TAction, TData>>> {\n const { data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n return coreRulesInScope(entries, principal, resource, data);\n }\n\n function trace(input: WardDecisionInput<TAction, TData>): WardTrace<TAction, TData> {\n const { action, data, principal, resource } = input;\n\n validatePrincipal(principal);\n\n const matching: CompiledEntry<TAction, TData>[] = [];\n\n for (const entry of entries) {\n if (matchesRule(entry, principal, resource, action, data)) {\n matching.push(entry);\n }\n }\n\n let winner: CompiledEntry<TAction, TData> | undefined;\n\n for (const entry of matching) {\n if (!winner || isOverriddenBy(winner, entry)) winner = entry;\n }\n\n const decision = toDecision(winner);\n\n const candidates: WardTraceCandidate<TAction, TData>[] = matching.map((entry) => ({\n index: entry.index,\n priority: entry.priority,\n rule: entry.rule,\n score: entry.score,\n won: entry === winner,\n }));\n\n return { candidates, decision };\n }\n\n function forUser(principal: UserPrincipal): BoundWard<TAction, TData> {\n assertUserPrincipal(principal);\n\n const snap: UserPrincipal = {\n attributes: principal.attributes ? structuredClone(principal.attributes) : undefined,\n id: principal.id,\n roles: [...principal.roles],\n };\n\n return {\n allowedActions: (input: BoundWardAllowedActionsInput<TAction, TData>) =>\n coreAllowedActions(entries, snap, input.resource, input.knownActions, input.data),\n checkAll: (checks) => (checks.length === 0 ? [] : runCheckAll(snap, checks)),\n explain: (input: BoundWardDecisionInput<TAction, TData>) =>\n evaluateAndLog(snap, input.resource, input.action, input.data),\n rulesInScope: (input: BoundWardRulesInScopeInput<TData>) =>\n coreRulesInScope(entries, snap, input.resource, input.data),\n trace: (input: BoundWardDecisionInput<TAction, TData>) =>\n trace({ action: input.action, data: input.data, principal: snap, resource: input.resource }),\n };\n }\n\n // -------------------------------------------------------------------------\n // Conflict detection (lazy, cached)\n // -------------------------------------------------------------------------\n\n let conflictsCache: readonly WardConflict<TAction, TData>[] | undefined;\n\n function detectConflicts(): readonly WardConflict<TAction, TData>[] {\n return (conflictsCache ??= Object.freeze(computeConflicts(entries, maxConflicts)));\n }\n\n if (options.strict || options.onConflict) {\n const conflicts = detectConflicts();\n\n if (conflicts.length > 0) {\n if (options.onConflict) conflicts.forEach(options.onConflict);\n\n if (options.strict) {\n const details = conflicts\n .map((c) =>\n c.kind === 'duplicate'\n ? `Rule[${c.indexB}] ${c.kind} of Rule[${c.indexA}]`\n : `Rule[${c.shadowedIndex}] ${c.kind} by Rule[${c.shadowingIndex}]`,\n )\n .join('; ');\n\n throw new WardConfigError(`${conflicts.length} rule conflict(s) detected: ${details}`);\n }\n }\n }\n\n function tap(handler: (event: WardEvent<TAction, TData>) => void, opts?: { signal?: AbortSignal }): () => void {\n tappers.add(handler);\n\n const onAbort = () => tappers.delete(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n opts.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n return () => {\n tappers.delete(handler);\n opts?.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return { allowedActions, checkAll, detectConflicts, explain, forUser, rulesInScope, tap, trace };\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./builder.cjs"),t=require("./constants.cjs"),n=require("./errors.cjs"),r=require("./resource.cjs"),i=require("./factory.cjs");exports.ANONYMOUS=t.ANONYMOUS,exports.WILDCARD=t.WILDCARD,exports.WardConfigError=n.WardConfigError,exports.WardError=n.WardError,exports.WardPredicateError=n.WardPredicateError,exports.allow=e.allow,exports.createWard=i.createWard,exports.deny=e.deny,exports.matchesPattern=r.matchesPattern,exports.owns=e.owns,exports.patternCovers=r.patternCovers,exports.predicate=e.predicate,exports.ruleFor=e.ruleFor;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { ANONYMOUS, WILDCARD } from './constants';
|
|
2
1
|
export { allow, deny, owns, predicate, ruleFor } from './builder';
|
|
2
|
+
export { ANONYMOUS, WILDCARD } from './constants';
|
|
3
3
|
export { WardConfigError, WardError, WardPredicateError } from './errors';
|
|
4
4
|
export { createWard } from './factory';
|
|
5
5
|
export { matchesPattern, patternCovers } from './resource';
|
|
6
|
-
export type { BoundWardAllowedActionsInput, BoundWardDecisionInput, BoundWardRulesInScopeInput,
|
|
6
|
+
export type { BoundWard, BoundWardAllowedActionsInput, BoundWardDecisionInput, BoundWardRulesInScopeInput, ConflictKind, NormalizedWardRule, Principal, RuleContext, UserPrincipal, Ward, WardAllowedActionsInput, WardCheck, WardConflict, WardDecision, WardDecisionInput, WardDecisionResult, WardEvent, WardOptions, WardPredicate, WardRule, WardRulesInScopeInput, WardTrace, WardTraceCandidate, } from './types';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3D,YAAY,EACV,SAAS,EACT,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,aAAa,EACb,IAAI,EACJ,uBAAuB,EACvB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,aAAa,EACb,QAAQ,EACR,qBAAqB,EACrB,SAAS,EACT,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
export {
|
|
1
|
+
import { allow as e, deny as t, owns as n, predicate as r, ruleFor as i } from "./builder.js";
|
|
2
|
+
import { ANONYMOUS as a, WILDCARD as o } from "./constants.js";
|
|
3
|
+
import { WardConfigError as s, WardError as c, WardPredicateError as l } from "./errors.js";
|
|
4
|
+
import { matchesPattern as u, patternCovers as d } from "./resource.js";
|
|
5
|
+
import { createWard as f } from "./factory.js";
|
|
6
|
+
export { a as ANONYMOUS, o as WILDCARD, s as WardConfigError, c as WardError, l as WardPredicateError, e as allow, f as createWard, t as deny, u as matchesPattern, n as owns, d as patternCovers, r as predicate, i as ruleFor };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WILDCARD } from './constants';
|
|
1
|
+
import type { WILDCARD } from './constants';
|
|
2
2
|
export type UserPrincipal = {
|
|
3
3
|
attributes?: Record<string, unknown>;
|
|
4
4
|
id: string;
|
|
@@ -18,13 +18,25 @@ export type WardRule<TAction extends string = string, TData = unknown> = {
|
|
|
18
18
|
role: string | readonly string[];
|
|
19
19
|
when?: WardPredicate<TData>;
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* Normalized rule shape returned by decision APIs. `role` is always an array
|
|
23
|
+
* and `priority` is always a number (defaults to 0 when not authored).
|
|
24
|
+
*/
|
|
25
|
+
export type NormalizedWardRule<TAction extends string = string, TData = unknown> = Readonly<{
|
|
26
|
+
action: TAction | typeof WILDCARD;
|
|
27
|
+
effect: 'allow' | 'deny';
|
|
28
|
+
priority: number;
|
|
29
|
+
resource: string | typeof WILDCARD;
|
|
30
|
+
role: readonly string[];
|
|
31
|
+
when?: WardPredicate<TData>;
|
|
32
|
+
}>;
|
|
21
33
|
export type WardDecision<TAction extends string = string, TData = unknown> = {
|
|
22
34
|
allowed: true;
|
|
23
|
-
rule: Readonly<
|
|
35
|
+
rule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
24
36
|
} | {
|
|
25
37
|
allowed: false;
|
|
26
38
|
reason: 'explicit-deny';
|
|
27
|
-
rule: Readonly<
|
|
39
|
+
rule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
28
40
|
} | {
|
|
29
41
|
allowed: false;
|
|
30
42
|
reason: 'no-matching-rule';
|
|
@@ -74,19 +86,19 @@ export type WardConflict<TAction extends string = string, TData = unknown> = {
|
|
|
74
86
|
indexA: number;
|
|
75
87
|
indexB: number;
|
|
76
88
|
kind: 'duplicate';
|
|
77
|
-
ruleA: Readonly<
|
|
78
|
-
ruleB: Readonly<
|
|
89
|
+
ruleA: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
90
|
+
ruleB: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
79
91
|
} | {
|
|
80
92
|
kind: 'shadowed';
|
|
81
93
|
shadowedIndex: number;
|
|
82
|
-
shadowedRule: Readonly<
|
|
94
|
+
shadowedRule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
83
95
|
shadowingIndex: number;
|
|
84
|
-
shadowingRule: Readonly<
|
|
96
|
+
shadowingRule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
85
97
|
};
|
|
86
98
|
export type WardTraceCandidate<TAction extends string = string, TData = unknown> = {
|
|
87
99
|
index: number;
|
|
88
100
|
priority: number;
|
|
89
|
-
rule: Readonly<
|
|
101
|
+
rule: Readonly<NormalizedWardRule<TAction, TData>>;
|
|
90
102
|
score: number;
|
|
91
103
|
won: boolean;
|
|
92
104
|
};
|
|
@@ -100,24 +112,32 @@ export type Ward<TAction extends string = string, TData = unknown> = {
|
|
|
100
112
|
detectConflicts(): readonly WardConflict<TAction, TData>[];
|
|
101
113
|
explain(input: WardDecisionInput<TAction, TData>): WardDecision<TAction, TData>;
|
|
102
114
|
forUser(principal: UserPrincipal): BoundWard<TAction, TData>;
|
|
103
|
-
rulesInScope(input: WardRulesInScopeInput<TData>): ReadonlyArray<Readonly<
|
|
115
|
+
rulesInScope(input: WardRulesInScopeInput<TData>): ReadonlyArray<Readonly<NormalizedWardRule<TAction, TData>>>;
|
|
116
|
+
tap(handler: (event: WardEvent<TAction, TData>) => void, options?: {
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
}): () => void;
|
|
104
119
|
trace(input: WardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;
|
|
105
120
|
};
|
|
106
121
|
export type BoundWard<TAction extends string = string, TData = unknown> = {
|
|
107
122
|
allowedActions(input: BoundWardAllowedActionsInput<TAction, TData>): TAction[];
|
|
108
123
|
checkAll(checks: readonly WardCheck<TAction, TData>[]): WardDecisionResult<TAction, TData>[];
|
|
109
124
|
explain(input: BoundWardDecisionInput<TAction, TData>): WardDecision<TAction, TData>;
|
|
110
|
-
rulesInScope(input: BoundWardRulesInScopeInput<TData>): ReadonlyArray<Readonly<
|
|
125
|
+
rulesInScope(input: BoundWardRulesInScopeInput<TData>): ReadonlyArray<Readonly<NormalizedWardRule<TAction, TData>>>;
|
|
111
126
|
trace(input: BoundWardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;
|
|
112
127
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Runtime events emitted by {@link Ward.tap}.
|
|
130
|
+
* Subscribe via `ward.tap(handler)` — handler errors are swallowed.
|
|
131
|
+
*/
|
|
132
|
+
export type WardEvent<TAction extends string = string, TData = unknown> = {
|
|
133
|
+
readonly type: 'decision';
|
|
134
|
+
readonly decision: WardDecision<TAction, TData>;
|
|
135
|
+
readonly action: TAction;
|
|
136
|
+
readonly data?: TData;
|
|
137
|
+
readonly principal: Principal;
|
|
138
|
+
readonly resource: string;
|
|
118
139
|
};
|
|
119
140
|
export type WardOptions<TAction extends string = string, TData = unknown> = {
|
|
120
|
-
logger?: (context: WardLoggerContext<TAction, TData>) => void;
|
|
121
141
|
maxConflicts?: number;
|
|
122
142
|
onConflict?: (conflict: WardConflict<TAction, TData>) => void;
|
|
123
143
|
strict?: boolean;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/ward.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t,n,r,i){return r.map(r=>({action:r,effect:e,...i?.priority===void 0?{}:{priority:i.priority},resource:n,role:t,...i?.when===void 0?{}:{when:i.when}}))}function t(t,n,r,i){return e(`allow`,t,n,r,i)}function n(t,n,r,i){return e(`deny`,t,n,r,i)}var r={and(...e){return t=>e.every(e=>e(t))},not(e){return t=>!e(t)},or(...e){return t=>e.some(e=>e(t))},owns(e){return({data:t,principal:n})=>{if(!t||typeof t!=`object`)return!1;let r=t;return Object.hasOwn(r,e)?r[e]===n.id:!1}}};function i(e){return r.owns(e)}var a=`*`,o=`anonymous`,s=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},c=class extends s{},l=class extends s{ruleIndex;constructor(e,t){let n=t instanceof Error?t.message:String(t);super(`Rule[${e}] threw: ${n}`,{cause:t}),this.ruleIndex=e}};function u(e,t){let n=`Rule[${t}]`,r=Array.isArray(e.role)?e.role:[e.role];if(r.length===0||r.some(e=>typeof e!=`string`||!e.trim()))throw new c(`${n}.role must be a non-empty string or non-empty array of strings`);if(typeof e.resource!=`string`||!e.resource.trim())throw new c(`${n}.resource must be a non-empty string`);if(e.resource.endsWith(`:`))throw new c(`${n}.resource '${String(e.resource)}' ends with ':' — did you mean '${String(e.resource)}*'?`);if(typeof e.action!=`string`||!e.action.trim())throw new c(`${n}.action must be a non-empty string`);if(e.action.endsWith(`:`))throw new c(`${n}.action '${String(e.action)}' ends with ':' — did you mean '${String(e.action)}*'?`);if(e.effect!==`allow`&&e.effect!==`deny`)throw new c(`${n}.effect must be "allow" or "deny"`);if(e.priority!==void 0&&(typeof e.priority!=`number`||!Number.isFinite(e.priority)))throw new c(`${n}.priority must be a finite number`);if(e.when!==void 0&&typeof e.when!=`function`)throw new c(`${n}.when must be a function`)}function d(e){return e===`*`?0:e.endsWith(`:*`)?1:2}function f(e){return+!e.role.includes(`*`)+d(e.resource)+d(e.action)}function p(e,t){u(e,t);let n=Array.isArray(e.role)?[...e.role]:[e.role],r=Object.freeze([...new Set(n)]),i=Object.freeze({action:e.action,effect:e.effect,priority:e.priority??0,resource:e.resource,role:r,...e.when===void 0?{}:{when:e.when}}),a=f(i),o=i.priority;return{denyBonus:+(i.effect===`deny`),index:t,priority:o,roles:r,rule:i,score:a}}function m(e,t){if(e===`*`||e===t)return!0;if(e.endsWith(`:*`)){let n=e.slice(0,-1);return t.startsWith(n)}return!1}function h(e,t){return e===`*`?!0:t===`*`?!1:e.endsWith(`:*`)?t.startsWith(e.slice(0,-1)):e===t}function g(e){if(typeof e!=`object`||!e)throw new c(`Invalid principal: expected { id: string, roles: string[] }`);let t=e;if(typeof t.id!=`string`||!t.id.trim())throw new c(`Invalid principal: id must be a non-empty string`);if(!Array.isArray(t.roles)||t.roles.some(e=>typeof e!=`string`||!e.trim()))throw new c(`Invalid principal: roles must be an array of non-empty strings`)}function _(e){e!==null&&g(e)}function v(e,t){return t===null?e.includes(o):e.every(e=>e===`anonymous`)?!1:e.includes(`*`)?!0:e.some(e=>t.roles.includes(e))}function y(e,t,n,r,i,a=!1){if(!v(e.roles,t)||!m(e.rule.resource,n)||r!==void 0&&!m(e.rule.action,r))return!1;if(a||!e.rule.when)return!0;if(t===null)return!1;try{let n=e.rule.when({data:i,principal:t});if(n instanceof Promise)throw TypeError(`Rule[${e.index}] when() returned a Promise. Async predicates are not supported — use a synchronous predicate.`);return n}catch(t){throw new l(e.index,t)}}function b(e,t){return t.priority>e.priority||t.priority===e.priority&&t.score>e.score||t.priority===e.priority&&t.score===e.score&&t.denyBonus>e.denyBonus}function x(e,t,n,r,i){let a;for(let o of e)y(o,t,n,r,i)&&(!a||b(a,o))&&(a=o);return a}function S(e){return e?e.rule.effect===`deny`?{allowed:!1,reason:`explicit-deny`,rule:e.rule}:{allowed:!0,rule:e.rule}:{allowed:!1,reason:`no-matching-rule`}}function C(e,t){let n=t.includes(o),r=t.some(e=>e!==o);return n&&!e.includes(`anonymous`)?!1:r?e.includes(`*`)?!0:t.filter(e=>e!==o).every(t=>e.includes(t)):!0}function w(e,t){if(e.length!==t.length)return!1;let n=new Set(e);return t.every(e=>n.has(e))}function T(e,t){return C(e.roles,t.roles)&&h(e.rule.resource,t.rule.resource)&&h(e.rule.action,t.rule.action)}function E(e,t){let n=[];for(let r=0;r<e.length&&n.length<t;r++)for(let i=r+1;i<e.length&&n.length<t;i++){let t=e[r],a=e[i];if(!t.rule.when&&!a.rule.when&&w(t.roles,a.roles)&&t.rule.resource===a.rule.resource&&t.rule.action===a.rule.action){n.push({indexA:t.index,indexB:a.index,kind:`duplicate`,ruleA:t.rule,ruleB:a.rule});continue}let o=b(t,a);!o&&!t.rule.when&&T(t,a)?n.push({kind:`shadowed`,shadowedIndex:a.index,shadowedRule:a.rule,shadowingIndex:t.index,shadowingRule:t.rule}):o&&!a.rule.when&&T(a,t)&&n.push({kind:`shadowed`,shadowedIndex:t.index,shadowedRule:t.rule,shadowingIndex:a.index,shadowingRule:a.rule})}return n}function D(e){}var O=D;function k(e,t,n,r,i){let a=new Set,o=[];for(let s of r)a.has(s)||(a.add(s),x(e,t,n,s,i)?.rule.effect===`allow`&&o.push(s));return o}function A(e,t,n,r){let i=r===void 0,a=[];for(let o of e)y(o,t,n,void 0,r,i)&&a.push(o.rule);return a}function j(e=[],t={}){if(t.maxConflicts!==void 0&&(!Number.isFinite(t.maxConflicts)||t.maxConflicts<0))throw new c(`maxConflicts must be a finite non-negative number.`);if(t.onConflict!==void 0&&typeof t.onConflict!=`function`)throw new c(`onConflict must be a function.`);let{maxConflicts:n=1/0}=t,r=new Set,i=[];for(let t of e)if(Array.isArray(t))for(let e of t)i.push(e);else i.push(t);let a=i.map((e,t)=>p(e,t));O(a);function o(e){if(r.size!==0)for(let t of r)try{t(e)}catch{}}function s(e,t,n,r){let i=S(x(a,e,t,n,r));return o({action:n,data:r,decision:i,principal:e,resource:t,type:`decision`}),i}function l(e){let{action:t,data:n,principal:r,resource:i}=e;return _(r),s(r,i,t,n)}function u(e,t){return t.map(t=>({...s(e,t.resource,t.action,t.data),action:t.action,resource:t.resource}))}function d(e,t){return t.length===0?[]:(_(e),u(e,t))}function f(e){let{data:t,knownActions:n,principal:r,resource:i}=e;return _(r),k(a,r,i,n,t)}function m(e){let{data:t,principal:n,resource:r}=e;return _(n),A(a,n,r,t)}function h(e){let{action:t,data:n,principal:r,resource:i}=e;_(r);let o=[];for(let e of a)y(e,r,i,t,n)&&o.push(e);let s;for(let e of o)(!s||b(s,e))&&(s=e);let c=S(s);return{candidates:o.map(e=>({index:e.index,priority:e.priority,rule:e.rule,score:e.score,won:e===s})),decision:c}}function v(e){g(e);let t={attributes:e.attributes?structuredClone(e.attributes):void 0,id:e.id,roles:[...e.roles]};return{allowedActions:e=>k(a,t,e.resource,e.knownActions,e.data),checkAll:e=>e.length===0?[]:u(t,e),explain:e=>s(t,e.resource,e.action,e.data),rulesInScope:e=>A(a,t,e.resource,e.data),trace:e=>h({action:e.action,data:e.data,principal:t,resource:e.resource})}}let C;function w(){return C??=Object.freeze(E(a,n))}if(t.strict||t.onConflict){let e=w();if(e.length>0&&(t.onConflict&&e.forEach(t.onConflict),t.strict)){let t=e.map(e=>e.kind===`duplicate`?`Rule[${e.indexB}] ${e.kind} of Rule[${e.indexA}]`:`Rule[${e.shadowedIndex}] ${e.kind} by Rule[${e.shadowingIndex}]`).join(`; `);throw new c(`${e.length} rule conflict(s) detected: ${t}`)}}function T(e,t){r.add(e);let n=()=>r.delete(e);if(t?.signal){if(t.signal.aborted)return r.delete(e),()=>{};t.signal.addEventListener(`abort`,n,{once:!0})}return()=>{r.delete(e),t?.signal?.removeEventListener(`abort`,n)}}return{allowedActions:f,checkAll:d,detectConflicts:w,explain:l,forUser:v,rulesInScope:m,tap:T,trace:h}}exports.ANONYMOUS=o,exports.WILDCARD=a,exports.WardConfigError=c,exports.WardError=s,exports.WardPredicateError=l,exports.allow=t,exports.createWard=j,exports.deny=n,exports.matchesPattern=m,exports.owns=i,exports.patternCovers=h,exports.predicate=r,exports.ruleFor=e;
|
|
2
2
|
//# sourceMappingURL=ward.cjs.map
|