@vielzeug/ward 2.3.0 → 26.10.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.
Files changed (70) hide show
  1. package/README.md +30 -35
  2. package/dist/_compile.cjs +1 -1
  3. package/dist/_compile.cjs.map +1 -1
  4. package/dist/_compile.d.ts +5 -23
  5. package/dist/_compile.d.ts.map +1 -1
  6. package/dist/_compile.js +1 -42
  7. package/dist/_compile.js.map +1 -1
  8. package/dist/_match.cjs +1 -1
  9. package/dist/_match.cjs.map +1 -1
  10. package/dist/_match.d.ts +9 -19
  11. package/dist/_match.d.ts.map +1 -1
  12. package/dist/_match.js +1 -55
  13. package/dist/_match.js.map +1 -1
  14. package/dist/constants.js +1 -5
  15. package/dist/constants.js.map +1 -1
  16. package/dist/errors.cjs +1 -1
  17. package/dist/errors.cjs.map +1 -1
  18. package/dist/errors.d.ts +3 -16
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/errors.js +1 -15
  21. package/dist/errors.js.map +1 -1
  22. package/dist/factory.cjs +1 -1
  23. package/dist/factory.cjs.map +1 -1
  24. package/dist/factory.d.ts +16 -10
  25. package/dist/factory.d.ts.map +1 -1
  26. package/dist/factory.js +1 -137
  27. package/dist/factory.js.map +1 -1
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.ts +4 -4
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -6
  32. package/dist/resource.cjs +1 -1
  33. package/dist/resource.cjs.map +1 -1
  34. package/dist/resource.d.ts +0 -18
  35. package/dist/resource.d.ts.map +1 -1
  36. package/dist/resource.js +1 -16
  37. package/dist/resource.js.map +1 -1
  38. package/dist/rules.cjs +2 -0
  39. package/dist/rules.cjs.map +1 -0
  40. package/dist/rules.d.ts +14 -0
  41. package/dist/rules.d.ts.map +1 -0
  42. package/dist/rules.js +2 -0
  43. package/dist/rules.js.map +1 -0
  44. package/dist/types.d.ts +68 -133
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/ward.cjs +1 -1
  47. package/dist/ward.cjs.map +1 -1
  48. package/dist/ward.iife.js +1 -1
  49. package/dist/ward.iife.js.map +1 -1
  50. package/dist/ward.js +1 -1
  51. package/dist/ward.js.map +1 -1
  52. package/package.json +10 -7
  53. package/dist/_conflict.cjs +0 -2
  54. package/dist/_conflict.cjs.map +0 -1
  55. package/dist/_conflict.d.ts +0 -10
  56. package/dist/_conflict.d.ts.map +0 -1
  57. package/dist/_conflict.js +0 -51
  58. package/dist/_conflict.js.map +0 -1
  59. package/dist/_dev.cjs +0 -2
  60. package/dist/_dev.cjs.map +0 -1
  61. package/dist/_dev.d.ts +0 -8
  62. package/dist/_dev.d.ts.map +0 -1
  63. package/dist/_dev.js +0 -6
  64. package/dist/_dev.js.map +0 -1
  65. package/dist/builder.cjs +0 -2
  66. package/dist/builder.cjs.map +0 -1
  67. package/dist/builder.d.ts +0 -71
  68. package/dist/builder.d.ts.map +0 -1
  69. package/dist/builder.js +0 -39
  70. package/dist/builder.js.map +0 -1
package/dist/_dev.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["import type { CompiledEntry } from './_compile';\n\nimport { ANONYMOUS } from './constants';\n\nconst isDev = !(globalThis as { __WARD_PROD__?: boolean }).__WARD_PROD__;\n\n/** @internal @security Messages may include user-supplied role names. */\nfunction warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/ward] ${msg}`);\n}\n\n/**\n * Emit a development-only warning when an ANONYMOUS-role rule has a `when` predicate.\n * The predicate is skipped for unauthenticated principals, so the rule can never match\n * anonymous requests — likely a policy authoring mistake.\n */\nexport function warnAnonymousPredicates<TAction extends string, TData>(\n entries: readonly CompiledEntry<TAction, TData>[],\n): void {\n if (!isDev) return;\n\n for (const entry of entries) {\n if (!entry.rule.when) continue;\n\n const hasAnonymous = entry.roles.includes(ANONYMOUS);\n const hasOnlyAnonymous = hasAnonymous && entry.roles.length === 1;\n\n if (hasOnlyAnonymous) {\n warn(\n `Rule[${entry.index}] pairs role '${ANONYMOUS}' with a \\`when\\` predicate. ` +\n 'Predicates are skipped for unauthenticated principals, so this rule can never match anonymous requests.',\n );\n }\n }\n}\n"],"mappings":";AAgBA,SAAgB,EACd,GACM,CAgBR"}
package/dist/builder.cjs DELETED
@@ -1,2 +0,0 @@
1
- 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}}};exports.allow=t,exports.deny=n,exports.predicate=r;
2
- //# sourceMappingURL=builder.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"builder.cjs","names":[],"sources":["../src/builder.ts"],"sourcesContent":["import type { WILDCARD } from './constants';\nimport type { WardPredicate, WardRule } from './types';\n\n// ---------------------------------------------------------------------------\n// RuleOptions — shared options for allow/deny\n// ---------------------------------------------------------------------------\n\ntype RuleOptions<TData> = {\n priority?: number;\n when?: WardPredicate<TData>;\n};\n\n// ---------------------------------------------------------------------------\n// buildRules — shared implementation\n// ---------------------------------------------------------------------------\n\nfunction buildRules<TAction extends string, TData>(\n effect: 'allow' | 'deny',\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options: RuleOptions<TData> | undefined,\n): WardRule<TAction, TData>[] {\n return actions.map((action) => ({\n action,\n effect,\n ...(options?.priority !== undefined ? { priority: options.priority } : {}),\n resource,\n role,\n ...(options?.when !== undefined ? { when: options.when } : {}),\n }));\n}\n\n// ---------------------------------------------------------------------------\n// allow / deny — ergonomic factories\n// ---------------------------------------------------------------------------\n\n/**\n * Creates one `WardRule` per action with `effect: 'allow'`.\n *\n * Reads naturally: \"allow editor to read/update posts\".\n *\n * @example\n * ```ts\n * allow('editor', 'posts', ['read', 'update'])\n * allow(['editor', 'admin'], 'posts:*', ['read', 'update'], { when: predicate.owns('authorId') })\n * ```\n */\nexport function allow<TAction extends string = string, TData = unknown>(\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options?: RuleOptions<TData>,\n): WardRule<TAction, TData>[] {\n return buildRules('allow', role, resource, actions, options);\n}\n\n/**\n * Creates one `WardRule` per action with `effect: 'deny'`.\n *\n * Reads naturally: \"deny blocked from reading posts\".\n *\n * @example\n * ```ts\n * deny('blocked', 'posts', ['read', 'update'])\n * deny('guest', WILDCARD, [WILDCARD], { priority: 10 })\n * ```\n */\nexport function deny<TAction extends string = string, TData = unknown>(\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options?: RuleOptions<TData>,\n): WardRule<TAction, TData>[] {\n return buildRules('deny', role, resource, actions, options);\n}\n\n// ---------------------------------------------------------------------------\n// predicate — grouped predicate helpers (R10)\n// ---------------------------------------------------------------------------\n\n/**\n * Grouped predicate factories. Import as a namespace to avoid name collisions:\n * ```ts\n * import { predicate } from '@vielzeug/ward';\n * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })\n * allow('user', 'posts:*', ['read'], { when: predicate.and(predicate.owns('authorId'), myPred) })\n * ```\n */\nexport const predicate = {\n /**\n * Returns a `WardPredicate` that combines all given predicates with AND semantics —\n * all must return `true` for the rule to match.\n */\n and<TData = unknown>(...preds: WardPredicate<TData>[]): WardPredicate<TData> {\n return (ctx) => preds.every((p) => p(ctx));\n },\n\n /**\n * Returns a `WardPredicate` that inverts the given predicate.\n */\n not<TData = unknown>(pred: WardPredicate<TData>): WardPredicate<TData> {\n return (ctx) => !pred(ctx);\n },\n\n /**\n * Returns a `WardPredicate` that combines all given predicates with OR semantics —\n * at least one must return `true` for the rule to match.\n */\n or<TData = unknown>(...preds: WardPredicate<TData>[]): WardPredicate<TData> {\n return (ctx) => preds.some((p) => p(ctx));\n },\n\n /**\n * Returns a `WardPredicate` that checks whether the data object's `attributeKey` field\n * matches the principal's `id`. Use to express ownership constraints.\n *\n * Must be used with a rule that requires authentication (non-`ANONYMOUS` role).\n * Predicates only execute for authenticated principals — pairing `owns` with an\n * `ANONYMOUS`-role rule produces a rule that can never match because the predicate\n * is skipped for unauthenticated requests.\n *\n * @example\n * ```ts\n * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })\n * ```\n */\n owns<TData = unknown>(\n attributeKey: [keyof TData] extends [never] ? string : keyof TData & string,\n ): WardPredicate<TData> {\n return ({ data, principal }) => {\n if (!data || typeof data !== 'object') return false;\n\n const record = data as Record<string, unknown>;\n\n if (!Object.hasOwn(record, attributeKey)) return false;\n\n return record[attributeKey] === principal.id;\n };\n },\n} as const;\n"],"mappings":"AAgBA,SAAS,EACP,EACA,EACA,EACA,EACA,EAC4B,CAC5B,OAAO,EAAQ,IAAK,IAAY,CAC9B,SACA,SACA,GAAI,GAAS,WAAa,IAAA,GAA6C,CAAC,EAAlC,CAAE,SAAU,EAAQ,QAAS,EACnE,WACA,OACA,GAAI,GAAS,OAAS,IAAA,GAAqC,CAAC,EAA1B,CAAE,KAAM,EAAQ,IAAK,CACzD,EAAE,CACJ,CAiBA,SAAgB,EACd,EACA,EACA,EACA,EAC4B,CAC5B,OAAO,EAAW,QAAS,EAAM,EAAU,EAAS,CAAO,CAC7D,CAaA,SAAgB,EACd,EACA,EACA,EACA,EAC4B,CAC5B,OAAO,EAAW,OAAQ,EAAM,EAAU,EAAS,CAAO,CAC5D,CAcA,IAAa,EAAY,CAKvB,IAAqB,GAAG,EAAqD,CAC3E,MAAQ,IAAQ,EAAM,MAAO,GAAM,EAAE,CAAG,CAAC,CAC3C,EAKA,IAAqB,EAAkD,CACrE,MAAQ,IAAQ,CAAC,EAAK,CAAG,CAC3B,EAMA,GAAoB,GAAG,EAAqD,CAC1E,MAAQ,IAAQ,EAAM,KAAM,GAAM,EAAE,CAAG,CAAC,CAC1C,EAgBA,KACE,EACsB,CACtB,OAAQ,CAAE,OAAM,eAAgB,CAC9B,GAAI,CAAC,GAAQ,OAAO,GAAS,SAAU,MAAO,GAE9C,IAAM,EAAS,EAIf,OAFK,OAAO,OAAO,EAAQ,CAAY,EAEhC,EAAO,KAAkB,EAAU,GAFO,EAGnD,CACF,CACF"}
package/dist/builder.d.ts DELETED
@@ -1,71 +0,0 @@
1
- import type { WILDCARD } from './constants';
2
- import type { WardPredicate, WardRule } from './types';
3
- type RuleOptions<TData> = {
4
- priority?: number;
5
- when?: WardPredicate<TData>;
6
- };
7
- /**
8
- * Creates one `WardRule` per action with `effect: 'allow'`.
9
- *
10
- * Reads naturally: "allow editor to read/update posts".
11
- *
12
- * @example
13
- * ```ts
14
- * allow('editor', 'posts', ['read', 'update'])
15
- * allow(['editor', 'admin'], 'posts:*', ['read', 'update'], { when: predicate.owns('authorId') })
16
- * ```
17
- */
18
- export declare function allow<TAction extends string = string, TData = unknown>(role: string | readonly string[], resource: string | typeof WILDCARD, actions: readonly (TAction | NoInfer<typeof WILDCARD>)[], options?: RuleOptions<TData>): WardRule<TAction, TData>[];
19
- /**
20
- * Creates one `WardRule` per action with `effect: 'deny'`.
21
- *
22
- * Reads naturally: "deny blocked from reading posts".
23
- *
24
- * @example
25
- * ```ts
26
- * deny('blocked', 'posts', ['read', 'update'])
27
- * deny('guest', WILDCARD, [WILDCARD], { priority: 10 })
28
- * ```
29
- */
30
- export declare function deny<TAction extends string = string, TData = unknown>(role: string | readonly string[], resource: string | typeof WILDCARD, actions: readonly (TAction | NoInfer<typeof WILDCARD>)[], options?: RuleOptions<TData>): WardRule<TAction, TData>[];
31
- /**
32
- * Grouped predicate factories. Import as a namespace to avoid name collisions:
33
- * ```ts
34
- * import { predicate } from '@vielzeug/ward';
35
- * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })
36
- * allow('user', 'posts:*', ['read'], { when: predicate.and(predicate.owns('authorId'), myPred) })
37
- * ```
38
- */
39
- export declare const predicate: {
40
- /**
41
- * Returns a `WardPredicate` that combines all given predicates with AND semantics —
42
- * all must return `true` for the rule to match.
43
- */
44
- readonly and: <TData = unknown>(...preds: WardPredicate<TData>[]) => WardPredicate<TData>;
45
- /**
46
- * Returns a `WardPredicate` that inverts the given predicate.
47
- */
48
- readonly not: <TData = unknown>(pred: WardPredicate<TData>) => WardPredicate<TData>;
49
- /**
50
- * Returns a `WardPredicate` that combines all given predicates with OR semantics —
51
- * at least one must return `true` for the rule to match.
52
- */
53
- readonly or: <TData = unknown>(...preds: WardPredicate<TData>[]) => WardPredicate<TData>;
54
- /**
55
- * Returns a `WardPredicate` that checks whether the data object's `attributeKey` field
56
- * matches the principal's `id`. Use to express ownership constraints.
57
- *
58
- * Must be used with a rule that requires authentication (non-`ANONYMOUS` role).
59
- * Predicates only execute for authenticated principals — pairing `owns` with an
60
- * `ANONYMOUS`-role rule produces a rule that can never match because the predicate
61
- * is skipped for unauthenticated requests.
62
- *
63
- * @example
64
- * ```ts
65
- * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })
66
- * ```
67
- */
68
- readonly owns: <TData = unknown>(attributeKey: [keyof TData] extends [never] ? string : keyof TData & string) => WardPredicate<TData>;
69
- };
70
- export {};
71
- //# sourceMappingURL=builder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAMvD,KAAK,WAAW,CAAC,KAAK,IAAI;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAC7B,CAAC;AA2BF;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,GAAG,OAAO,EACpE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAChC,QAAQ,EAAE,MAAM,GAAG,OAAO,QAAQ,EAClC,OAAO,EAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,QAAQ,CAAC,CAAC,EAAE,EACxD,OAAO,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,GAC3B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAE5B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,GAAG,OAAO,EACnE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAChC,QAAQ,EAAE,MAAM,GAAG,OAAO,QAAQ,EAClC,OAAO,EAAE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,QAAQ,CAAC,CAAC,EAAE,EACxD,OAAO,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,GAC3B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAE5B;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;IACpB;;;OAGG;mBACC,KAAK,sBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,KAAG,aAAa,CAAC,KAAK,CAAC;IAI5E;;OAEG;mBACC,KAAK,kBAAkB,aAAa,CAAC,KAAK,CAAC,KAAG,aAAa,CAAC,KAAK,CAAC;IAItE;;;OAGG;kBACA,KAAK,sBAAsB,aAAa,CAAC,KAAK,CAAC,EAAE,KAAG,aAAa,CAAC,KAAK,CAAC;IAI3E;;;;;;;;;;;;;OAaG;oBACE,KAAK,0BACM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,MAAM,KAAK,GAAG,MAAM,KAC1E,aAAa,CAAC,KAAK,CAAC;CAWf,CAAC"}
package/dist/builder.js DELETED
@@ -1,39 +0,0 @@
1
- //#region src/builder.ts
2
- function e(e, t, n, r, i) {
3
- return r.map((r) => ({
4
- action: r,
5
- effect: e,
6
- ...i?.priority === void 0 ? {} : { priority: i.priority },
7
- resource: n,
8
- role: t,
9
- ...i?.when === void 0 ? {} : { when: i.when }
10
- }));
11
- }
12
- function t(t, n, r, i) {
13
- return e("allow", t, n, r, i);
14
- }
15
- function n(t, n, r, i) {
16
- return e("deny", t, n, r, i);
17
- }
18
- var r = {
19
- and(...e) {
20
- return (t) => e.every((e) => e(t));
21
- },
22
- not(e) {
23
- return (t) => !e(t);
24
- },
25
- or(...e) {
26
- return (t) => e.some((e) => e(t));
27
- },
28
- owns(e) {
29
- return ({ data: t, principal: n }) => {
30
- if (!t || typeof t != "object") return !1;
31
- let r = t;
32
- return Object.hasOwn(r, e) ? r[e] === n.id : !1;
33
- };
34
- }
35
- };
36
- //#endregion
37
- export { t as allow, n as deny, r as predicate };
38
-
39
- //# sourceMappingURL=builder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"builder.js","names":[],"sources":["../src/builder.ts"],"sourcesContent":["import type { WILDCARD } from './constants';\nimport type { WardPredicate, WardRule } from './types';\n\n// ---------------------------------------------------------------------------\n// RuleOptions — shared options for allow/deny\n// ---------------------------------------------------------------------------\n\ntype RuleOptions<TData> = {\n priority?: number;\n when?: WardPredicate<TData>;\n};\n\n// ---------------------------------------------------------------------------\n// buildRules — shared implementation\n// ---------------------------------------------------------------------------\n\nfunction buildRules<TAction extends string, TData>(\n effect: 'allow' | 'deny',\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options: RuleOptions<TData> | undefined,\n): WardRule<TAction, TData>[] {\n return actions.map((action) => ({\n action,\n effect,\n ...(options?.priority !== undefined ? { priority: options.priority } : {}),\n resource,\n role,\n ...(options?.when !== undefined ? { when: options.when } : {}),\n }));\n}\n\n// ---------------------------------------------------------------------------\n// allow / deny — ergonomic factories\n// ---------------------------------------------------------------------------\n\n/**\n * Creates one `WardRule` per action with `effect: 'allow'`.\n *\n * Reads naturally: \"allow editor to read/update posts\".\n *\n * @example\n * ```ts\n * allow('editor', 'posts', ['read', 'update'])\n * allow(['editor', 'admin'], 'posts:*', ['read', 'update'], { when: predicate.owns('authorId') })\n * ```\n */\nexport function allow<TAction extends string = string, TData = unknown>(\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options?: RuleOptions<TData>,\n): WardRule<TAction, TData>[] {\n return buildRules('allow', role, resource, actions, options);\n}\n\n/**\n * Creates one `WardRule` per action with `effect: 'deny'`.\n *\n * Reads naturally: \"deny blocked from reading posts\".\n *\n * @example\n * ```ts\n * deny('blocked', 'posts', ['read', 'update'])\n * deny('guest', WILDCARD, [WILDCARD], { priority: 10 })\n * ```\n */\nexport function deny<TAction extends string = string, TData = unknown>(\n role: string | readonly string[],\n resource: string | typeof WILDCARD,\n actions: readonly (TAction | NoInfer<typeof WILDCARD>)[],\n options?: RuleOptions<TData>,\n): WardRule<TAction, TData>[] {\n return buildRules('deny', role, resource, actions, options);\n}\n\n// ---------------------------------------------------------------------------\n// predicate — grouped predicate helpers (R10)\n// ---------------------------------------------------------------------------\n\n/**\n * Grouped predicate factories. Import as a namespace to avoid name collisions:\n * ```ts\n * import { predicate } from '@vielzeug/ward';\n * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })\n * allow('user', 'posts:*', ['read'], { when: predicate.and(predicate.owns('authorId'), myPred) })\n * ```\n */\nexport const predicate = {\n /**\n * Returns a `WardPredicate` that combines all given predicates with AND semantics —\n * all must return `true` for the rule to match.\n */\n and<TData = unknown>(...preds: WardPredicate<TData>[]): WardPredicate<TData> {\n return (ctx) => preds.every((p) => p(ctx));\n },\n\n /**\n * Returns a `WardPredicate` that inverts the given predicate.\n */\n not<TData = unknown>(pred: WardPredicate<TData>): WardPredicate<TData> {\n return (ctx) => !pred(ctx);\n },\n\n /**\n * Returns a `WardPredicate` that combines all given predicates with OR semantics —\n * at least one must return `true` for the rule to match.\n */\n or<TData = unknown>(...preds: WardPredicate<TData>[]): WardPredicate<TData> {\n return (ctx) => preds.some((p) => p(ctx));\n },\n\n /**\n * Returns a `WardPredicate` that checks whether the data object's `attributeKey` field\n * matches the principal's `id`. Use to express ownership constraints.\n *\n * Must be used with a rule that requires authentication (non-`ANONYMOUS` role).\n * Predicates only execute for authenticated principals — pairing `owns` with an\n * `ANONYMOUS`-role rule produces a rule that can never match because the predicate\n * is skipped for unauthenticated requests.\n *\n * @example\n * ```ts\n * allow('editor', 'posts:*', ['update'], { when: predicate.owns('authorId') })\n * ```\n */\n owns<TData = unknown>(\n attributeKey: [keyof TData] extends [never] ? string : keyof TData & string,\n ): WardPredicate<TData> {\n return ({ data, principal }) => {\n if (!data || typeof data !== 'object') return false;\n\n const record = data as Record<string, unknown>;\n\n if (!Object.hasOwn(record, attributeKey)) return false;\n\n return record[attributeKey] === principal.id;\n };\n },\n} as const;\n"],"mappings":";AAgBA,SAAS,EACP,GACA,GACA,GACA,GACA,GAC4B;CAC5B,OAAO,EAAQ,KAAK,OAAY;EAC9B;EACA;EACA,GAAI,GAAS,aAAa,KAAA,IAA6C,CAAC,IAAlC,EAAE,UAAU,EAAQ,SAAS;EACnE;EACA;EACA,GAAI,GAAS,SAAS,KAAA,IAAqC,CAAC,IAA1B,EAAE,MAAM,EAAQ,KAAK;CACzD,EAAE;AACJ;AAiBA,SAAgB,EACd,GACA,GACA,GACA,GAC4B;CAC5B,OAAO,EAAW,SAAS,GAAM,GAAU,GAAS,CAAO;AAC7D;AAaA,SAAgB,EACd,GACA,GACA,GACA,GAC4B;CAC5B,OAAO,EAAW,QAAQ,GAAM,GAAU,GAAS,CAAO;AAC5D;AAcA,IAAa,IAAY;CAKvB,IAAqB,GAAG,GAAqD;EAC3E,QAAQ,MAAQ,EAAM,OAAO,MAAM,EAAE,CAAG,CAAC;CAC3C;CAKA,IAAqB,GAAkD;EACrE,QAAQ,MAAQ,CAAC,EAAK,CAAG;CAC3B;CAMA,GAAoB,GAAG,GAAqD;EAC1E,QAAQ,MAAQ,EAAM,MAAM,MAAM,EAAE,CAAG,CAAC;CAC1C;CAgBA,KACE,GACsB;EACtB,QAAQ,EAAE,SAAM,mBAAgB;GAC9B,IAAI,CAAC,KAAQ,OAAO,KAAS,UAAU,OAAO;GAE9C,IAAM,IAAS;GAIf,OAFK,OAAO,OAAO,GAAQ,CAAY,IAEhC,EAAO,OAAkB,EAAU,KAFO;EAGnD;CACF;AACF"}