@vielzeug/ward 2.1.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 +5 -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.map +1 -1
- package/dist/factory.js +26 -14
- package/dist/factory.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/types.d.ts +14 -6
- 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 +2 -13
- 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/README.md
CHANGED
|
@@ -51,5 +51,9 @@ bound.explain({ resource: 'posts', action: 'update', data: { authorId: 'u2' } })
|
|
|
51
51
|
2. `allowedActions()` takes `{ principal, resource, knownActions, data? }`.
|
|
52
52
|
3. `rulesInScope()` takes `{ principal, resource, data? }`.
|
|
53
53
|
4. `BoundWard` methods use object inputs without `principal`.
|
|
54
|
-
5. `trace()` does not
|
|
54
|
+
5. `trace()` does not fire a `decision` event; `explain()` and `checkAll()` do.
|
|
55
55
|
6. Use `explain()` directly at request boundaries instead of middleware wrappers.
|
|
56
|
+
7. Subscribe to decision events with `tap()` for logging and diagnostics:
|
|
57
|
+
```ts
|
|
58
|
+
ward.tap((event) => console.debug(`ward:${event.type}`, event.decision));
|
|
59
|
+
```
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class
|
|
1
|
+
var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{ruleIndex;constructor(e,t){let n=t instanceof Error?t.message:String(t);super(`Rule[${e}] threw: ${n}`,{cause:t}),this.ruleIndex=e}};exports.WardConfigError=t,exports.WardError=e,exports.WardPredicateError=n;
|
|
2
2
|
//# sourceMappingURL=errors.cjs.map
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all ward errors. Use `instanceof WardError` to catch any ward-originated error. */\nexport class WardError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all ward errors. Use `instanceof WardError` to catch any ward-originated error. */\nexport class WardError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a rule definition or principal is malformed. */\nexport class WardConfigError extends WardError {}\n\n/**\n * Thrown when a `when` predicate in a ward rule throws an exception.\n *\n * Catch this to distinguish predicate failures from other errors:\n * ```ts\n * try {\n * ward.explain(principal, resource, action, data);\n * } catch (e) {\n * if (e instanceof WardPredicateError) {\n * console.error(`Predicate in Rule[${e.ruleIndex}] threw`, e.cause);\n * }\n * }\n * ```\n */\nexport class WardPredicateError extends WardError {\n readonly ruleIndex: number;\n\n constructor(ruleIndex: number, cause: unknown) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n\n super(`Rule[${ruleIndex}] threw: ${msg}`, { cause });\n this.ruleIndex = ruleIndex;\n }\n}\n"],"mappings":"AACA,IAAa,EAAb,cAA+B,KAAM,CACnC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAqC,CAAU,CAAC,EAgBnC,EAAb,cAAwC,CAAU,CAChD,UAEA,YAAY,EAAmB,EAAgB,CAC7C,IAAM,EAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAEjE,MAAM,QAAQ,EAAU,WAAW,IAAO,CAAE,OAAM,CAAC,EACnD,KAAK,UAAY,CACnB,CACF"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** Base class for all ward errors. Use `instanceof WardError` to catch any ward-originated error. */
|
|
2
2
|
export declare class WardError extends Error {
|
|
3
3
|
constructor(message: string, opts?: ErrorOptions);
|
|
4
|
-
static is(err: unknown): err is WardError;
|
|
5
4
|
}
|
|
6
5
|
/** Thrown when a rule definition or principal is malformed. */
|
|
7
6
|
export declare class WardConfigError extends WardError {
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,+DAA+D;AAC/D,qBAAa,eAAgB,SAAQ,SAAS;CAAG;AAEjD;;;;;;;;;;;;;GAaG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAM9C"}
|
package/dist/errors.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
//#region src/errors.ts
|
|
2
|
-
var e = class
|
|
2
|
+
var e = class extends Error {
|
|
3
3
|
constructor(e, t) {
|
|
4
4
|
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
5
5
|
}
|
|
6
|
-
static is(t) {
|
|
7
|
-
return t instanceof e;
|
|
8
|
-
}
|
|
9
6
|
}, t = class extends e {}, n = class extends e {
|
|
10
7
|
ruleIndex;
|
|
11
8
|
constructor(e, t) {
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all ward errors. Use `instanceof WardError` to catch any ward-originated error. */\nexport class WardError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all ward errors. Use `instanceof WardError` to catch any ward-originated error. */\nexport class WardError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a rule definition or principal is malformed. */\nexport class WardConfigError extends WardError {}\n\n/**\n * Thrown when a `when` predicate in a ward rule throws an exception.\n *\n * Catch this to distinguish predicate failures from other errors:\n * ```ts\n * try {\n * ward.explain(principal, resource, action, data);\n * } catch (e) {\n * if (e instanceof WardPredicateError) {\n * console.error(`Predicate in Rule[${e.ruleIndex}] threw`, e.cause);\n * }\n * }\n * ```\n */\nexport class WardPredicateError extends WardError {\n readonly ruleIndex: number;\n\n constructor(ruleIndex: number, cause: unknown) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n\n super(`Rule[${ruleIndex}] threw: ${msg}`, { cause });\n this.ruleIndex = ruleIndex;\n }\n}\n"],"mappings":";AACA,IAAa,IAAb,cAA+B,MAAM;CACnC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAGa,IAAb,cAAqC,EAAU,CAAC,GAgBnC,IAAb,cAAwC,EAAU;CAChD;CAEA,YAAY,GAAmB,GAAgB;EAC7C,IAAM,IAAM,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;EAGjE,AADA,MAAM,QAAQ,EAAU,WAAW,KAAO,EAAE,SAAM,CAAC,GACnD,KAAK,YAAY;CACnB;AACF"}
|
package/dist/factory.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./_compile.cjs"),n=require("./_match.cjs"),r=require("./_conflict.cjs");var i=require("./_dev.cjs").warnAnonymousPredicates;function a(e,t,r,i,a){let o=new Set,s=[];for(let c of i)o.has(c)||(o.add(c),n.pickWinner(e,t,r,c,a)?.rule.effect===`allow`&&s.push(c));return s}function o(e,t,r,i){let a=i===void 0,o=[];for(let s of e)n.matchesRule(s,t,r,void 0,i,a)&&o.push(s.rule);return o}function s(s=[],c={}){if(c.
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_compile.cjs"),n=require("./_match.cjs"),r=require("./_conflict.cjs");var i=require("./_dev.cjs").warnAnonymousPredicates;function a(e,t,r,i,a){let o=new Set,s=[];for(let c of i)o.has(c)||(o.add(c),n.pickWinner(e,t,r,c,a)?.rule.effect===`allow`&&s.push(c));return s}function o(e,t,r,i){let a=i===void 0,o=[];for(let s of e)n.matchesRule(s,t,r,void 0,i,a)&&o.push(s.rule);return o}function s(s=[],c={}){if(c.maxConflicts!==void 0&&(!Number.isFinite(c.maxConflicts)||c.maxConflicts<0))throw new e.WardConfigError(`maxConflicts must be a finite non-negative number.`);if(c.onConflict!==void 0&&typeof c.onConflict!=`function`)throw new e.WardConfigError(`onConflict must be a function.`);let{maxConflicts:l=1/0}=c,u=new Set,d=[];for(let e of s)if(Array.isArray(e))for(let t of e)d.push(t);else d.push(e);let f=d.map((e,n)=>t.compileEntry(e,n));i(f);function p(e){if(u.size!==0)for(let t of u)try{t(e)}catch{}}function m(e,t,r,i){let a=n.pickWinner(f,e,t,r,i),o=n.toDecision(a);return p({action:r,data:i,decision:o,principal:e,resource:t,type:`decision`}),o}function h(e){let{action:t,data:r,principal:i,resource:a}=e;return n.validatePrincipal(i),m(i,a,t,r)}function g(e,t){return t.map(t=>({...m(e,t.resource,t.action,t.data),action:t.action,resource:t.resource}))}function _(e,t){return t.length===0?[]:(n.validatePrincipal(e),g(e,t))}function v(e){let{data:t,knownActions:r,principal:i,resource:o}=e;return n.validatePrincipal(i),a(f,i,o,r,t)}function y(e){let{data:t,principal:r,resource:i}=e;return n.validatePrincipal(r),o(f,r,i,t)}function b(e){let{action:t,data:r,principal:i,resource:a}=e;n.validatePrincipal(i);let o=[];for(let e of f)n.matchesRule(e,i,a,t,r)&&o.push(e);let s;for(let e of o)(!s||n.isOverriddenBy(s,e))&&(s=e);let c=n.toDecision(s);return{candidates:o.map(e=>({index:e.index,priority:e.priority,rule:e.rule,score:e.score,won:e===s})),decision:c}}function x(e){n.assertUserPrincipal(e);let t={attributes:e.attributes?structuredClone(e.attributes):void 0,id:e.id,roles:[...e.roles]};return{allowedActions:e=>a(f,t,e.resource,e.knownActions,e.data),checkAll:e=>e.length===0?[]:g(t,e),explain:e=>m(t,e.resource,e.action,e.data),rulesInScope:e=>o(f,t,e.resource,e.data),trace:e=>b({action:e.action,data:e.data,principal:t,resource:e.resource})}}let S;function C(){return S??=Object.freeze(r.computeConflicts(f,l))}if(c.strict||c.onConflict){let t=C();if(t.length>0&&(c.onConflict&&t.forEach(c.onConflict),c.strict)){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(`; `);throw new e.WardConfigError(`${t.length} rule conflict(s) detected: ${n}`)}}function w(e,t){u.add(e);let n=()=>u.delete(e);if(t?.signal){if(t.signal.aborted)return u.delete(e),()=>{};t.signal.addEventListener(`abort`,n,{once:!0})}return()=>{u.delete(e),t?.signal?.removeEventListener(`abort`,n)}}return{allowedActions:v,checkAll:_,detectConflicts:C,explain:h,forUser:x,rulesInScope:y,tap:w,trace:b}}exports.createWard=s;
|
|
2
2
|
//# sourceMappingURL=factory.cjs.map
|
package/dist/factory.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.cjs","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 WardLoggerContext,\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.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 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 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 const ctx = {\n ...decision,\n action,\n data,\n principal,\n resource,\n } as WardLoggerContext<TAction, TData>;\n\n logger(ctx);\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(\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 return { allowedActions, checkAll, detectConflicts, explain, forUser, rulesInScope, trace };\n}\n"],"mappings":"mHA6BA,IAAM,sBAAO,CAAA,CAAA,wBAMb,SAAS,EACP,EACA,EACA,EACA,EACA,EACW,CACX,IAAM,EAAO,IAAI,IACX,EAAoB,CAAC,EAE3B,IAAK,IAAM,KAAU,EACf,EAAK,IAAI,CAAM,IAEnB,EAAK,IAAI,CAAM,EAEA,EAAA,WAAW,EAAS,EAAW,EAAU,EAAQ,CAE5D,CAAA,EAAQ,KAAK,SAAW,SAAS,EAAO,KAAK,CAAM,GAGzD,OAAO,CACT,CAEA,SAAS,EACP,EACA,EACA,EACA,EACsC,CACtC,IAAM,EAAgB,IAAS,IAAA,GACzB,EAA+C,CAAC,EAEtD,IAAK,IAAM,KAAS,EACb,EAAA,YAAY,EAAO,EAAW,EAAU,IAAA,GAAW,EAAM,CAAa,GAE3E,EAAO,KAAK,EAAM,IAA0C,EAG9D,OAAO,CACT,CAiBA,SAAgB,EACd,EAAqF,CAAC,EACtF,EAAuC,CAAC,EAClB,CACtB,GAAI,EAAQ,SAAW,IAAA,IAAa,OAAO,EAAQ,QAAW,WAC5D,MAAM,IAAI,EAAA,gBAAgB,4BAA4B,EAGxD,GAAI,EAAQ,eAAiB,IAAA,KACvB,CAAC,OAAO,SAAS,EAAQ,YAAY,GAAK,EAAQ,aAAe,GACnE,MAAM,IAAI,EAAA,gBAAgB,oDAAoD,EAIlF,GAAI,EAAQ,aAAe,IAAA,IAAa,OAAO,EAAQ,YAAe,WACpE,MAAM,IAAI,EAAA,gBAAgB,gCAAgC,EAG5D,GAAM,CAAE,SAAQ,eAAe,KAAa,EACtC,EAAmC,CAAC,EAE1C,IAAK,IAAM,KAAS,EAClB,GAAI,MAAM,QAAQ,CAAK,EACrB,IAAK,IAAM,KAAQ,EAAO,EAAK,KAAK,CAAI,OAExC,EAAK,KAAK,CAAiC,EAI/C,IAAM,EAAU,EAAK,KAAK,EAAM,IAAM,EAAA,aAAa,EAAM,CAAC,CAAC,EAG3D,EAAK,CAAO,EAMZ,SAAS,EACP,EACA,EACA,EACA,EACA,EACM,CACN,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAM,CACV,GAAG,EACH,SACA,OACA,YACA,UACF,EAEA,EAAO,CAAG,CACZ,CAEA,SAAS,EACP,EACA,EACA,EACA,EAC8B,CAC9B,IAAM,EAAS,EAAA,WAAW,EAAS,EAAW,EAAU,EAAQ,CAAI,EAC9D,EAAW,EAAA,WAAW,CAAM,EAIlC,OAFA,EAAW,EAAW,EAAU,EAAQ,EAAM,CAAQ,EAE/C,CACT,CAQA,SAAS,EAAQ,EAAwE,CACvF,GAAM,CAAE,SAAQ,OAAM,YAAW,YAAa,EAI9C,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAe,EAAW,EAAU,EAAQ,CAAI,CACzD,CAEA,SAAS,EACP,EACA,EACsC,CACtC,OAAO,EAAO,IAAK,IAAW,CAC5B,GAAG,EAAe,EAAW,EAAM,SAAU,EAAM,OAAQ,EAAM,IAAI,EACrE,OAAQ,EAAM,OACd,SAAU,EAAM,QAClB,EAAE,CACJ,CAEA,SAAS,EACP,EACA,EACsC,CAKtC,OAJI,EAAO,SAAW,EAAU,CAAC,GAEjC,EAAA,kBAAkB,CAAS,EAEpB,EAAY,EAAW,CAAM,EACtC,CAEA,SAAS,EAAe,EAA2D,CACjF,GAAM,CAAE,OAAM,eAAc,YAAW,YAAa,EAIpD,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAmB,EAAS,EAAW,EAAU,EAAc,CAAI,CAC5E,CAEA,SAAS,EACP,EAC6D,CAC7D,GAAM,CAAE,OAAM,YAAW,YAAa,EAItC,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAiB,EAAS,EAAW,EAAU,CAAI,CAC5D,CAEA,SAAS,EAAM,EAAqE,CAClF,GAAM,CAAE,SAAQ,OAAM,YAAW,YAAa,EAE9C,EAAA,kBAAkB,CAAS,EAE3B,IAAM,EAA4C,CAAC,EAEnD,IAAK,IAAM,KAAS,EACd,EAAA,YAAY,EAAO,EAAW,EAAU,EAAQ,CAAI,GACtD,EAAS,KAAK,CAAK,EAIvB,IAAI,EAEJ,IAAK,IAAM,KAAS,GACd,CAAC,GAAU,EAAA,eAAe,EAAQ,CAAK,KAAG,EAAS,GAGzD,IAAM,EAAW,EAAA,WAAW,CAAM,EAUlC,MAAO,CAAE,WARgD,EAAS,IAAK,IAAW,CAChF,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,IAAK,IAAU,CACjB,EAES,EAAY,UAAS,CAChC,CAEA,SAAS,EAAQ,EAAqD,CACpE,EAAA,oBAAoB,CAAS,EAE7B,IAAM,EAAsB,CAC1B,WAAY,EAAU,WAAa,gBAAgB,EAAU,UAAU,EAAI,IAAA,GAC3E,GAAI,EAAU,GACd,MAAO,CAAC,GAAG,EAAU,KAAK,CAC5B,EAEA,MAAO,CACL,eAAiB,GACf,EAAmB,EAAS,EAAM,EAAM,SAAU,EAAM,aAAc,EAAM,IAAI,EAClF,SAAW,GAAY,EAAO,SAAW,EAAI,CAAC,EAAI,EAAY,EAAM,CAAM,EAC1E,QAAU,GACR,EAAe,EAAM,EAAM,SAAU,EAAM,OAAQ,EAAM,IAAI,EAC/D,aAAe,GACb,EAAiB,EAAS,EAAM,EAAM,SAAU,EAAM,IAAI,EAC5D,MAAQ,GACN,EAAM,CAAE,OAAQ,EAAM,OAAQ,KAAM,EAAM,KAAM,UAAW,EAAM,SAAU,EAAM,QAAS,CAAC,CAC/F,CACF,CAMA,IAAI,EAEJ,SAAS,GAA2D,CAClE,MAAQ,KAAmB,OAAO,OAAO,EAAA,iBAAiB,EAAS,CAAY,CAAC,CAClF,CAEA,GAAI,EAAQ,QAAU,EAAQ,WAAY,CACxC,IAAM,EAAY,EAAgB,EAElC,GAAI,EAAU,OAAS,IACjB,EAAQ,YAAY,EAAU,QAAQ,EAAQ,UAAU,EAExD,EAAQ,QAAQ,CAClB,IAAM,EAAU,EACb,IAAK,GACJ,EAAE,OAAS,YACP,QAAQ,EAAE,OAAO,IAAI,EAAE,KAAK,WAAW,EAAE,OAAO,GAChD,QAAQ,EAAE,cAAc,IAAI,EAAE,KAAK,WAAW,EAAE,eAAe,EACrE,CAAC,CACA,KAAK,IAAI,EAEZ,MAAM,IAAI,EAAA,gBAAgB,GAAG,EAAU,OAAO,8BAA8B,GAAS,CACvF,CAEJ,CAEA,MAAO,CAAE,iBAAgB,WAAU,kBAAiB,UAAS,UAAS,eAAc,OAAM,CAC5F"}
|
|
1
|
+
{"version":3,"file":"factory.cjs","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":"mHA6BA,IAAM,sBAAO,CAAA,CAAA,wBAMb,SAAS,EACP,EACA,EACA,EACA,EACA,EACW,CACX,IAAM,EAAO,IAAI,IACX,EAAoB,CAAC,EAE3B,IAAK,IAAM,KAAU,EACf,EAAK,IAAI,CAAM,IAEnB,EAAK,IAAI,CAAM,EAEA,EAAA,WAAW,EAAS,EAAW,EAAU,EAAQ,CAE5D,CAAA,EAAQ,KAAK,SAAW,SAAS,EAAO,KAAK,CAAM,GAGzD,OAAO,CACT,CAEA,SAAS,EACP,EACA,EACA,EACA,EACsC,CACtC,IAAM,EAAgB,IAAS,IAAA,GACzB,EAA+C,CAAC,EAEtD,IAAK,IAAM,KAAS,EACb,EAAA,YAAY,EAAO,EAAW,EAAU,IAAA,GAAW,EAAM,CAAa,GAE3E,EAAO,KAAK,EAAM,IAA0C,EAG9D,OAAO,CACT,CAiBA,SAAgB,EACd,EAAqF,CAAC,EACtF,EAAuC,CAAC,EAClB,CACtB,GAAI,EAAQ,eAAiB,IAAA,KACvB,CAAC,OAAO,SAAS,EAAQ,YAAY,GAAK,EAAQ,aAAe,GACnE,MAAM,IAAI,EAAA,gBAAgB,oDAAoD,EAIlF,GAAI,EAAQ,aAAe,IAAA,IAAa,OAAO,EAAQ,YAAe,WACpE,MAAM,IAAI,EAAA,gBAAgB,gCAAgC,EAG5D,GAAM,CAAE,eAAe,KAAa,EAC9B,EAAU,IAAI,IACd,EAAmC,CAAC,EAE1C,IAAK,IAAM,KAAS,EAClB,GAAI,MAAM,QAAQ,CAAK,EACrB,IAAK,IAAM,KAAQ,EAAO,EAAK,KAAK,CAAI,OAExC,EAAK,KAAK,CAAiC,EAI/C,IAAM,EAAU,EAAK,KAAK,EAAM,IAAM,EAAA,aAAa,EAAM,CAAC,CAAC,EAG3D,EAAK,CAAO,EAMZ,SAAS,EAAQ,EAAwC,CACnD,KAAQ,OAAS,EACrB,IAAK,IAAM,KAAU,EACnB,GAAI,CACF,EAAO,CAAK,CACd,MAAQ,CAER,CAEJ,CAEA,SAAS,EACP,EACA,EACA,EACA,EAC8B,CAC9B,IAAM,EAAS,EAAA,WAAW,EAAS,EAAW,EAAU,EAAQ,CAAI,EAC9D,EAAW,EAAA,WAAW,CAAM,EAIlC,OAFA,EAAQ,CAAE,SAAQ,OAAM,WAAU,YAAW,WAAU,KAAM,UAAW,CAAC,EAElE,CACT,CAQA,SAAS,EAAQ,EAAwE,CACvF,GAAM,CAAE,SAAQ,OAAM,YAAW,YAAa,EAI9C,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAe,EAAW,EAAU,EAAQ,CAAI,CACzD,CAEA,SAAS,EACP,EACA,EACsC,CACtC,OAAO,EAAO,IAAK,IAAW,CAC5B,GAAG,EAAe,EAAW,EAAM,SAAU,EAAM,OAAQ,EAAM,IAAI,EACrE,OAAQ,EAAM,OACd,SAAU,EAAM,QAClB,EAAE,CACJ,CAEA,SAAS,EACP,EACA,EACsC,CAKtC,OAJI,EAAO,SAAW,EAAU,CAAC,GAEjC,EAAA,kBAAkB,CAAS,EAEpB,EAAY,EAAW,CAAM,EACtC,CAEA,SAAS,EAAe,EAA2D,CACjF,GAAM,CAAE,OAAM,eAAc,YAAW,YAAa,EAIpD,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAmB,EAAS,EAAW,EAAU,EAAc,CAAI,CAC5E,CAEA,SAAS,EACP,EAC6D,CAC7D,GAAM,CAAE,OAAM,YAAW,YAAa,EAItC,OAFA,EAAA,kBAAkB,CAAS,EAEpB,EAAiB,EAAS,EAAW,EAAU,CAAI,CAC5D,CAEA,SAAS,EAAM,EAAqE,CAClF,GAAM,CAAE,SAAQ,OAAM,YAAW,YAAa,EAE9C,EAAA,kBAAkB,CAAS,EAE3B,IAAM,EAA4C,CAAC,EAEnD,IAAK,IAAM,KAAS,EACd,EAAA,YAAY,EAAO,EAAW,EAAU,EAAQ,CAAI,GACtD,EAAS,KAAK,CAAK,EAIvB,IAAI,EAEJ,IAAK,IAAM,KAAS,GACd,CAAC,GAAU,EAAA,eAAe,EAAQ,CAAK,KAAG,EAAS,GAGzD,IAAM,EAAW,EAAA,WAAW,CAAM,EAUlC,MAAO,CAAE,WARgD,EAAS,IAAK,IAAW,CAChF,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,IAAK,IAAU,CACjB,EAES,EAAY,UAAS,CAChC,CAEA,SAAS,EAAQ,EAAqD,CACpE,EAAA,oBAAoB,CAAS,EAE7B,IAAM,EAAsB,CAC1B,WAAY,EAAU,WAAa,gBAAgB,EAAU,UAAU,EAAI,IAAA,GAC3E,GAAI,EAAU,GACd,MAAO,CAAC,GAAG,EAAU,KAAK,CAC5B,EAEA,MAAO,CACL,eAAiB,GACf,EAAmB,EAAS,EAAM,EAAM,SAAU,EAAM,aAAc,EAAM,IAAI,EAClF,SAAW,GAAY,EAAO,SAAW,EAAI,CAAC,EAAI,EAAY,EAAM,CAAM,EAC1E,QAAU,GACR,EAAe,EAAM,EAAM,SAAU,EAAM,OAAQ,EAAM,IAAI,EAC/D,aAAe,GACb,EAAiB,EAAS,EAAM,EAAM,SAAU,EAAM,IAAI,EAC5D,MAAQ,GACN,EAAM,CAAE,OAAQ,EAAM,OAAQ,KAAM,EAAM,KAAM,UAAW,EAAM,SAAU,EAAM,QAAS,CAAC,CAC/F,CACF,CAMA,IAAI,EAEJ,SAAS,GAA2D,CAClE,MAAQ,KAAmB,OAAO,OAAO,EAAA,iBAAiB,EAAS,CAAY,CAAC,CAClF,CAEA,GAAI,EAAQ,QAAU,EAAQ,WAAY,CACxC,IAAM,EAAY,EAAgB,EAElC,GAAI,EAAU,OAAS,IACjB,EAAQ,YAAY,EAAU,QAAQ,EAAQ,UAAU,EAExD,EAAQ,QAAQ,CAClB,IAAM,EAAU,EACb,IAAK,GACJ,EAAE,OAAS,YACP,QAAQ,EAAE,OAAO,IAAI,EAAE,KAAK,WAAW,EAAE,OAAO,GAChD,QAAQ,EAAE,cAAc,IAAI,EAAE,KAAK,WAAW,EAAE,eAAe,EACrE,CAAC,CACA,KAAK,IAAI,EAEZ,MAAM,IAAI,EAAA,gBAAgB,GAAG,EAAU,OAAO,8BAA8B,GAAS,CACvF,CAEJ,CAEA,SAAS,EAAI,EAAqD,EAA6C,CAC7G,EAAQ,IAAI,CAAO,EAEnB,IAAM,MAAgB,EAAQ,OAAO,CAAO,EAE5C,GAAI,GAAM,OAAQ,CAChB,GAAI,EAAK,OAAO,QAEd,OADA,EAAQ,OAAO,CAAO,MACT,CAAC,EAEhB,EAAK,OAAO,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,CAC/D,CAEA,UAAa,CACX,EAAQ,OAAO,CAAO,EACtB,GAAM,QAAQ,oBAAoB,QAAS,CAAO,CACpD,CACF,CAEA,MAAO,CAAE,iBAAgB,WAAU,kBAAiB,UAAS,UAAS,eAAc,MAAK,OAAM,CACjG"}
|
package/dist/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAQV,IAAI,EAQJ,WAAW,EACX,QAAQ,EAIT,MAAM,SAAS,CAAC;AAqDjB;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,GAAG,OAAO,EACzE,KAAK,GAAE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,SAAS,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,EAAO,EACvF,OAAO,GAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAM,GACxC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAQV,IAAI,EAQJ,WAAW,EACX,QAAQ,EAIT,MAAM,SAAS,CAAC;AAqDjB;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,GAAG,OAAO,EACzE,KAAK,GAAE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,SAAS,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,EAAO,EACvF,OAAO,GAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAM,GACxC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAuNtB"}
|
package/dist/factory.js
CHANGED
|
@@ -16,28 +16,28 @@ 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 {
|
|
21
|
+
let { maxConflicts: m = Infinity } = p, h = /* @__PURE__ */ new Set(), g = [];
|
|
23
22
|
for (let e of l) if (Array.isArray(e)) for (let t of e) g.push(t);
|
|
24
23
|
else g.push(e);
|
|
25
24
|
let _ = g.map((e, n) => t(e, n));
|
|
26
25
|
u(_);
|
|
27
|
-
function v(e
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
action: n,
|
|
32
|
-
data: r,
|
|
33
|
-
principal: e,
|
|
34
|
-
resource: t
|
|
35
|
-
};
|
|
36
|
-
m(a);
|
|
26
|
+
function v(e) {
|
|
27
|
+
if (h.size !== 0) for (let t of h) try {
|
|
28
|
+
t(e);
|
|
29
|
+
} catch {}
|
|
37
30
|
}
|
|
38
31
|
function y(e, t, n, r) {
|
|
39
32
|
let i = a(_, e, t, n, r), s = o(i);
|
|
40
|
-
return v(
|
|
33
|
+
return v({
|
|
34
|
+
action: n,
|
|
35
|
+
data: r,
|
|
36
|
+
decision: s,
|
|
37
|
+
principal: e,
|
|
38
|
+
resource: t,
|
|
39
|
+
type: "decision"
|
|
40
|
+
}), s;
|
|
41
41
|
}
|
|
42
42
|
function b(e) {
|
|
43
43
|
let { action: t, data: n, principal: r, resource: i } = e;
|
|
@@ -102,7 +102,7 @@ function p(l = [], p = {}) {
|
|
|
102
102
|
}
|
|
103
103
|
let D;
|
|
104
104
|
function O() {
|
|
105
|
-
return D ??= Object.freeze(c(_,
|
|
105
|
+
return D ??= Object.freeze(c(_, m));
|
|
106
106
|
}
|
|
107
107
|
if (p.strict || p.onConflict) {
|
|
108
108
|
let t = O();
|
|
@@ -111,6 +111,17 @@ function p(l = [], p = {}) {
|
|
|
111
111
|
throw new e(`${t.length} rule conflict(s) detected: ${n}`);
|
|
112
112
|
}
|
|
113
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
|
+
}
|
|
114
125
|
return {
|
|
115
126
|
allowedActions: C,
|
|
116
127
|
checkAll: S,
|
|
@@ -118,6 +129,7 @@ function p(l = [], p = {}) {
|
|
|
118
129
|
explain: b,
|
|
119
130
|
forUser: E,
|
|
120
131
|
rulesInScope: w,
|
|
132
|
+
tap: k,
|
|
121
133
|
trace: T
|
|
122
134
|
};
|
|
123
135
|
}
|
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 { 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 WardLoggerContext,\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.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 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 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 const ctx = {\n ...decision,\n action,\n data,\n principal,\n resource,\n } as WardLoggerContext<TAction, TData>;\n\n logger(ctx);\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(\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 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
|
@@ -3,5 +3,5 @@ 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 { BoundWard, BoundWardAllowedActionsInput, BoundWardDecisionInput, BoundWardRulesInScopeInput, ConflictKind, NormalizedWardRule, Principal, RuleContext, UserPrincipal, Ward, WardAllowedActionsInput, WardCheck, WardConflict, WardDecision, WardDecisionInput, WardDecisionResult,
|
|
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,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,
|
|
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
|
@@ -113,6 +113,9 @@ export type Ward<TAction extends string = string, TData = unknown> = {
|
|
|
113
113
|
explain(input: WardDecisionInput<TAction, TData>): WardDecision<TAction, TData>;
|
|
114
114
|
forUser(principal: UserPrincipal): BoundWard<TAction, TData>;
|
|
115
115
|
rulesInScope(input: WardRulesInScopeInput<TData>): ReadonlyArray<Readonly<NormalizedWardRule<TAction, TData>>>;
|
|
116
|
+
tap(handler: (event: WardEvent<TAction, TData>) => void, options?: {
|
|
117
|
+
signal?: AbortSignal;
|
|
118
|
+
}): () => void;
|
|
116
119
|
trace(input: WardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;
|
|
117
120
|
};
|
|
118
121
|
export type BoundWard<TAction extends string = string, TData = unknown> = {
|
|
@@ -122,14 +125,19 @@ export type BoundWard<TAction extends string = string, TData = unknown> = {
|
|
|
122
125
|
rulesInScope(input: BoundWardRulesInScopeInput<TData>): ReadonlyArray<Readonly<NormalizedWardRule<TAction, TData>>>;
|
|
123
126
|
trace(input: BoundWardDecisionInput<TAction, TData>): WardTrace<TAction, TData>;
|
|
124
127
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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;
|
|
130
139
|
};
|
|
131
140
|
export type WardOptions<TAction extends string = string, TData = unknown> = {
|
|
132
|
-
logger?: (context: WardLoggerContext<TAction, TData>) => void;
|
|
133
141
|
maxConflicts?: number;
|
|
134
142
|
onConflict?: (conflict: WardConflict<TAction, TData>) => void;
|
|
135
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":"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
|
|
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`});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
|
|
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
|