@zap-studio/permit 0.3.3 → 0.3.4
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 +8 -0
- package/LICENSE +1 -1
- package/README.md +13 -3
- package/dist/{index.d.mts → conditions.d.ts} +4 -88
- 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} +1 -1
- package/dist/errors.d.ts.map +1 -0
- package/dist/{errors.mjs → errors.js} +1 -1
- package/dist/errors.js.map +1 -0
- package/dist/{helpers.d.mts → helpers.d.ts} +1 -1
- package/dist/helpers.d.ts.map +1 -0
- package/dist/{helpers.mjs → helpers.js} +1 -1
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/policy.d.ts +89 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/{index.mjs → policy.js} +4 -179
- package/dist/policy.js.map +1 -0
- package/dist/{types.d.mts → types.d.ts} +1 -1
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +14 -15
- package/dist/errors.d.mts.map +0 -1
- package/dist/errors.mjs.map +0 -1
- package/dist/helpers.d.mts.map +0 -1
- package/dist/helpers.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## @zap-studio/permit@0.3.4
|
|
2
|
+
|
|
3
|
+
### Tree-shakeable root re-exports
|
|
4
|
+
|
|
5
|
+
The package root now re-exports the full public API, so everything can be imported from `@zap-studio/permit` directly, including `PolicyError`, `assertNever`, and all public types. All exports are side-effect free and tree-shakeable; granular subpath imports keep working.
|
|
6
|
+
|
|
7
|
+
- The implementation moved out of the entrypoint into two new subpaths: `./conditions` (`allow`, `deny`, `when`, `and`, `or`, `not`, `has`, `hasRole`, `collectInheritedRoles`) and `./policy` (`createPolicy`, `mergePolicies`, `mergePoliciesAny`).
|
|
8
|
+
|
|
1
9
|
## @zap-studio/permit@0.3.3
|
|
2
10
|
|
|
3
11
|
### Migrate to ultracite lint/format
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -14,8 +14,6 @@ A type-safe, declarative authorization library for TypeScript with [Standard Sch
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
pnpm add @zap-studio/permit
|
|
18
|
-
# or
|
|
19
17
|
npm install @zap-studio/permit
|
|
20
18
|
```
|
|
21
19
|
|
|
@@ -24,7 +22,7 @@ npm install @zap-studio/permit
|
|
|
24
22
|
```ts
|
|
25
23
|
import { z } from "zod";
|
|
26
24
|
import { createPolicy, allow, deny, when } from "@zap-studio/permit";
|
|
27
|
-
import type { Resources, Actions } from "@zap-studio/permit
|
|
25
|
+
import type { Resources, Actions } from "@zap-studio/permit";
|
|
28
26
|
|
|
29
27
|
// 1. Define your resource schemas
|
|
30
28
|
const resources = {
|
|
@@ -268,3 +266,15 @@ const resources = {
|
|
|
268
266
|
post: type({ id: "string" }),
|
|
269
267
|
} satisfies Resources;
|
|
270
268
|
```
|
|
269
|
+
|
|
270
|
+
## Runtime Support
|
|
271
|
+
|
|
272
|
+
| Runtime | Minimum version |
|
|
273
|
+
| ------------------ | ------------------------------------------------ |
|
|
274
|
+
| Node.js | 18.0.0 |
|
|
275
|
+
| Bun | 1.0.0 |
|
|
276
|
+
| Deno | 1.42 |
|
|
277
|
+
| Cloudflare Workers | Any current release |
|
|
278
|
+
| Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
|
|
279
|
+
|
|
280
|
+
The package ships standard ESM only and uses no runtime-specific APIs. Deno 1.42 is the first release that can install packages from JSR (`deno add jsr:@zap-studio/permit`).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/
|
|
1
|
+
import { ConditionFn, Context, PolicyFn, Role, RoleHierarchy } from "./types.js";
|
|
2
|
+
//#region src/conditions.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Returns a policy function that always allows the action.
|
|
5
5
|
*
|
|
@@ -172,90 +172,6 @@ interface HasRoleFn {
|
|
|
172
172
|
* ```
|
|
173
173
|
*/
|
|
174
174
|
declare const hasRole: HasRoleFn;
|
|
175
|
-
/**
|
|
176
|
-
* Creates a type-safe policy from resource schemas, actions, and rules.
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* ```ts
|
|
180
|
-
* import { z } from "zod";
|
|
181
|
-
* import { createPolicy, allow, deny, when } from "@zap-studio/permit";
|
|
182
|
-
* import type { Resources, Actions } from "@zap-studio/permit/types";
|
|
183
|
-
*
|
|
184
|
-
* // Define resource schemas
|
|
185
|
-
* const resources = {
|
|
186
|
-
* post: z.object({
|
|
187
|
-
* id: z.string(),
|
|
188
|
-
* authorId: z.string(),
|
|
189
|
-
* visibility: z.enum(["public", "private"]),
|
|
190
|
-
* }),
|
|
191
|
-
* comment: z.object({
|
|
192
|
-
* id: z.string(),
|
|
193
|
-
* postId: z.string(),
|
|
194
|
-
* authorId: z.string(),
|
|
195
|
-
* }),
|
|
196
|
-
* } satisfies Resources;
|
|
197
|
-
*
|
|
198
|
-
* // Define actions per resource
|
|
199
|
-
* const actions = {
|
|
200
|
-
* post: ["read", "write", "delete"],
|
|
201
|
-
* comment: ["read", "write"],
|
|
202
|
-
* } as const satisfies Actions<typeof resources>;
|
|
203
|
-
*
|
|
204
|
-
* // Define context type
|
|
205
|
-
* type AppContext = { user: { id: string; role: string } };
|
|
206
|
-
*
|
|
207
|
-
* // Create the policy
|
|
208
|
-
* const policy = createPolicy<AppContext>({
|
|
209
|
-
* resources,
|
|
210
|
-
* actions,
|
|
211
|
-
* rules: {
|
|
212
|
-
* post: {
|
|
213
|
-
* read: when((ctx, action, resource) => resource.visibility === "public"),
|
|
214
|
-
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
215
|
-
* delete: deny(),
|
|
216
|
-
* },
|
|
217
|
-
* comment: {
|
|
218
|
-
* read: allow(),
|
|
219
|
-
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
220
|
-
* },
|
|
221
|
-
* },
|
|
222
|
-
* });
|
|
223
|
-
*
|
|
224
|
-
* // Check permissions
|
|
225
|
-
* const post = { id: "1", authorId: "user-1", visibility: "public" as const };
|
|
226
|
-
* await policy.can(ctx, "post:read", post); // true
|
|
227
|
-
* await policy.can(ctx, "post:write", post); // depends on ctx.user.id
|
|
228
|
-
* ```
|
|
229
|
-
*/
|
|
230
|
-
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>;
|
|
231
|
-
/**
|
|
232
|
-
* Merges multiple policies into one using "deny-overrides" strategy.
|
|
233
|
-
* If any policy denies, the merged policy denies. All must allow for the result to allow.
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* ```ts
|
|
237
|
-
* const basePolicy = createPolicy({ ... });
|
|
238
|
-
* const adminPolicy = createPolicy({ ... });
|
|
239
|
-
*
|
|
240
|
-
* const merged = mergePolicies(basePolicy, adminPolicy);
|
|
241
|
-
* // Both policies must allow for the action to be permitted
|
|
242
|
-
* ```
|
|
243
|
-
*/
|
|
244
|
-
declare const mergePolicies: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
245
|
-
/**
|
|
246
|
-
* Merges multiple policies into one using "allow-overrides" strategy.
|
|
247
|
-
* If any policy allows, the merged policy allows. All must deny for the result to deny.
|
|
248
|
-
*
|
|
249
|
-
* @example
|
|
250
|
-
* ```ts
|
|
251
|
-
* const guestPolicy = createPolicy({ ... });
|
|
252
|
-
* const memberPolicy = createPolicy({ ... });
|
|
253
|
-
*
|
|
254
|
-
* const merged = mergePoliciesAny(guestPolicy, memberPolicy);
|
|
255
|
-
* // If either policy allows, the action is permitted
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
declare const mergePoliciesAny: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
259
175
|
//#endregion
|
|
260
|
-
export { allow, and, collectInheritedRoles,
|
|
261
|
-
//# sourceMappingURL=
|
|
176
|
+
export { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when };
|
|
177
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","names":[],"sources":["../src/conditions.ts"],"mappings":";;;;;;;;;;;;;;;;;;cA+Ba,QAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBpB,MAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBvB,KAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;cAkBvB,MAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,YAAY,UAAU,SAAS;;;;;;;;;;;;;cAgBvB,MACV,iBAAiB,SAAS,gBAAgB,UACzC,KAAK,GACL,OAAO,SAAS,OACf,YAAY;;;;;;;;;;;;;;;;;;cAqBJ,wBAAyB,cAAc,OAAO,MACzD,OAAO,SACP,WAAW,cAAc,WACxB,IAAI;;;;UAwBG;GAEN;IAAmB,MAAM,OAAO;KAChC,iCACA,qBAEA,MAAM,OACL,YAAY,UAAU,SAAS;GAEhC;IAAmB,MAAM,QAAQ;KACjC,iCACA,qBACA,cAAc,OAAO,MAErB,MAAM,OACN,WAAW,cAAc,SACxB,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BvB,SAAS"}
|
|
@@ -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 parents = hierarchy[role] ?? [];
|
|
140
|
+
for (const parent of parents) add(parent);
|
|
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 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 parents = hierarchy[role] ?? [];\n for (const parent of parents) {\n add(parent);\n }\n };\n\n for (const role of roles) {\n add(role);\n }\n return inherited;\n};\n\n/**\n * Call signatures for {@link hasRole}, preserving the with/without hierarchy overloads.\n */\ninterface HasRoleFn {\n <\n TContext extends { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n >(\n role: Role\n ): ConditionFn<TContext, TAction, TResource>;\n <\n TContext extends { role: TRole | TRole[] },\n TAction extends string = string,\n TResource = unknown,\n TRole extends Role = Role,\n >(\n role: TRole,\n hierarchy: RoleHierarchy<TRole>\n ): ConditionFn<TContext, TAction, TResource>;\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":";;;;;;;;;;;;;;;;;AA+BA,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,UAAU,UAAU,SAAS,CAAC;EACpC,KAAK,MAAM,UAAU,SACnB,IAAI,MAAM;CAEd;CAEA,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI;CAEV,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;cAUa,oBAAoB;;;;;;EAM/B,YAAY"}
|
|
@@ -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 */\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":";;;;;;;;;;AAUA,IAAa,cAAb,cAAiC,MAAM;;;;;;CAMrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4Ba,cAAe"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["/**\n * Helper utilities for permit consumers.\n *\n * @module @zap-studio/permit/helpers\n */\n\n/**\n * Ensures that a value of type `never` is actually never encountered at runtime.\n * This is useful for exhaustive checks on discriminated unions.\n *\n * @example\n * ```ts\n * type Action = 'read' | 'write'\n *\n * function performAction(action: Action) {\n * switch (action) {\n * case 'read':\n * console.log('Reading...')\n * break\n * case 'write':\n * console.log('Writing...')\n * break\n * default:\n * assertNever(action) // TypeScript will error if a new Action is added but not handled\n * }\n * }\n * ```\n */\nexport const assertNever = (value: never): never => {\n throw new Error(`Unexpected value: ${String(value)}`);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,eAAe,UAAwB;CAClD,MAAM,IAAI,MAAM,qBAAqB,OAAO,KAAK,GAAG;AACtD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
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 { assertNever } from "./helpers.js";
|
|
5
|
+
import { createPolicy, mergePolicies, mergePoliciesAny } from "./policy.js";
|
|
6
|
+
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, assertNever, collectInheritedRoles, createPolicy, deny, has, hasRole, mergePolicies, mergePoliciesAny, not, or, when };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when } from "./conditions.js";
|
|
2
|
+
import { PolicyError } from "./errors.js";
|
|
3
|
+
import { assertNever } from "./helpers.js";
|
|
4
|
+
import { createPolicy, mergePolicies, mergePoliciesAny } from "./policy.js";
|
|
5
|
+
export { PolicyError, allow, and, assertNever, collectInheritedRoles, createPolicy, deny, has, hasRole, mergePolicies, mergePoliciesAny, not, or, when };
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
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 using "deny-overrides" strategy.
|
|
61
|
+
* If any policy denies, the merged policy denies. All must allow for the result to allow.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* const basePolicy = createPolicy({ ... });
|
|
66
|
+
* const adminPolicy = createPolicy({ ... });
|
|
67
|
+
*
|
|
68
|
+
* const merged = mergePolicies(basePolicy, adminPolicy);
|
|
69
|
+
* // Both policies must allow for the action to be permitted
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
declare const mergePolicies: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
73
|
+
/**
|
|
74
|
+
* Merges multiple policies into one using "allow-overrides" strategy.
|
|
75
|
+
* If any policy allows, the merged policy allows. All must deny for the result to deny.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const guestPolicy = createPolicy({ ... });
|
|
80
|
+
* const memberPolicy = createPolicy({ ... });
|
|
81
|
+
*
|
|
82
|
+
* const merged = mergePoliciesAny(guestPolicy, memberPolicy);
|
|
83
|
+
* // If either policy allows, the action is permitted
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
declare const mergePoliciesAny: <TContext extends Context, TResources extends Resources = Resources, TActions extends Actions<TResources> = Actions<TResources>>(...policies: Policy<TContext, TResources, TActions>[]) => Policy<TContext, TResources, TActions>;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { createPolicy, mergePolicies, mergePoliciesAny };
|
|
89
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","names":[],"sources":["../src/policy.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyGa,eACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,aAE/C,QAAQ,aAAa,UAAU,YAAY,cAC1C,OAAO,UAAU,YAAY;;;;;;;;;;;;;;cA2InB,gBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY;;;;;;;;;;;;;;cAgBnB,mBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY"}
|
|
@@ -1,181 +1,6 @@
|
|
|
1
|
-
import { PolicyError } from "./errors.
|
|
1
|
+
import { PolicyError } from "./errors.js";
|
|
2
2
|
import { createStandardValidator } from "@zap-studio/validation";
|
|
3
|
-
//#region src/
|
|
4
|
-
/**
|
|
5
|
-
* Returns a policy function that always allows the action.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* const policy = createPolicy({
|
|
10
|
-
* resources,
|
|
11
|
-
* actions,
|
|
12
|
-
* rules: {
|
|
13
|
-
* post: {
|
|
14
|
-
* read: allow(), // Always allow reading posts
|
|
15
|
-
* },
|
|
16
|
-
* },
|
|
17
|
-
* });
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
const allow = () => () => "allow";
|
|
21
|
-
/**
|
|
22
|
-
* Returns a policy function that always denies the action.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* const policy = createPolicy({
|
|
27
|
-
* resources,
|
|
28
|
-
* actions,
|
|
29
|
-
* rules: {
|
|
30
|
-
* post: {
|
|
31
|
-
* delete: deny(), // Never allow deleting posts
|
|
32
|
-
* },
|
|
33
|
-
* },
|
|
34
|
-
* });
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
const deny = () => () => "deny";
|
|
38
|
-
/**
|
|
39
|
-
* Returns a policy function that allows or denies based on a condition.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* const policy = createPolicy({
|
|
44
|
-
* resources,
|
|
45
|
-
* actions,
|
|
46
|
-
* rules: {
|
|
47
|
-
* post: {
|
|
48
|
-
* write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
49
|
-
* },
|
|
50
|
-
* },
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
const when = (condition) => (context, action, resource) => condition(context, action, resource) ? "allow" : "deny";
|
|
55
|
-
/**
|
|
56
|
-
* Returns a condition function that returns `true` if all conditions are met.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* const isOwnerAndPublished = and(
|
|
61
|
-
* (ctx, action, resource) => ctx.user.id === resource.authorId,
|
|
62
|
-
* (ctx, action, resource) => resource.status === "published"
|
|
63
|
-
* );
|
|
64
|
-
*
|
|
65
|
-
* rules: {
|
|
66
|
-
* post: {
|
|
67
|
-
* delete: when(isOwnerAndPublished),
|
|
68
|
-
* },
|
|
69
|
-
* }
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
const and = (...conditions) => (context, action, resource) => conditions.every((condition) => condition(context, action, resource));
|
|
73
|
-
/**
|
|
74
|
-
* Returns a condition function that returns `true` if any condition is met.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* const isOwnerOrAdmin = or(
|
|
79
|
-
* (ctx, action, resource) => ctx.user.id === resource.authorId,
|
|
80
|
-
* (ctx, action, resource) => ctx.user.role === "admin"
|
|
81
|
-
* );
|
|
82
|
-
*
|
|
83
|
-
* rules: {
|
|
84
|
-
* post: {
|
|
85
|
-
* write: when(isOwnerOrAdmin),
|
|
86
|
-
* },
|
|
87
|
-
* }
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
const or = (...conditions) => (context, action, resource) => conditions.some((condition) => condition(context, action, resource));
|
|
91
|
-
/**
|
|
92
|
-
* Returns a condition function that negates another condition.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```ts
|
|
96
|
-
* const isNotOwner = not((ctx, action, resource) => ctx.user.id === resource.authorId);
|
|
97
|
-
*
|
|
98
|
-
* rules: {
|
|
99
|
-
* post: {
|
|
100
|
-
* like: when(isNotOwner), // Can only like posts you don't own
|
|
101
|
-
* },
|
|
102
|
-
* }
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
const not = (condition) => (context, action, resource) => !condition(context, action, resource);
|
|
106
|
-
/**
|
|
107
|
-
* Returns a condition function that checks if a context property equals a value.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* rules: {
|
|
112
|
-
* post: {
|
|
113
|
-
* write: when(has("role", "admin")), // Only admins can write
|
|
114
|
-
* },
|
|
115
|
-
* }
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
const has = (key, value) => (context) => context[key] === value;
|
|
119
|
-
/**
|
|
120
|
-
* Collects all roles including inherited ones from a role hierarchy.
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```ts
|
|
124
|
-
* type Role = "guest" | "user" | "admin";
|
|
125
|
-
*
|
|
126
|
-
* const hierarchy: RoleHierarchy<Role> = {
|
|
127
|
-
* guest: [],
|
|
128
|
-
* user: ["guest"],
|
|
129
|
-
* admin: ["user"],
|
|
130
|
-
* };
|
|
131
|
-
*
|
|
132
|
-
* collectInheritedRoles(["admin"], hierarchy);
|
|
133
|
-
* // Returns: Set { "admin", "user", "guest" }
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
const collectInheritedRoles = (roles, hierarchy) => {
|
|
137
|
-
const inherited = /* @__PURE__ */ new Set();
|
|
138
|
-
const add = (role) => {
|
|
139
|
-
if (inherited.has(role)) return;
|
|
140
|
-
inherited.add(role);
|
|
141
|
-
const parents = hierarchy[role] ?? [];
|
|
142
|
-
for (const parent of parents) add(parent);
|
|
143
|
-
};
|
|
144
|
-
for (const role of roles) add(role);
|
|
145
|
-
return inherited;
|
|
146
|
-
};
|
|
147
|
-
/**
|
|
148
|
-
* Returns a condition function that checks if the user has a specific role.
|
|
149
|
-
* Supports role hierarchy for inherited permissions.
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* ```ts
|
|
153
|
-
* // Without hierarchy
|
|
154
|
-
* rules: {
|
|
155
|
-
* post: {
|
|
156
|
-
* delete: when(hasRole("admin")),
|
|
157
|
-
* },
|
|
158
|
-
* }
|
|
159
|
-
*
|
|
160
|
-
* // With hierarchy
|
|
161
|
-
* const hierarchy = {
|
|
162
|
-
* guest: [],
|
|
163
|
-
* user: ["guest"],
|
|
164
|
-
* admin: ["user"],
|
|
165
|
-
* };
|
|
166
|
-
*
|
|
167
|
-
* rules: {
|
|
168
|
-
* post: {
|
|
169
|
-
* read: when(hasRole("guest", hierarchy)), // Admins and users can also read
|
|
170
|
-
* },
|
|
171
|
-
* }
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
const hasRole = (role, hierarchy) => (context) => {
|
|
175
|
-
const userRoles = Array.isArray(context.role) ? context.role : [context.role];
|
|
176
|
-
if (hierarchy === void 0) return userRoles.includes(role);
|
|
177
|
-
return collectInheritedRoles(userRoles, hierarchy).has(role);
|
|
178
|
-
};
|
|
3
|
+
//#region src/policy.ts
|
|
179
4
|
/**
|
|
180
5
|
* Splits a typed `resource:action` permission string into its parts.
|
|
181
6
|
* Returns `null` when the string is malformed (missing/empty part or extra segments).
|
|
@@ -323,6 +148,6 @@ const mergePolicies = (...policies) => mergePoliciesWithStrategy(policies, "deny
|
|
|
323
148
|
*/
|
|
324
149
|
const mergePoliciesAny = (...policies) => mergePoliciesWithStrategy(policies, "allow-overrides");
|
|
325
150
|
//#endregion
|
|
326
|
-
export {
|
|
151
|
+
export { createPolicy, mergePolicies, mergePoliciesAny };
|
|
327
152
|
|
|
328
|
-
//# sourceMappingURL=
|
|
153
|
+
//# 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`, `mergePolicies`, and\n * `mergePoliciesAny`.\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 segments).\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<TActions, K> & string}`\n): { action: InferAction<TActions, K>; resourceType: K } | null => {\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 ) {\n return null;\n }\n\n return {\n action: actionValue,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Parsed permission strings are constrained by the typed permission template.\n resourceType: resourceTypeValue as K,\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<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<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<TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n const parsedPermission = parsePermission<TResources, TActions, K>(\n permission\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: \"allow-overrides\" | \"deny-overrides\"\n): Policy<TContext, TResources, TActions> => ({\n async can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n permission: `${K & string}:${InferAction<TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n if (policies.length === 0) {\n return false;\n }\n for (const policy of policies) {\n // oxlint-disable-next-line no-await-in-loop -- Policies must evaluate sequentially to preserve short-circuit semantics.\n const allowed = await policy.can(context, permission, resource);\n\n if (strategy === \"allow-overrides\" && allowed) {\n return true;\n }\n if (strategy === \"deny-overrides\" && !allowed) {\n return false;\n }\n }\n return strategy === \"deny-overrides\";\n },\n});\n\n/**\n * Merges multiple policies into one using \"deny-overrides\" strategy.\n * If any policy denies, the merged policy denies. All must allow for the result to allow.\n *\n * @example\n * ```ts\n * const basePolicy = createPolicy({ ... });\n * const adminPolicy = createPolicy({ ... });\n *\n * const merged = mergePolicies(basePolicy, adminPolicy);\n * // Both policies must allow for the action to be permitted\n * ```\n */\nexport const mergePolicies = <\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, \"deny-overrides\");\n\n/**\n * Merges multiple policies into one using \"allow-overrides\" strategy.\n * If any policy allows, the merged policy allows. All must deny for the result to deny.\n *\n * @example\n * ```ts\n * const guestPolicy = createPolicy({ ... });\n * const memberPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesAny(guestPolicy, memberPolicy);\n * // If either policy allows, the action is permitted\n * ```\n */\nexport const mergePoliciesAny = <\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, \"allow-overrides\");\n"],"mappings":";;;;;;;AAyBA,MAAM,mBAKJ,eACiE;CACjE,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,GAEd,OAAO;CAGT,OAAO;EACL,QAAQ;EAER,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,UACF;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;CAET,KAAK,MAAM,UAAU,UAAU;EAE7B,MAAM,UAAU,MAAM,OAAO,IAAI,SAAS,YAAY,QAAQ;EAE9D,IAAI,aAAa,qBAAqB,SACpC,OAAO;EAET,IAAI,aAAa,oBAAoB,CAAC,SACpC,OAAO;CAEX;CACA,OAAO,aAAa;AACtB,EACF;;;;;;;;;;;;;;AAeA,MAAa,iBAKX,GAAG,aAEH,0BAA0B,UAAU,gBAAgB;;;;;;;;;;;;;;AAetD,MAAa,oBAKX,GAAG,aAEH,0BAA0B,UAAU,iBAAiB"}
|
|
@@ -139,4 +139,4 @@ interface Policy<TContext extends Context, TResources extends Resources = Resour
|
|
|
139
139
|
}
|
|
140
140
|
//#endregion
|
|
141
141
|
export { ActionPolicyMap, Actions, ConditionFn, Context, Decision, InferAction, InferPermission, InferResource, PermitConfig, Policy, PolicyFn, Resources, Role, RoleHierarchy, Rules };
|
|
142
|
-
//# sourceMappingURL=types.d.
|
|
142
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;KAaY;;;;;KAMA,QAAQ,sBAAsB;;;;KAK9B,KAAK,iCAAiC;;;;;;;;;;;;;;;;KAiBtC,cAAc,cAAc,OAAO,QAAQ,OAAO,OAAO;;;;;;;;;;;;;;;;KAiBzD,UAAU,wCAAwC,OAC5D,cACA;;;;;;;;;;;;;;;KAiBU,QAAQ,mBAAmB,gBACpC,WAAW;;;;KAMF,cACV,mBAAmB,WACnB,2BAA2B,cACzB,iBAAiB,YAAY,WAAW;;;;KAKhC,YACV,iBAAiB,mCACjB,gBAAgB,YACd,SAAS;;;;;;;;;;KAWD,gBACV,mBAAmB,WACnB,iBAAiB,QAAQ,kBAExB,WAAW,mBACJ,cAAc,cAAc,YAAY,UAAU,uBACpD,mBAAmB;;;;KAKf,SACV,iBAAiB,SACjB,iCACA,wBACG,SAAS,UAAU,QAAQ,SAAS,UAAU,cAAc;;;;KAKrD,YACV,iBAAiB,SACjB,iCACA,wBACG,SAAS,UAAU,QAAQ,SAAS,UAAU;;;;KAKvC,gBACV,iBAAiB,SACjB,iCACA,0BAEC,KAAK,WAAW,SAAS,UAAU,GAAG;;;;;KAO7B,MACV,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,kBAE9C,WAAW,mBAAmB,WAAW,gBACxC,UACA,YAAY,UAAU,IACtB,cAAc,YAAY;;;;;;;;;;;;;;;UAkBb,aACf,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ;EAE/C,SAAS;EACT,WAAW;EACX,OAAO,MAAM,UAAU,YAAY;;;;;;;;;;;;;;;;;UAkBpB,OACf,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ;;;;EAK/C,MAAM,gBAAgB,mBAAmB,UACvC,SAAS,UACT,eAAe,cAAc,YAAY,UAAU,eACnD,UAAU,cAAc,YAAY,OACjC"}
|
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zap-studio/permit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A type-safe, declarative authorization library for TypeScript with Standard Schema support",
|
|
6
6
|
"keywords": [
|
|
@@ -31,30 +31,29 @@
|
|
|
31
31
|
],
|
|
32
32
|
"type": "module",
|
|
33
33
|
"sideEffects": false,
|
|
34
|
-
"types": "./dist/index.d.
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
35
|
"exports": {
|
|
36
|
-
".": "./dist/index.
|
|
37
|
-
"./
|
|
38
|
-
"./
|
|
39
|
-
"./
|
|
36
|
+
".": "./dist/index.js",
|
|
37
|
+
"./conditions": "./dist/conditions.js",
|
|
38
|
+
"./errors": "./dist/errors.js",
|
|
39
|
+
"./helpers": "./dist/helpers.js",
|
|
40
|
+
"./policy": "./dist/policy.js",
|
|
41
|
+
"./types": "./dist/types.js",
|
|
40
42
|
"./package.json": "./package.json"
|
|
41
43
|
},
|
|
42
44
|
"publishConfig": {
|
|
43
45
|
"access": "public"
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
|
-
"@zap-studio/validation": "
|
|
48
|
+
"@zap-studio/validation": "workspace:*"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
51
|
+
"@zap-studio/typescript": "workspace:*",
|
|
52
|
+
"tsdown": "catalog:",
|
|
53
|
+
"typescript": "catalog:",
|
|
54
|
+
"vitest": "catalog:"
|
|
53
55
|
},
|
|
54
56
|
"engines": {
|
|
55
57
|
"node": ">=18.0.0"
|
|
56
|
-
},
|
|
57
|
-
"scripts": {
|
|
58
|
-
"build": "tsdown --config ./tsdown.config.ts"
|
|
59
58
|
}
|
|
60
|
-
}
|
|
59
|
+
}
|
package/dist/errors.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;cAUa,oBAAoB;;;;;;EAM/B,YAAY"}
|
package/dist/errors.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","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 */\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":";;;;;;;;;;AAUA,IAAa,cAAb,cAAiC,MAAM;;;;;;CAMrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
|
package/dist/helpers.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.mts","names":[],"sources":["../src/helpers.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4Ba,cAAe"}
|
package/dist/helpers.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.mjs","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["/**\n * Helper utilities for permit consumers.\n *\n * @module @zap-studio/permit/helpers\n */\n\n/**\n * Ensures that a value of type `never` is actually never encountered at runtime.\n * This is useful for exhaustive checks on discriminated unions.\n *\n * @example\n * ```ts\n * type Action = 'read' | 'write'\n *\n * function performAction(action: Action) {\n * switch (action) {\n * case 'read':\n * console.log('Reading...')\n * break\n * case 'write':\n * console.log('Writing...')\n * break\n * default:\n * assertNever(action) // TypeScript will error if a new Action is added but not handled\n * }\n * }\n * ```\n */\nexport const assertNever = (value: never): never => {\n throw new Error(`Unexpected value: ${String(value)}`);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,eAAe,UAAwB;CAClD,MAAM,IAAI,MAAM,qBAAqB,OAAO,KAAK,GAAG;AACtD"}
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;cAwCa,QAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,0BACG,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;cAoBtB,OAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,SAAS,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBpB,MAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;cAqBvB,KAET,iBAAiB,SACjB,iCACA,wBAEG,YAAY,YAAY,UAAU,SAAS,iBAC7C,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;cAkBvB,MAET,iBAAiB,SACjB,iCACA,qBAEA,WAAW,YAAY,UAAU,SAAS,eACzC,YAAY,UAAU,SAAS;;;;;;;;;;;;;cAgBvB,MACV,iBAAiB,SAAS,gBAAgB,UACzC,KAAK,GACL,OAAO,SAAS,OACf,YAAY;;;;;;;;;;;;;;;;;;cAqBJ,wBAAyB,cAAc,OAAO,MACzD,OAAO,SACP,WAAW,cAAc,WACxB,IAAI;;;;UAwBG;GAEN;IAAmB,MAAM,OAAO;KAChC,iCACA,qBAEA,MAAM,OACL,YAAY,UAAU,SAAS;GAEhC;IAAmB,MAAM,QAAQ;KACjC,iCACA,qBACA,cAAc,OAAO,MAErB,MAAM,OACN,WAAW,cAAc,SACxB,YAAY,UAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BvB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsGT,eACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,aAE/C,QAAQ,aAAa,UAAU,YAAY,cAC1C,OAAO,UAAU,YAAY;;;;;;;;;;;;;;cA2InB,gBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY;;;;;;;;;;;;;;cAgBnB,mBACX,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,gBAE5C,UAAU,OAAO,UAAU,YAAY,gBACzC,OAAO,UAAU,YAAY"}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Policy composition and evaluation utilities.\n *\n * @module @zap-studio/permit\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 ConditionFn,\n Context,\n InferAction,\n InferResource,\n PermitConfig,\n Policy,\n PolicyFn,\n Resources,\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 parents = hierarchy[role] ?? [];\n for (const parent of parents) {\n add(parent);\n }\n };\n\n for (const role of roles) {\n add(role);\n }\n return inherited;\n};\n\n/**\n * Call signatures for {@link hasRole}, preserving the with/without hierarchy overloads.\n */\ninterface HasRoleFn {\n <\n TContext extends { role: Role | Role[] },\n TAction extends string = string,\n TResource = unknown,\n >(\n role: Role\n ): ConditionFn<TContext, TAction, TResource>;\n <\n TContext extends { role: TRole | TRole[] },\n TAction extends string = string,\n TResource = unknown,\n TRole extends Role = Role,\n >(\n role: TRole,\n hierarchy: RoleHierarchy<TRole>\n ): ConditionFn<TContext, TAction, TResource>;\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\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 segments).\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<TActions, K> & string}`\n): { action: InferAction<TActions, K>; resourceType: K } | null => {\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 ) {\n return null;\n }\n\n return {\n action: actionValue,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Parsed permission strings are constrained by the typed permission template.\n resourceType: resourceTypeValue as K,\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<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<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<TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n const parsedPermission = parsePermission<TResources, TActions, K>(\n permission\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: \"allow-overrides\" | \"deny-overrides\"\n): Policy<TContext, TResources, TActions> => ({\n async can<K extends keyof TResources & keyof TActions>(\n context: TContext,\n permission: `${K & string}:${InferAction<TActions, K> & string}`,\n resource: InferResource<TResources, K>\n ): Promise<boolean> {\n if (policies.length === 0) {\n return false;\n }\n for (const policy of policies) {\n // oxlint-disable-next-line no-await-in-loop -- Policies must evaluate sequentially to preserve short-circuit semantics.\n const allowed = await policy.can(context, permission, resource);\n\n if (strategy === \"allow-overrides\" && allowed) {\n return true;\n }\n if (strategy === \"deny-overrides\" && !allowed) {\n return false;\n }\n }\n return strategy === \"deny-overrides\";\n },\n});\n\n/**\n * Merges multiple policies into one using \"deny-overrides\" strategy.\n * If any policy denies, the merged policy denies. All must allow for the result to allow.\n *\n * @example\n * ```ts\n * const basePolicy = createPolicy({ ... });\n * const adminPolicy = createPolicy({ ... });\n *\n * const merged = mergePolicies(basePolicy, adminPolicy);\n * // Both policies must allow for the action to be permitted\n * ```\n */\nexport const mergePolicies = <\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, \"deny-overrides\");\n\n/**\n * Merges multiple policies into one using \"allow-overrides\" strategy.\n * If any policy allows, the merged policy allows. All must deny for the result to deny.\n *\n * @example\n * ```ts\n * const guestPolicy = createPolicy({ ... });\n * const memberPolicy = createPolicy({ ... });\n *\n * const merged = mergePoliciesAny(guestPolicy, memberPolicy);\n * // If either policy allows, the action is permitted\n * ```\n */\nexport const mergePoliciesAny = <\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, \"allow-overrides\");\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwCA,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,UAAU,UAAU,SAAS,CAAC;EACpC,KAAK,MAAM,UAAU,SACnB,IAAI,MAAM;CAEd;CAEA,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI;CAEV,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,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;;;;;AAMF,MAAM,mBAKJ,eACiE;CACjE,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,GAEd,OAAO;CAGT,OAAO;EACL,QAAQ;EAER,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,UACF;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;CAET,KAAK,MAAM,UAAU,UAAU;EAE7B,MAAM,UAAU,MAAM,OAAO,IAAI,SAAS,YAAY,QAAQ;EAE9D,IAAI,aAAa,qBAAqB,SACpC,OAAO;EAET,IAAI,aAAa,oBAAoB,CAAC,SACpC,OAAO;CAEX;CACA,OAAO,aAAa;AACtB,EACF;;;;;;;;;;;;;;AAeA,MAAa,iBAKX,GAAG,aAEH,0BAA0B,UAAU,gBAAgB;;;;;;;;;;;;;;AAetD,MAAa,oBAKX,GAAG,aAEH,0BAA0B,UAAU,iBAAiB"}
|
package/dist/types.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;KAaY;;;;;KAMA,QAAQ,sBAAsB;;;;KAK9B,KAAK,iCAAiC;;;;;;;;;;;;;;;;KAiBtC,cAAc,cAAc,OAAO,QAAQ,OAAO,OAAO;;;;;;;;;;;;;;;;KAiBzD,UAAU,wCAAwC,OAC5D,cACA;;;;;;;;;;;;;;;KAiBU,QAAQ,mBAAmB,gBACpC,WAAW;;;;KAMF,cACV,mBAAmB,WACnB,2BAA2B,cACzB,iBAAiB,YAAY,WAAW;;;;KAKhC,YACV,iBAAiB,mCACjB,gBAAgB,YACd,SAAS;;;;;;;;;;KAWD,gBACV,mBAAmB,WACnB,iBAAiB,QAAQ,kBAExB,WAAW,mBACJ,cAAc,cAAc,YAAY,UAAU,uBACpD,mBAAmB;;;;KAKf,SACV,iBAAiB,SACjB,iCACA,wBACG,SAAS,UAAU,QAAQ,SAAS,UAAU,cAAc;;;;KAKrD,YACV,iBAAiB,SACjB,iCACA,wBACG,SAAS,UAAU,QAAQ,SAAS,UAAU;;;;KAKvC,gBACV,iBAAiB,SACjB,iCACA,0BAEC,KAAK,WAAW,SAAS,UAAU,GAAG;;;;;KAO7B,MACV,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ,kBAE9C,WAAW,mBAAmB,WAAW,gBACxC,UACA,YAAY,UAAU,IACtB,cAAc,YAAY;;;;;;;;;;;;;;;UAkBb,aACf,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ;EAE/C,SAAS;EACT,WAAW;EACX,OAAO,MAAM,UAAU,YAAY;;;;;;;;;;;;;;;;;UAkBpB,OACf,iBAAiB,SACjB,mBAAmB,YAAY,WAC/B,iBAAiB,QAAQ,cAAc,QAAQ;;;;EAK/C,MAAM,gBAAgB,mBAAmB,UACvC,SAAS,UACT,eAAe,cAAc,YAAY,UAAU,eACnD,UAAU,cAAc,YAAY,OACjC"}
|
package/dist/types.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|