@vielzeug/ward 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -35
- package/dist/_compile.cjs +1 -1
- package/dist/_compile.cjs.map +1 -1
- package/dist/_compile.d.ts +5 -23
- package/dist/_compile.d.ts.map +1 -1
- package/dist/_compile.js +40 -35
- package/dist/_compile.js.map +1 -1
- package/dist/_match.cjs +1 -1
- package/dist/_match.cjs.map +1 -1
- package/dist/_match.d.ts +9 -19
- package/dist/_match.d.ts.map +1 -1
- package/dist/_match.js +40 -37
- package/dist/_match.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +3 -16
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -3
- package/dist/errors.js.map +1 -1
- package/dist/factory.cjs +1 -1
- package/dist/factory.cjs.map +1 -1
- package/dist/factory.d.ts +14 -10
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +71 -119
- package/dist/factory.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/rules.cjs +2 -0
- package/dist/rules.cjs.map +1 -0
- package/dist/rules.d.ts +15 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +26 -0
- package/dist/rules.js.map +1 -0
- package/dist/types.d.ts +63 -132
- package/dist/types.d.ts.map +1 -1
- package/dist/ward.cjs +1 -1
- package/dist/ward.cjs.map +1 -1
- package/dist/ward.iife.js +1 -1
- package/dist/ward.iife.js.map +1 -1
- package/dist/ward.js +1 -1
- package/dist/ward.js.map +1 -1
- package/package.json +7 -7
- package/dist/_conflict.cjs +0 -2
- package/dist/_conflict.cjs.map +0 -1
- package/dist/_conflict.d.ts +0 -10
- package/dist/_conflict.d.ts.map +0 -1
- package/dist/_conflict.js +0 -51
- package/dist/_conflict.js.map +0 -1
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.d.ts +0 -8
- package/dist/_dev.d.ts.map +0 -1
- package/dist/_dev.js +0 -6
- package/dist/_dev.js.map +0 -1
- package/dist/builder.cjs +0 -2
- package/dist/builder.cjs.map +0 -1
- package/dist/builder.d.ts +0 -71
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -39
- package/dist/builder.js.map +0 -1
package/dist/_dev.js
DELETED
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
|
package/dist/builder.cjs.map
DELETED
|
@@ -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
|
package/dist/builder.d.ts.map
DELETED
|
@@ -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
|
package/dist/builder.js.map
DELETED
|
@@ -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"}
|