@zap-studio/permit 0.3.3 → 1.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/CHANGELOG.md +51 -37
- package/README.md +58 -180
- package/dist/{index.d.mts → conditions.d.ts} +4 -99
- package/dist/conditions.d.ts.map +1 -0
- package/dist/conditions.js +180 -0
- package/dist/conditions.js.map +1 -0
- package/dist/{errors.d.mts → errors.d.ts} +15 -1
- package/dist/errors.d.ts.map +1 -0
- package/dist/{errors.mjs → errors.js} +15 -1
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/policy.d.ts +91 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +156 -0
- package/dist/policy.js.map +1 -0
- package/dist/{types.d.mts → types.d.ts} +84 -6
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +10 -12
- package/dist/errors.d.mts.map +0 -1
- package/dist/errors.mjs.map +0 -1
- package/dist/helpers.d.mts +0 -32
- package/dist/helpers.d.mts.map +0 -1
- package/dist/helpers.mjs +0 -35
- package/dist/helpers.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -328
- package/dist/index.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs +0 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
//#region src/conditions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Returns a policy function that always allows the action.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const policy = createPolicy({
|
|
8
|
+
* resources,
|
|
9
|
+
* actions,
|
|
10
|
+
* rules: {
|
|
11
|
+
* post: {
|
|
12
|
+
* read: allow(), // Always allow reading posts
|
|
13
|
+
* },
|
|
14
|
+
* },
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
const allow = () => () => "allow";
|
|
19
|
+
/**
|
|
20
|
+
* Returns a policy function that always denies the action.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const policy = createPolicy({
|
|
25
|
+
* resources,
|
|
26
|
+
* actions,
|
|
27
|
+
* rules: {
|
|
28
|
+
* post: {
|
|
29
|
+
* delete: deny(), // Never allow deleting posts
|
|
30
|
+
* },
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const deny = () => () => "deny";
|
|
36
|
+
/**
|
|
37
|
+
* Returns a policy function that allows or denies based on a condition.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const policy = createPolicy({
|
|
42
|
+
* resources,
|
|
43
|
+
* actions,
|
|
44
|
+
* rules: {
|
|
45
|
+
* post: {
|
|
46
|
+
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
47
|
+
* },
|
|
48
|
+
* },
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
const when = (condition) => (context, action, resource) => condition(context, action, resource) ? "allow" : "deny";
|
|
53
|
+
/**
|
|
54
|
+
* Returns a condition function that returns `true` if all conditions are met.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const isOwnerAndPublished = and(
|
|
59
|
+
* (ctx, action, resource) => ctx.user.id === resource.authorId,
|
|
60
|
+
* (ctx, action, resource) => resource.status === "published"
|
|
61
|
+
* );
|
|
62
|
+
*
|
|
63
|
+
* rules: {
|
|
64
|
+
* post: {
|
|
65
|
+
* delete: when(isOwnerAndPublished),
|
|
66
|
+
* },
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
const and = (...conditions) => (context, action, resource) => conditions.every((condition) => condition(context, action, resource));
|
|
71
|
+
/**
|
|
72
|
+
* Returns a condition function that returns `true` if any condition is met.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const isOwnerOrAdmin = or(
|
|
77
|
+
* (ctx, action, resource) => ctx.user.id === resource.authorId,
|
|
78
|
+
* (ctx, action, resource) => ctx.user.role === "admin"
|
|
79
|
+
* );
|
|
80
|
+
*
|
|
81
|
+
* rules: {
|
|
82
|
+
* post: {
|
|
83
|
+
* write: when(isOwnerOrAdmin),
|
|
84
|
+
* },
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
const or = (...conditions) => (context, action, resource) => conditions.some((condition) => condition(context, action, resource));
|
|
89
|
+
/**
|
|
90
|
+
* Returns a condition function that negates another condition.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const isNotOwner = not((ctx, action, resource) => ctx.user.id === resource.authorId);
|
|
95
|
+
*
|
|
96
|
+
* rules: {
|
|
97
|
+
* post: {
|
|
98
|
+
* like: when(isNotOwner), // Can only like posts you don't own
|
|
99
|
+
* },
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
const not = (condition) => (context, action, resource) => !condition(context, action, resource);
|
|
104
|
+
/**
|
|
105
|
+
* Returns a condition function that checks if a context property equals a value.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* rules: {
|
|
110
|
+
* post: {
|
|
111
|
+
* write: when(has("role", "admin")), // Only admins can write
|
|
112
|
+
* },
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
const has = (key, value) => (context) => context[key] === value;
|
|
117
|
+
/**
|
|
118
|
+
* Collects all roles including inherited ones from a role hierarchy.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* type Role = "guest" | "user" | "admin";
|
|
123
|
+
*
|
|
124
|
+
* const hierarchy: RoleHierarchy<Role> = {
|
|
125
|
+
* guest: [],
|
|
126
|
+
* user: ["guest"],
|
|
127
|
+
* admin: ["user"],
|
|
128
|
+
* };
|
|
129
|
+
*
|
|
130
|
+
* collectInheritedRoles(["admin"], hierarchy);
|
|
131
|
+
* // Returns: Set { "admin", "user", "guest" }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
const collectInheritedRoles = (roles, hierarchy) => {
|
|
135
|
+
const inherited = /* @__PURE__ */ new Set();
|
|
136
|
+
const add = (role) => {
|
|
137
|
+
if (inherited.has(role)) return;
|
|
138
|
+
inherited.add(role);
|
|
139
|
+
const baseRoles = hierarchy[role] ?? [];
|
|
140
|
+
for (const baseRole of baseRoles) add(baseRole);
|
|
141
|
+
};
|
|
142
|
+
for (const role of roles) add(role);
|
|
143
|
+
return inherited;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Returns a condition function that checks if the user has a specific role.
|
|
147
|
+
* Supports role hierarchy for inherited permissions.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* // Without hierarchy
|
|
152
|
+
* rules: {
|
|
153
|
+
* post: {
|
|
154
|
+
* delete: when(hasRole("admin")),
|
|
155
|
+
* },
|
|
156
|
+
* }
|
|
157
|
+
*
|
|
158
|
+
* // With hierarchy
|
|
159
|
+
* const hierarchy = {
|
|
160
|
+
* guest: [],
|
|
161
|
+
* user: ["guest"],
|
|
162
|
+
* admin: ["user"],
|
|
163
|
+
* };
|
|
164
|
+
*
|
|
165
|
+
* rules: {
|
|
166
|
+
* post: {
|
|
167
|
+
* read: when(hasRole("guest", hierarchy)), // Admins and users can also read
|
|
168
|
+
* },
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
const hasRole = (role, hierarchy) => (context) => {
|
|
173
|
+
const userRoles = Array.isArray(context.role) ? context.role : [context.role];
|
|
174
|
+
if (hierarchy === void 0) return userRoles.includes(role);
|
|
175
|
+
return collectInheritedRoles(userRoles, hierarchy).has(role);
|
|
176
|
+
};
|
|
177
|
+
//#endregion
|
|
178
|
+
export { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when };
|
|
179
|
+
|
|
180
|
+
//# sourceMappingURL=conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.js","names":[],"sources":["../src/conditions.ts"],"sourcesContent":["/**\n * Policy and condition combinators: `allow`, `deny`, `when`, boolean\n * composition, and role helpers.\n *\n * @module @zap-studio/permit/conditions\n */\n\nimport type {\n ConditionFn,\n Context,\n HasRoleFn,\n PolicyFn,\n Role,\n RoleHierarchy,\n} from \"./types.js\";\n\n/**\n * Returns a policy function that always allows the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: allow(), // Always allow reading posts\n * },\n * },\n * });\n * ```\n */\nexport const allow =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(): PolicyFn<TContext, TAction, TResource> =>\n () =>\n \"allow\";\n\n/**\n * Returns a policy function that always denies the action.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * delete: deny(), // Never allow deleting posts\n * },\n * },\n * });\n * ```\n */\nexport const deny =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(): PolicyFn<TContext, TAction, TResource> =>\n () =>\n \"deny\";\n\n/**\n * Returns a policy function that allows or denies based on a condition.\n *\n * @example\n * ```ts\n * const policy = createPolicy({\n * resources,\n * actions,\n * rules: {\n * post: {\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n * ```\n */\nexport const when =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(\n condition: ConditionFn<TContext, TAction, TResource>\n ): PolicyFn<TContext, TAction, TResource> =>\n (context, action, resource) =>\n condition(context, action, resource) ? \"allow\" : \"deny\";\n\n/**\n * Returns a condition function that returns `true` if all conditions are met.\n *\n * @example\n * ```ts\n * const isOwnerAndPublished = and(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => resource.status === \"published\"\n * );\n *\n * rules: {\n * post: {\n * delete: when(isOwnerAndPublished),\n * },\n * }\n * ```\n */\nexport const and =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n ): ConditionFn<TContext, TAction, TResource> =>\n (context, action, resource) =>\n conditions.every((condition) => condition(context, action, resource));\n\n/**\n * Returns a condition function that returns `true` if any condition is met.\n *\n * @example\n * ```ts\n * const isOwnerOrAdmin = or(\n * (ctx, action, resource) => ctx.user.id === resource.authorId,\n * (ctx, action, resource) => ctx.user.role === \"admin\"\n * );\n *\n * rules: {\n * post: {\n * write: when(isOwnerOrAdmin),\n * },\n * }\n * ```\n */\nexport const or =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(\n ...conditions: ConditionFn<TContext, TAction, TResource>[]\n ): ConditionFn<TContext, TAction, TResource> =>\n (context, action, resource) =>\n conditions.some((condition) => condition(context, action, resource));\n\n/**\n * Returns a condition function that negates another condition.\n *\n * @example\n * ```ts\n * const isNotOwner = not((ctx, action, resource) => ctx.user.id === resource.authorId);\n *\n * rules: {\n * post: {\n * like: when(isNotOwner), // Can only like posts you don't own\n * },\n * }\n * ```\n */\nexport const not =\n <\n TContext extends Context,\n TAction extends string = string,\n TResource = unknown,\n >(\n condition: ConditionFn<TContext, TAction, TResource>\n ): ConditionFn<TContext, TAction, TResource> =>\n (context, action, resource) =>\n !condition(context, action, resource);\n\n/**\n * Returns a condition function that checks if a context property equals a value.\n *\n * @example\n * ```ts\n * rules: {\n * post: {\n * write: when(has(\"role\", \"admin\")), // Only admins can write\n * },\n * }\n * ```\n */\nexport const has =\n <TContext extends Context, K extends keyof TContext>(\n key: K,\n value: TContext[K]\n ): ConditionFn<TContext> =>\n (context) =>\n context[key] === value;\n\n/**\n * Collects all roles including inherited ones from a role hierarchy.\n *\n * @example\n * ```ts\n * type Role = \"guest\" | \"user\" | \"admin\";\n *\n * const hierarchy: RoleHierarchy<Role> = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * collectInheritedRoles([\"admin\"], hierarchy);\n * // Returns: Set { \"admin\", \"user\", \"guest\" }\n * ```\n */\nexport const collectInheritedRoles = <TRole extends Role = Role>(\n roles: TRole[],\n hierarchy: RoleHierarchy<TRole>\n): Set<TRole> => {\n const inherited = new Set<TRole>();\n\n const add = (role: TRole): void => {\n if (inherited.has(role)) {\n return;\n }\n\n inherited.add(role);\n const baseRoles = hierarchy[role] ?? [];\n for (const baseRole of baseRoles) {\n add(baseRole);\n }\n };\n\n for (const role of roles) {\n add(role);\n }\n return inherited;\n};\n\n/**\n * Returns a condition function that checks if the user has a specific role.\n * Supports role hierarchy for inherited permissions.\n *\n * @example\n * ```ts\n * // Without hierarchy\n * rules: {\n * post: {\n * delete: when(hasRole(\"admin\")),\n * },\n * }\n *\n * // With hierarchy\n * const hierarchy = {\n * guest: [],\n * user: [\"guest\"],\n * admin: [\"user\"],\n * };\n *\n * rules: {\n * post: {\n * read: when(hasRole(\"guest\", hierarchy)), // Admins and users can also read\n * },\n * }\n * ```\n */\nexport const hasRole: HasRoleFn =\n (\n role: Role,\n hierarchy?: RoleHierarchy\n ): ConditionFn<{ role: Role | Role[] }> =>\n (context) => {\n const userRoles = Array.isArray(context.role)\n ? context.role\n : [context.role];\n\n if (hierarchy === undefined) {\n return userRoles.includes(role);\n }\n\n const inherited = collectInheritedRoles(userRoles, hierarchy);\n return inherited.has(role);\n };\n"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,MAAa,oBAOT;;;;;;;;;;;;;;;;;AAkBJ,MAAa,mBAOT;;;;;;;;;;;;;;;;;AAkBJ,MAAa,QAMT,eAED,SAAS,QAAQ,aAChB,UAAU,SAAS,QAAQ,QAAQ,IAAI,UAAU;;;;;;;;;;;;;;;;;;AAmBrD,MAAa,OAMT,GAAG,gBAEJ,SAAS,QAAQ,aAChB,WAAW,OAAO,cAAc,UAAU,SAAS,QAAQ,QAAQ,CAAC;;;;;;;;;;;;;;;;;;AAmBxE,MAAa,MAMT,GAAG,gBAEJ,SAAS,QAAQ,aAChB,WAAW,MAAM,cAAc,UAAU,SAAS,QAAQ,QAAQ,CAAC;;;;;;;;;;;;;;;AAgBvE,MAAa,OAMT,eAED,SAAS,QAAQ,aAChB,CAAC,UAAU,SAAS,QAAQ,QAAQ;;;;;;;;;;;;;AAcxC,MAAa,OAET,KACA,WAED,YACC,QAAQ,SAAS;;;;;;;;;;;;;;;;;;AAmBrB,MAAa,yBACX,OACA,cACe;CACf,MAAM,4BAAY,IAAI,IAAW;CAEjC,MAAM,OAAO,SAAsB;EACjC,IAAI,UAAU,IAAI,IAAI,GACpB;EAGF,UAAU,IAAI,IAAI;EAClB,MAAM,YAAY,UAAU,SAAS,CAAC;EACtC,KAAK,MAAM,YAAY,WACrB,IAAI,QAAQ;CAEhB;CAEA,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI;CAEV,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,WAET,MACA,eAED,YAAY;CACX,MAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,IACxC,QAAQ,OACR,CAAC,QAAQ,IAAI;CAEjB,IAAI,cAAc,KAAA,GAChB,OAAO,UAAU,SAAS,IAAI;CAIhC,OADkB,sBAAsB,WAAW,SACpC,CAAC,CAAC,IAAI,IAAI;AAC3B"}
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Represents an error that occurs during policy evaluation or enforcement.
|
|
9
9
|
* Use this error to indicate issues related to policy logic, configuration, or execution.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { PolicyError } from "@zap-studio/permit";
|
|
14
|
+
*
|
|
15
|
+
* try {
|
|
16
|
+
* const policy = createPolicy(config);
|
|
17
|
+
* await policy.can(ctx, "post:read", post);
|
|
18
|
+
* } catch (error) {
|
|
19
|
+
* if (error instanceof PolicyError) {
|
|
20
|
+
* console.error("Invalid policy configuration:", error.message);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
10
24
|
*/
|
|
11
25
|
declare class PolicyError extends Error {
|
|
12
26
|
/**
|
|
@@ -18,4 +32,4 @@ declare class PolicyError extends Error {
|
|
|
18
32
|
}
|
|
19
33
|
//#endregion
|
|
20
34
|
export { PolicyError };
|
|
21
|
-
//# sourceMappingURL=errors.d.
|
|
35
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAwBa,oBAAoB;;;;;;EAM/B,YAAY"}
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Represents an error that occurs during policy evaluation or enforcement.
|
|
9
9
|
* Use this error to indicate issues related to policy logic, configuration, or execution.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { PolicyError } from "@zap-studio/permit";
|
|
14
|
+
*
|
|
15
|
+
* try {
|
|
16
|
+
* const policy = createPolicy(config);
|
|
17
|
+
* await policy.can(ctx, "post:read", post);
|
|
18
|
+
* } catch (error) {
|
|
19
|
+
* if (error instanceof PolicyError) {
|
|
20
|
+
* console.error("Invalid policy configuration:", error.message);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
10
24
|
*/
|
|
11
25
|
var PolicyError = class extends Error {
|
|
12
26
|
/**
|
|
@@ -22,4 +36,4 @@ var PolicyError = class extends Error {
|
|
|
22
36
|
//#endregion
|
|
23
37
|
export { PolicyError };
|
|
24
38
|
|
|
25
|
-
//# sourceMappingURL=errors.
|
|
39
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Error primitives for policy evaluation and configuration failures.\n *\n * @module @zap-studio/permit/errors\n */\n\n/**\n * Represents an error that occurs during policy evaluation or enforcement.\n * Use this error to indicate issues related to policy logic, configuration, or execution.\n *\n * @example\n * ```ts\n * import { PolicyError } from \"@zap-studio/permit\";\n *\n * try {\n * const policy = createPolicy(config);\n * await policy.can(ctx, \"post:read\", post);\n * } catch (error) {\n * if (error instanceof PolicyError) {\n * console.error(\"Invalid policy configuration:\", error.message);\n * }\n * }\n * ```\n */\nexport class PolicyError extends Error {\n /**\n * Creates a policy error with a human-readable message.\n *\n * @param message - Error message describing the policy failure.\n */\n constructor(message: string) {\n super(message);\n this.name = \"PolicyError\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,cAAb,cAAiC,MAAM;;;;;;CAMrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ActionPolicyMap, Actions, ConditionFn, Context, Decision, InferAction, InferPermission, InferResource, PermitConfig, Policy, PolicyFn, Resources, Role, RoleHierarchy, Rules } from "./types.js";
|
|
2
|
+
import { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when } from "./conditions.js";
|
|
3
|
+
import { PolicyError } from "./errors.js";
|
|
4
|
+
import { createPolicy, mergePoliciesAnd, mergePoliciesOr } from "./policy.js";
|
|
5
|
+
export { type ActionPolicyMap, type Actions, type ConditionFn, type Context, type Decision, type InferAction, type InferPermission, type InferResource, type PermitConfig, type Policy, PolicyError, type PolicyFn, type Resources, type Role, type RoleHierarchy, type Rules, allow, and, collectInheritedRoles, createPolicy, deny, has, hasRole, mergePoliciesAnd, mergePoliciesOr, not, or, when };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when } from "./conditions.js";
|
|
2
|
+
import { PolicyError } from "./errors.js";
|
|
3
|
+
import { createPolicy, mergePoliciesAnd, mergePoliciesOr } from "./policy.js";
|
|
4
|
+
export { PolicyError, allow, and, collectInheritedRoles, createPolicy, deny, has, hasRole, mergePoliciesAnd, mergePoliciesOr, not, or, when };
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Actions, Context, PermitConfig, Policy, Resources } from "./types.js";
|
|
2
|
+
//#region src/policy.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Creates a type-safe policy from resource schemas, actions, and rules.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { z } from "zod";
|
|
9
|
+
* import { createPolicy, allow, deny, when } from "@zap-studio/permit";
|
|
10
|
+
* import type { Resources, Actions } from "@zap-studio/permit/types";
|
|
11
|
+
*
|
|
12
|
+
* // Define resource schemas
|
|
13
|
+
* const resources = {
|
|
14
|
+
* post: z.object({
|
|
15
|
+
* id: z.string(),
|
|
16
|
+
* authorId: z.string(),
|
|
17
|
+
* visibility: z.enum(["public", "private"]),
|
|
18
|
+
* }),
|
|
19
|
+
* comment: z.object({
|
|
20
|
+
* id: z.string(),
|
|
21
|
+
* postId: z.string(),
|
|
22
|
+
* authorId: z.string(),
|
|
23
|
+
* }),
|
|
24
|
+
* } satisfies Resources;
|
|
25
|
+
*
|
|
26
|
+
* // Define actions per resource
|
|
27
|
+
* const actions = {
|
|
28
|
+
* post: ["read", "write", "delete"],
|
|
29
|
+
* comment: ["read", "write"],
|
|
30
|
+
* } as const satisfies Actions<typeof resources>;
|
|
31
|
+
*
|
|
32
|
+
* // Define context type
|
|
33
|
+
* type AppContext = { user: { id: string; role: string } };
|
|
34
|
+
*
|
|
35
|
+
* // Create the policy
|
|
36
|
+
* const policy = createPolicy<AppContext>({
|
|
37
|
+
* resources,
|
|
38
|
+
* actions,
|
|
39
|
+
* rules: {
|
|
40
|
+
* post: {
|
|
41
|
+
* read: when((ctx, action, resource) => resource.visibility === "public"),
|
|
42
|
+
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
43
|
+
* delete: deny(),
|
|
44
|
+
* },
|
|
45
|
+
* comment: {
|
|
46
|
+
* read: allow(),
|
|
47
|
+
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
48
|
+
* },
|
|
49
|
+
* },
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Check permissions
|
|
53
|
+
* const post = { id: "1", authorId: "user-1", visibility: "public" as const };
|
|
54
|
+
* await policy.can(ctx, "post:read", post); // true
|
|
55
|
+
* await policy.can(ctx, "post:write", post); // depends on ctx.user.id
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const createPolicy: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(config: PermitConfig<TContext, TResources, TActions>) => Policy<TContext, TResources, TActions>;
|
|
59
|
+
/**
|
|
60
|
+
* Merges multiple policies into one, requiring every policy to allow.
|
|
61
|
+
* If any policy denies, the merged policy denies. Policies are evaluated
|
|
62
|
+
* in parallel; every policy is invoked regardless of outcome.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const basePolicy = createPolicy({ ... });
|
|
67
|
+
* const adminPolicy = createPolicy({ ... });
|
|
68
|
+
*
|
|
69
|
+
* const merged = mergePoliciesAnd(basePolicy, adminPolicy);
|
|
70
|
+
* // Both policies must allow for the action to be permitted
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare const mergePoliciesAnd: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
74
|
+
/**
|
|
75
|
+
* Merges multiple policies into one, requiring at least one policy to allow.
|
|
76
|
+
* If every policy denies, the merged policy denies. Policies are evaluated
|
|
77
|
+
* in parallel; every policy is invoked regardless of outcome.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* const guestPolicy = createPolicy({ ... });
|
|
82
|
+
* const memberPolicy = createPolicy({ ... });
|
|
83
|
+
*
|
|
84
|
+
* const merged = mergePoliciesOr(guestPolicy, memberPolicy);
|
|
85
|
+
* // If either policy allows, the action is permitted
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
declare const mergePoliciesOr: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { createPolicy, mergePoliciesAnd, mergePoliciesOr };
|
|
91
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","names":[],"sources":["../src/policy.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8Ga,eACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,aAE/C,QAAQ,aAAa,UAAU,YAAY,cAC1C,OAAO,UAAU,YAAY;;;;;;;;;;;;;;;cAgJnB,mBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY;;;;;;;;;;;;;;;cAiBnB,kBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { PolicyError } from "./errors.js";
|
|
2
|
+
import { createStandardValidator } from "@zap-studio/validation";
|
|
3
|
+
//#region src/policy.ts
|
|
4
|
+
/**
|
|
5
|
+
* Splits a typed `resource:action` permission string into its parts.
|
|
6
|
+
* Returns `null` when the string is malformed (missing/empty part or extra
|
|
7
|
+
* segments) or `resourceType` is not one of `actions`' keys.
|
|
8
|
+
*/
|
|
9
|
+
const parsePermission = (permission, actions) => {
|
|
10
|
+
const isValidResourceKey = (value) => Object.keys(actions).includes(value);
|
|
11
|
+
const [resourceTypeValue, actionValue, ...rest] = permission.split(":");
|
|
12
|
+
if (resourceTypeValue === void 0 || resourceTypeValue.length === 0 || actionValue === void 0 || actionValue.length === 0 || rest.length > 0 || !isValidResourceKey(resourceTypeValue)) return null;
|
|
13
|
+
return {
|
|
14
|
+
action: actionValue,
|
|
15
|
+
resourceType: resourceTypeValue
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Creates a type-safe policy from resource schemas, actions, and rules.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { z } from "zod";
|
|
24
|
+
* import { createPolicy, allow, deny, when } from "@zap-studio/permit";
|
|
25
|
+
* import type { Resources, Actions } from "@zap-studio/permit/types";
|
|
26
|
+
*
|
|
27
|
+
* // Define resource schemas
|
|
28
|
+
* const resources = {
|
|
29
|
+
* post: z.object({
|
|
30
|
+
* id: z.string(),
|
|
31
|
+
* authorId: z.string(),
|
|
32
|
+
* visibility: z.enum(["public", "private"]),
|
|
33
|
+
* }),
|
|
34
|
+
* comment: z.object({
|
|
35
|
+
* id: z.string(),
|
|
36
|
+
* postId: z.string(),
|
|
37
|
+
* authorId: z.string(),
|
|
38
|
+
* }),
|
|
39
|
+
* } satisfies Resources;
|
|
40
|
+
*
|
|
41
|
+
* // Define actions per resource
|
|
42
|
+
* const actions = {
|
|
43
|
+
* post: ["read", "write", "delete"],
|
|
44
|
+
* comment: ["read", "write"],
|
|
45
|
+
* } as const satisfies Actions<typeof resources>;
|
|
46
|
+
*
|
|
47
|
+
* // Define context type
|
|
48
|
+
* type AppContext = { user: { id: string; role: string } };
|
|
49
|
+
*
|
|
50
|
+
* // Create the policy
|
|
51
|
+
* const policy = createPolicy<AppContext>({
|
|
52
|
+
* resources,
|
|
53
|
+
* actions,
|
|
54
|
+
* rules: {
|
|
55
|
+
* post: {
|
|
56
|
+
* read: when((ctx, action, resource) => resource.visibility === "public"),
|
|
57
|
+
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
58
|
+
* delete: deny(),
|
|
59
|
+
* },
|
|
60
|
+
* comment: {
|
|
61
|
+
* read: allow(),
|
|
62
|
+
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
63
|
+
* },
|
|
64
|
+
* },
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* // Check permissions
|
|
68
|
+
* const post = { id: "1", authorId: "user-1", visibility: "public" as const };
|
|
69
|
+
* await policy.can(ctx, "post:read", post); // true
|
|
70
|
+
* await policy.can(ctx, "post:write", post); // depends on ctx.user.id
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
const createPolicy = (config) => {
|
|
74
|
+
const { rules, resources, actions } = config;
|
|
75
|
+
const validators = /* @__PURE__ */ new Map();
|
|
76
|
+
const getValidatedResource = async (resourceType, resource) => {
|
|
77
|
+
const validator = validators.get(resourceType);
|
|
78
|
+
if (validator === void 0) return null;
|
|
79
|
+
try {
|
|
80
|
+
const result = await validator(resource);
|
|
81
|
+
if (result.issues) return null;
|
|
82
|
+
return result.value;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.warn(`Resource validation failed for ${String(resourceType)}: ${String(error)}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const hasAllowedAction = (resourceType, action) => actions[resourceType]?.includes(action) ?? false;
|
|
89
|
+
const evaluatePolicy = (context, resourceType, action, resource) => {
|
|
90
|
+
const policyFn = rules[resourceType]?.[action];
|
|
91
|
+
if (policyFn === void 0) return false;
|
|
92
|
+
try {
|
|
93
|
+
return policyFn(context, action, resource) === "allow";
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn(`Policy evaluation error for ${String(resourceType)}.${action}: ${String(error)}`);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
for (const key of Object.keys(resources)) {
|
|
100
|
+
const schema = resources[key];
|
|
101
|
+
if (schema === void 0) throw new PolicyError(`Missing schema for resource: ${String(key)}`);
|
|
102
|
+
const validator = createStandardValidator(schema);
|
|
103
|
+
validators.set(key, async (input) => await validator(input));
|
|
104
|
+
}
|
|
105
|
+
return { async can(context, permission, resource) {
|
|
106
|
+
const parsedPermission = parsePermission(permission, actions);
|
|
107
|
+
if (parsedPermission === null) return false;
|
|
108
|
+
const { action, resourceType } = parsedPermission;
|
|
109
|
+
if (!hasAllowedAction(resourceType, action)) return false;
|
|
110
|
+
const validatedResource = await getValidatedResource(resourceType, resource);
|
|
111
|
+
if (validatedResource === null) return false;
|
|
112
|
+
return evaluatePolicy(context, resourceType, action, validatedResource);
|
|
113
|
+
} };
|
|
114
|
+
};
|
|
115
|
+
const mergePoliciesWithStrategy = (policies, strategy) => ({ async can(context, permission, resource) {
|
|
116
|
+
if (policies.length === 0) return false;
|
|
117
|
+
const results = (await Promise.allSettled(policies.map(async (policy) => await policy.can(context, permission, resource)))).map((result) => {
|
|
118
|
+
if (result.status === "fulfilled") return result.value;
|
|
119
|
+
return false;
|
|
120
|
+
});
|
|
121
|
+
return strategy === "and" ? results.every(Boolean) : results.some(Boolean);
|
|
122
|
+
} });
|
|
123
|
+
/**
|
|
124
|
+
* Merges multiple policies into one, requiring every policy to allow.
|
|
125
|
+
* If any policy denies, the merged policy denies. Policies are evaluated
|
|
126
|
+
* in parallel; every policy is invoked regardless of outcome.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const basePolicy = createPolicy({ ... });
|
|
131
|
+
* const adminPolicy = createPolicy({ ... });
|
|
132
|
+
*
|
|
133
|
+
* const merged = mergePoliciesAnd(basePolicy, adminPolicy);
|
|
134
|
+
* // Both policies must allow for the action to be permitted
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
const mergePoliciesAnd = (...policies) => mergePoliciesWithStrategy(policies, "and");
|
|
138
|
+
/**
|
|
139
|
+
* Merges multiple policies into one, requiring at least one policy to allow.
|
|
140
|
+
* If every policy denies, the merged policy denies. Policies are evaluated
|
|
141
|
+
* in parallel; every policy is invoked regardless of outcome.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const guestPolicy = createPolicy({ ... });
|
|
146
|
+
* const memberPolicy = createPolicy({ ... });
|
|
147
|
+
*
|
|
148
|
+
* const merged = mergePoliciesOr(guestPolicy, memberPolicy);
|
|
149
|
+
* // If either policy allows, the action is permitted
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
const mergePoliciesOr = (...policies) => mergePoliciesWithStrategy(policies, "or");
|
|
153
|
+
//#endregion
|
|
154
|
+
export { createPolicy, mergePoliciesAnd, mergePoliciesOr };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","names":[],"sources":["../src/policy.ts"],"sourcesContent":["/**\n * Policy creation and composition: `createPolicy`, `mergePoliciesAnd`, and\n * `mergePoliciesOr`.\n *\n * @module @zap-studio/permit/policy\n */\n\nimport type { StandardSchemaV1 } from \"@zap-studio/validation\";\nimport { createStandardValidator } from \"@zap-studio/validation\";\n\nimport { PolicyError } from \"./errors.js\";\nimport type {\n Actions,\n Context,\n InferAction,\n InferResource,\n PermitConfig,\n Policy,\n Resources,\n} from \"./types.js\";\n\n/**\n * Splits a typed `resource:action` permission string into its parts.\n * Returns `null` when the string is malformed (missing/empty part or extra\n * segments) or `resourceType` is not one of `actions`' keys.\n */\nconst parsePermission = <\n TResources extends Resources,\n TActions extends Actions<TResources>,\n K extends keyof TResources & keyof TActions,\n>(\n permission: `${K & string}:${InferAction<TResources, TActions, K> & string}`,\n actions: TActions\n): { action: InferAction<TResources, TActions, K>; resourceType: K } | null => {\n const isValidResourceKey = (value: string): value is K & string =>\n Object.keys(actions).includes(value);\n\n const [resourceTypeValue, actionValue, ...rest] = permission.split(\":\");\n if (\n resourceTypeValue === undefined ||\n resourceTypeValue.length === 0 ||\n actionValue === undefined ||\n actionValue.length === 0 ||\n rest.length > 0 ||\n !isValidResourceKey(resourceTypeValue)\n ) {\n return null;\n }\n\n return {\n action: actionValue,\n resourceType: resourceTypeValue,\n };\n};\n\n/**\n * Creates a type-safe policy from resource schemas, actions, and rules.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { createPolicy, allow, deny, when } from \"@zap-studio/permit\";\n * import type { Resources, Actions } from \"@zap-studio/permit/types\";\n *\n * // Define resource schemas\n * const resources = {\n * post: z.object({\n * id: z.string(),\n * authorId: z.string(),\n * visibility: z.enum([\"public\", \"private\"]),\n * }),\n * comment: z.object({\n * id: z.string(),\n * postId: z.string(),\n * authorId: z.string(),\n * }),\n * } satisfies Resources;\n *\n * // Define actions per resource\n * const actions = {\n * post: [\"read\", \"write\", \"delete\"],\n * comment: [\"read\", \"write\"],\n * } as const satisfies Actions<typeof resources>;\n *\n * // Define context type\n * type AppContext = { user: { id: string; role: string } };\n *\n * // Create the policy\n * const policy = createPolicy<AppContext>({\n * resources,\n * actions,\n * rules: {\n * post: {\n * read: when((ctx, action, resource) => resource.visibility === \"public\"),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * delete: deny(),\n * },\n * comment: {\n * read: allow(),\n * write: when((ctx, action, resource) => ctx.user.id === resource.authorId),\n * },\n * },\n * });\n *\n * // Check permissions\n * const post = { id: \"1\", authorId: \"user-1\", visibility: \"public\" as const };\n * await policy.can(ctx, \"post:read\", post); // true\n * await policy.can(ctx, \"post:write\", post); // depends on ctx.user.id\n * ```\n */\nexport const createPolicy = <\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n config: PermitConfig<TContext, TResources, TActions>\n): Policy<TContext, TResources, TActions> => {\n const { rules, resources, actions } = config;\n const validators = new Map<\n keyof TResources,\n (input: unknown) => Promise<StandardSchemaV1.Result<unknown>>\n >();\n\n const getValidatedResource = async <K extends keyof TResources>(\n resourceType: K,\n resource: InferResource<TResources, K>\n ): Promise<InferResource<TResources, K> | null> => {\n const validator = validators.get(resourceType);\n if (validator === undefined) {\n return null;\n }\n try {\n const result = await validator(resource);\n if (result.issues) {\n return null;\n }\n return result.value;\n } catch (error) {\n console.warn(\n `Resource validation failed for ${String(resourceType)}: ${String(error)}`\n );\n return null;\n }\n };\n\n const hasAllowedAction = <K extends keyof TResources & keyof TActions>(\n resourceType: K,\n action: InferAction<TResources, TActions, K>\n ): boolean => actions[resourceType]?.includes(action) ?? false;\n\n const evaluatePolicy = <K extends keyof TResources & keyof TActions>(\n context: TContext,\n resourceType: K,\n action: InferAction<TResources, TActions, K>,\n resource: InferResource<TResources, K>\n ): boolean => {\n const policyFn = rules[resourceType]?.[action];\n if (policyFn === undefined) {\n return false;\n }\n\n try {\n return policyFn(context, action, resource) === \"allow\";\n } catch (error) {\n console.warn(\n `Policy evaluation error for ${String(resourceType)}.${action}: ${String(error)}`\n );\n return false;\n }\n };\n\n for (const key of Object.keys(resources) as (keyof TResources)[]) {\n const schema = resources[key];\n if (schema === undefined) {\n throw new PolicyError(`Missing schema for resource: ${String(key)}`);\n }\n const validator = createStandardValidator(schema);\n validators.set(key, async (input: unknown) => await validator(input));\n }\n\n return {\n async can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n permission: `${K & string}:${InferAction<TResources, TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n const parsedPermission = parsePermission<TResources, TActions, K>(\n permission,\n actions\n );\n if (parsedPermission === null) {\n return false;\n }\n\n const { action, resourceType } = parsedPermission;\n if (!hasAllowedAction(resourceType, action)) {\n return false;\n }\n\n const validatedResource = await getValidatedResource(\n resourceType,\n resource\n );\n if (validatedResource === null) {\n return false;\n }\n\n return evaluatePolicy(context, resourceType, action, validatedResource);\n },\n };\n};\n\nconst mergePoliciesWithStrategy = <\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n policies: Policy<TContext, TResources, TActions>[],\n strategy: \"and\" | \"or\"\n): Policy<TContext, TResources, TActions> => ({\n async can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n permission: `${K & string}:${InferAction<TResources, TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n if (policies.length === 0) {\n return false;\n }\n\n const settled = await Promise.allSettled(\n policies.map(\n async (policy) => await policy.can(context, permission, resource)\n )\n );\n\n const results = settled.map((result) => {\n if (result.status === \"fulfilled\") {\n return result.value;\n }\n return false;\n });\n\n return strategy === \"and\" ? results.every(Boolean) : results.some(Boolean);\n },\n});\n\n/**\n * Merges multiple policies into one, requiring every policy to allow.\n * If any policy denies, the merged policy denies. Policies are evaluated\n * in parallel; every policy is invoked regardless of outcome.\n *\n * @example\n * ```ts\n * const basePolicy = createPolicy({ ... });\n * const adminPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesAnd(basePolicy, adminPolicy);\n * // Both policies must allow for the action to be permitted\n * ```\n */\nexport const mergePoliciesAnd = <\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> =>\n mergePoliciesWithStrategy(policies, \"and\");\n\n/**\n * Merges multiple policies into one, requiring at least one policy to allow.\n * If every policy denies, the merged policy denies. Policies are evaluated\n * in parallel; every policy is invoked regardless of outcome.\n *\n * @example\n * ```ts\n * const guestPolicy = createPolicy({ ... });\n * const memberPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesOr(guestPolicy, memberPolicy);\n * // If either policy allows, the action is permitted\n * ```\n */\nexport const mergePoliciesOr = <\n TContext extends Context,\n TResources extends Resources = Resources,\n TActions extends Actions<TResources> = Actions<TResources>,\n>(\n ...policies: Policy<TContext, TResources, TActions>[]\n): Policy<TContext, TResources, TActions> =>\n mergePoliciesWithStrategy(policies, \"or\");\n"],"mappings":";;;;;;;;AA0BA,MAAM,mBAKJ,YACA,YAC6E;CAC7E,MAAM,sBAAsB,UAC1B,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,KAAK;CAErC,MAAM,CAAC,mBAAmB,aAAa,GAAG,QAAQ,WAAW,MAAM,GAAG;CACtE,IACE,sBAAsB,KAAA,KACtB,kBAAkB,WAAW,KAC7B,gBAAgB,KAAA,KAChB,YAAY,WAAW,KACvB,KAAK,SAAS,KACd,CAAC,mBAAmB,iBAAiB,GAErC,OAAO;CAGT,OAAO;EACL,QAAQ;EACR,cAAc;CAChB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,MAAa,gBAKX,WAC2C;CAC3C,MAAM,EAAE,OAAO,WAAW,YAAY;CACtC,MAAM,6BAAa,IAAI,IAGrB;CAEF,MAAM,uBAAuB,OAC3B,cACA,aACiD;EACjD,MAAM,YAAY,WAAW,IAAI,YAAY;EAC7C,IAAI,cAAc,KAAA,GAChB,OAAO;EAET,IAAI;GACF,MAAM,SAAS,MAAM,UAAU,QAAQ;GACvC,IAAI,OAAO,QACT,OAAO;GAET,OAAO,OAAO;EAChB,SAAS,OAAO;GACd,QAAQ,KACN,kCAAkC,OAAO,YAAY,EAAE,IAAI,OAAO,KAAK,GACzE;GACA,OAAO;EACT;CACF;CAEA,MAAM,oBACJ,cACA,WACY,QAAQ,aAAa,EAAE,SAAS,MAAM,KAAK;CAEzD,MAAM,kBACJ,SACA,cACA,QACA,aACY;EACZ,MAAM,WAAW,MAAM,aAAa,GAAG;EACvC,IAAI,aAAa,KAAA,GACf,OAAO;EAGT,IAAI;GACF,OAAO,SAAS,SAAS,QAAQ,QAAQ,MAAM;EACjD,SAAS,OAAO;GACd,QAAQ,KACN,+BAA+B,OAAO,YAAY,EAAE,GAAG,OAAO,IAAI,OAAO,KAAK,GAChF;GACA,OAAO;EACT;CACF;CAEA,KAAK,MAAM,OAAO,OAAO,KAAK,SAAS,GAA2B;EAChE,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,YAAY,gCAAgC,OAAO,GAAG,GAAG;EAErE,MAAM,YAAY,wBAAwB,MAAM;EAChD,WAAW,IAAI,KAAK,OAAO,UAAmB,MAAM,UAAU,KAAK,CAAC;CACtE;CAEA,OAAO,EACL,MAAM,IACJ,SACA,YACA,UACkB;EAClB,MAAM,mBAAmB,gBACvB,YACA,OACF;EACA,IAAI,qBAAqB,MACvB,OAAO;EAGT,MAAM,EAAE,QAAQ,iBAAiB;EACjC,IAAI,CAAC,iBAAiB,cAAc,MAAM,GACxC,OAAO;EAGT,MAAM,oBAAoB,MAAM,qBAC9B,cACA,QACF;EACA,IAAI,sBAAsB,MACxB,OAAO;EAGT,OAAO,eAAe,SAAS,cAAc,QAAQ,iBAAiB;CACxE,EACF;AACF;AAEA,MAAM,6BAKJ,UACA,cAC4C,EAC5C,MAAM,IACJ,SACA,YACA,UACkB;CAClB,IAAI,SAAS,WAAW,GACtB,OAAO;CAST,MAAM,WAAU,MANM,QAAQ,WAC5B,SAAS,IACP,OAAO,WAAW,MAAM,OAAO,IAAI,SAAS,YAAY,QAAQ,CAClE,CACF,EAAA,CAEwB,KAAK,WAAW;EACtC,IAAI,OAAO,WAAW,aACpB,OAAO,OAAO;EAEhB,OAAO;CACT,CAAC;CAED,OAAO,aAAa,QAAQ,QAAQ,MAAM,OAAO,IAAI,QAAQ,KAAK,OAAO;AAC3E,EACF;;;;;;;;;;;;;;;AAgBA,MAAa,oBAKX,GAAG,aAEH,0BAA0B,UAAU,KAAK;;;;;;;;;;;;;;;AAgB3C,MAAa,mBAKX,GAAG,aAEH,0BAA0B,UAAU,IAAI"}
|