@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 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 call the logger; `explain()` and `checkAll()` do.
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 e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},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;
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
@@ -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\n static is(err: unknown): err is WardError {\n return err instanceof WardError;\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,MAAa,UAAkB,KAAM,CACnC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAgC,CACxC,OAAO,aAAe,CACxB,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"}
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 {
@@ -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;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS;CAG1C;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"}
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 e extends Error {
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) {
@@ -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\n static is(err: unknown): err is WardError {\n return err instanceof WardError;\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,MAAa,UAAkB,MAAM;CACnC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAgC;EACxC,OAAO,aAAe;CACxB;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"}
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.logger!==void 0&&typeof c.logger!=`function`)throw new e.WardConfigError(`logger must be a function.`);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{logger:l,maxConflicts:u=1/0}=c,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,t,n,r,i){if(!l)return;let a={...i,action:n,data:r,principal:e,resource:t};l(a)}function m(e,t,r,i){let a=n.pickWinner(f,e,t,r,i),o=n.toDecision(a);return p(e,t,r,i,o),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,u))}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}`)}}return{allowedActions:v,checkAll:_,detectConflicts:C,explain:h,forUser:x,rulesInScope:y,trace:b}}exports.createWard=s;
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
@@ -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":"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"}
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":"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"}
@@ -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,CAgNtB"}
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 { logger: m, maxConflicts: h = Infinity } = p, g = [];
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, t, n, r, i) {
28
- if (!m) return;
29
- let a = {
30
- ...i,
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(e, t, n, r, s), s;
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(_, h));
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
  }
@@ -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("./constants.cjs"),t=require("./errors.cjs"),n=require("./resource.cjs"),r=require("./factory.cjs"),i=require("./builder.cjs");exports.ANONYMOUS=e.ANONYMOUS,exports.WILDCARD=e.WILDCARD,exports.WardConfigError=t.WardConfigError,exports.WardError=t.WardError,exports.WardPredicateError=t.WardPredicateError,exports.allow=i.allow,exports.createWard=r.createWard,exports.deny=i.deny,exports.matchesPattern=n.matchesPattern,exports.owns=i.owns,exports.patternCovers=n.patternCovers,exports.predicate=i.predicate,exports.ruleFor=i.ruleFor;
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, WardLoggerContext, WardOptions, WardPredicate, WardRule, WardRulesInScopeInput, WardTrace, WardTraceCandidate, } from './types';
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
@@ -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,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,QAAQ,EACR,qBAAqB,EACrB,SAAS,EACT,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
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 { ANONYMOUS as e, WILDCARD as t } from "./constants.js";
2
- import { WardConfigError as n, WardError as r, WardPredicateError as i } from "./errors.js";
3
- import { matchesPattern as a, patternCovers as o } from "./resource.js";
4
- import { createWard as s } from "./factory.js";
5
- import { allow as c, deny as l, owns as u, predicate as d, ruleFor as f } from "./builder.js";
6
- export { e as ANONYMOUS, t as WILDCARD, n as WardConfigError, r as WardError, i as WardPredicateError, c as allow, s as createWard, l as deny, a as matchesPattern, u as owns, o as patternCovers, d as predicate, f as ruleFor };
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
- export type WardLoggerContext<TAction extends string = string, TData = unknown> = WardDecision<TAction, TData> & {
126
- action: TAction;
127
- data?: TData;
128
- principal: Principal;
129
- resource: string;
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;
@@ -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 e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},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.logger!==void 0&&typeof t.logger!=`function`)throw new c(`logger must be a function.`);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{logger:n,maxConflicts:r=1/0}=t,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,t,r,i,a){if(!n)return;let o={...a,action:r,data:i,principal:e,resource:t};n(o)}function s(e,t,n,r){let i=S(x(a,e,t,n,r));return o(e,t,n,r,i),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,r))}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}`)}}return{allowedActions:f,checkAll:d,detectConflicts:w,explain:l,forUser:v,rulesInScope:m,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;
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