@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
package/CHANGELOG.md
CHANGED
|
@@ -1,98 +1,112 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## [1.0.0]
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Changed
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- Clarified tree-shakeable design in the package description and README (no code change).
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Removed
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
`assertNever` is no longer exported — it had no internal consumers and existed only as a generic exhaustiveness-check convenience. Use your own `never`-typed helper if you relied on it.
|
|
16
16
|
|
|
17
17
|
### Fixed
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
|
|
19
|
+
Neither merge strategy short-circuits anymore. All policies now run concurrently via `Promise.allSettled`, and every policy is invoked regardless of outcome, for both `mergePoliciesSome` (allow) and `mergePoliciesEvery` (deny). A rejecting policy no longer sinks the whole check — it's treated as a deny (`false`) and logged with `console.warn`, consistent with how `createPolicy` handles internal validation/evaluation errors.
|
|
20
|
+
|
|
21
|
+
## [0.3.4]
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
- 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`).
|
|
28
|
+
|
|
29
|
+
## [0.3.3]
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
Internal formatting and lint cleanup only. No public API or behavior change.
|
|
34
|
+
|
|
35
|
+
## [0.3.2]
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Updated dependency `@zap-studio/validation` to `0.3.4`.
|
|
40
|
+
|
|
41
|
+
## [0.3.1]
|
|
21
42
|
|
|
22
43
|
### Changed
|
|
23
44
|
|
|
24
45
|
- 5fa58b1: Reduced policy evaluation complexity by extracting permission parsing and merge strategy helpers without changing the `policy.can(...)` API.
|
|
25
46
|
- abfda4b: Cleaned up public option typings by removing redundant `| undefined` unions from package types.
|
|
26
47
|
- 9f31f87: Switched the package build to ESNext-aligned output and updated package tooling and publish metadata.
|
|
48
|
+
- d9ba7d1: Reworked the packaged permit skill content.
|
|
49
|
+
- Updated dependency `@zap-studio/validation` to `0.3.3`.
|
|
27
50
|
|
|
28
|
-
###
|
|
51
|
+
### Removed
|
|
29
52
|
|
|
30
|
-
-
|
|
53
|
+
- d10d8c4: Removed the TanStack Intent-specific authoring helper from the package surface.
|
|
31
54
|
|
|
32
|
-
## 0.3.0
|
|
55
|
+
## [0.3.0]
|
|
33
56
|
|
|
34
|
-
###
|
|
57
|
+
### Changed
|
|
35
58
|
|
|
36
|
-
- fe60f55:
|
|
59
|
+
- fe60f55: **Breaking:** Changed `policy.can()` to use a single permission string plus the resource object.
|
|
37
60
|
|
|
38
61
|
`policy.can(ctx, "read", "post", post)` is replaced by `policy.can(ctx, "post:read", post)`.
|
|
39
62
|
|
|
40
63
|
This is a breaking API change in the `0.x` line. Docs and examples now use the new permission-string format consistently.
|
|
41
64
|
|
|
42
|
-
## 0.2.2
|
|
65
|
+
## [0.2.2]
|
|
43
66
|
|
|
44
|
-
###
|
|
67
|
+
### Changed
|
|
45
68
|
|
|
46
69
|
- e26293e: Updated dependencies.
|
|
47
70
|
- @zap-studio/validation@0.3.2
|
|
48
71
|
|
|
49
|
-
## 0.2.1
|
|
72
|
+
## [0.2.1]
|
|
50
73
|
|
|
51
|
-
###
|
|
74
|
+
### Changed
|
|
52
75
|
|
|
53
76
|
- 5ea3d3b: Updated dependencies.
|
|
54
77
|
- @zap-studio/validation@0.3.1
|
|
55
78
|
|
|
56
|
-
## 0.2.0
|
|
79
|
+
## [0.2.0]
|
|
57
80
|
|
|
58
81
|
### Changed
|
|
59
82
|
|
|
60
83
|
- f0f503e: Made policy evaluation asynchronous by default.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- f0f503e: `policy.can(...)` now returns `Promise<boolean>`.
|
|
65
|
-
- f0f503e: `createPolicy()` now uses async-safe Standard Schema validation for resource schemas (including async resource schemas); this is not a separate `Policy` schema API.
|
|
66
|
-
- f0f503e: The `Policy` interface changed so `can()` is async, and `mergePolicies`/`mergePoliciesAny` are async accordingly.
|
|
84
|
+
- f0f503e: **Breaking:** `policy.can(...)` now returns `Promise<boolean>`.
|
|
85
|
+
- f0f503e: **Breaking:** `createPolicy()` now uses async-safe Standard Schema validation for resource schemas (including async resource schemas); this is not a separate `Policy` schema API.
|
|
86
|
+
- f0f503e: **Breaking:** The `Policy` interface changed so `can()` is async, and `mergePolicies`/`mergePoliciesAny` are async accordingly.
|
|
67
87
|
- Action required: callers must `await policy.can(...)` and handle `mergePolicies`/`mergePoliciesAny` as async operations; also account for async-safe resource schema validation in `createPolicy()`.
|
|
68
|
-
|
|
69
|
-
### Dependencies
|
|
70
|
-
|
|
71
88
|
- f75b984: Updated dependency `@zap-studio/validation` to `0.3.0`.
|
|
72
89
|
|
|
73
|
-
## 0.1.3
|
|
90
|
+
## [0.1.3]
|
|
74
91
|
|
|
75
|
-
###
|
|
92
|
+
### Changed
|
|
76
93
|
|
|
77
94
|
- e4542bb: Updated dependency `@zap-studio/validation` to `0.2.1`.
|
|
78
95
|
|
|
79
|
-
## 0.1.2
|
|
96
|
+
## [0.1.2]
|
|
80
97
|
|
|
81
98
|
### Changed
|
|
82
99
|
|
|
83
100
|
- 2de8183: Adopted shared synchronous Standard Schema validator utilities from `@zap-studio/validation` for resource schema validation in `createPolicy`.
|
|
84
|
-
|
|
85
|
-
### Dependencies
|
|
86
|
-
|
|
87
101
|
- 2de8183: Updated dependency `@zap-studio/validation` to `0.2.0`.
|
|
88
102
|
|
|
89
|
-
## 0.1.1
|
|
103
|
+
## [0.1.1]
|
|
90
104
|
|
|
91
105
|
### Fixed
|
|
92
106
|
|
|
93
107
|
- 907d903: Added runtime resource validation and fail-closed behavior, including deny-by-default when merges are invoked with no policies.
|
|
94
108
|
|
|
95
|
-
## 0.1.0
|
|
109
|
+
## [0.1.0]
|
|
96
110
|
|
|
97
111
|
### Added
|
|
98
112
|
|
package/README.md
CHANGED
|
@@ -2,137 +2,93 @@
|
|
|
2
2
|
|
|
3
3
|
A type-safe, declarative authorization library for TypeScript with [Standard Schema](https://standardschema.dev/) support.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- Full type safety with TypeScript
|
|
8
|
-
- Standard Schema support (Zod, Valibot, ArkType, etc.)
|
|
9
|
-
- Declarative policy definitions
|
|
10
|
-
- Role hierarchy support
|
|
11
|
-
- Composable conditions (`and`, `or`, `not`)
|
|
12
|
-
- Policy merging strategies
|
|
5
|
+
Full documentation: [zapstudio.dev/permit](https://www.zapstudio.dev/permit)
|
|
13
6
|
|
|
14
7
|
## Installation
|
|
15
8
|
|
|
16
9
|
```bash
|
|
17
|
-
pnpm add @zap-studio/permit
|
|
18
|
-
# or
|
|
19
10
|
npm install @zap-studio/permit
|
|
20
11
|
```
|
|
21
12
|
|
|
13
|
+
You also need a schema library that implements [Standard Schema](https://standardschema.dev/), such as Zod, Valibot, or ArkType.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Full type safety** — actions, resources, and permissions are inferred from your schemas and `satisfies` declarations.
|
|
18
|
+
- **Standard Schema support** via `Resources` — works with Zod, Valibot, ArkType, or any compatible library.
|
|
19
|
+
- **Declarative policies** through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)`.
|
|
20
|
+
- **Role hierarchy support** via `hasRole(role, hierarchy?)`, with inheritance resolved by `collectInheritedRoles`.
|
|
21
|
+
- **Composable conditions** via `and`, `or`, and `not`.
|
|
22
|
+
- **Policy merging strategies** via `mergePoliciesAnd` and `mergePoliciesOr`.
|
|
23
|
+
- **Structured errors** with `PolicyError` for invalid configuration or evaluation failures.
|
|
24
|
+
- **Tree-shakeable** — policies and conditions are plain functions; unused exports are dropped by any modern bundler.
|
|
25
|
+
|
|
22
26
|
## Quick Start
|
|
23
27
|
|
|
24
28
|
```ts
|
|
25
29
|
import { z } from "zod";
|
|
26
30
|
import { createPolicy, allow, deny, when } from "@zap-studio/permit";
|
|
27
|
-
import type { Resources, Actions } from "@zap-studio/permit
|
|
31
|
+
import type { Resources, Actions } from "@zap-studio/permit";
|
|
28
32
|
|
|
29
|
-
// 1. Define your resource schemas
|
|
30
33
|
const resources = {
|
|
31
|
-
post: z.object({
|
|
32
|
-
id: z.string(),
|
|
33
|
-
authorId: z.string(),
|
|
34
|
-
visibility: z.enum(["public", "private"]),
|
|
35
|
-
}),
|
|
36
|
-
comment: z.object({
|
|
37
|
-
id: z.string(),
|
|
38
|
-
postId: z.string(),
|
|
39
|
-
authorId: z.string(),
|
|
40
|
-
}),
|
|
34
|
+
post: z.object({ id: z.string(), authorId: z.string() }),
|
|
41
35
|
} satisfies Resources;
|
|
42
36
|
|
|
43
|
-
// 2. Define actions per resource
|
|
44
37
|
const actions = {
|
|
45
38
|
post: ["read", "write", "delete"],
|
|
46
|
-
comment: ["read", "write"],
|
|
47
39
|
} as const satisfies Actions<typeof resources>;
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
type AppContext = {
|
|
51
|
-
user: { id: string; role: "guest" | "user" | "admin" };
|
|
52
|
-
};
|
|
41
|
+
type AppContext = { user: { id: string } };
|
|
53
42
|
|
|
54
|
-
// 4. Create your policy
|
|
55
43
|
const policy = createPolicy<AppContext>({
|
|
56
44
|
resources,
|
|
57
45
|
actions,
|
|
58
46
|
rules: {
|
|
59
47
|
post: {
|
|
60
|
-
read: when((ctx, action, resource) => resource.visibility === "public"),
|
|
61
|
-
write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
62
|
-
delete: deny(),
|
|
63
|
-
},
|
|
64
|
-
comment: {
|
|
65
48
|
read: allow(),
|
|
66
49
|
write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
50
|
+
delete: deny(),
|
|
67
51
|
},
|
|
68
52
|
},
|
|
69
53
|
});
|
|
70
54
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const post = { id: "1", authorId: "user-1", visibility: "public" as const };
|
|
55
|
+
const ctx: AppContext = { user: { id: "user-1" } };
|
|
56
|
+
const post = { id: "1", authorId: "user-1" };
|
|
74
57
|
|
|
75
|
-
await policy.can(ctx, "post:
|
|
76
|
-
await policy.can(ctx, "post:write", post); // true (user is author)
|
|
77
|
-
await policy.can(ctx, "post:delete", post); // false (always denied)
|
|
58
|
+
await policy.can(ctx, "post:write", post); // true, inferred as boolean
|
|
78
59
|
```
|
|
79
60
|
|
|
80
|
-
##
|
|
81
|
-
|
|
82
|
-
### Policy Builders
|
|
61
|
+
## Declarative Policies
|
|
83
62
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Returns a policy function that always allows the action.
|
|
63
|
+
Through `createPolicy(...)` with `allow()`, `deny()`, and `when(condition)`.
|
|
87
64
|
|
|
88
65
|
```ts
|
|
89
66
|
rules: {
|
|
90
67
|
post: {
|
|
91
|
-
read: allow(),
|
|
68
|
+
read: allow(),
|
|
69
|
+
delete: deny(),
|
|
70
|
+
write: when((ctx, action, resource) => ctx.user.id === resource.authorId),
|
|
92
71
|
},
|
|
93
72
|
}
|
|
94
73
|
```
|
|
95
74
|
|
|
96
|
-
|
|
75
|
+
## Role Hierarchy Support
|
|
97
76
|
|
|
98
|
-
|
|
77
|
+
Via `hasRole(role, hierarchy?)`, with inheritance resolved by `collectInheritedRoles`.
|
|
99
78
|
|
|
100
79
|
```ts
|
|
101
|
-
|
|
102
|
-
post: {
|
|
103
|
-
delete: deny(), // No one can delete
|
|
104
|
-
},
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
#### `when(condition)`
|
|
109
|
-
|
|
110
|
-
Returns a policy function that allows or denies based on a condition.
|
|
80
|
+
const hierarchy = { guest: [], user: ["guest"], admin: ["user"] };
|
|
111
81
|
|
|
112
|
-
```ts
|
|
113
82
|
rules: {
|
|
114
83
|
post: {
|
|
115
|
-
|
|
84
|
+
read: when(hasRole("guest", hierarchy)), // admins and users inherit guest access
|
|
116
85
|
},
|
|
117
86
|
}
|
|
118
87
|
```
|
|
119
88
|
|
|
120
|
-
|
|
89
|
+
## Composable Conditions
|
|
121
90
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Returns a condition that is true only if all conditions are true.
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
const isOwnerAndPublished = and(
|
|
128
|
-
(ctx, action, resource) => ctx.user.id === resource.authorId,
|
|
129
|
-
(ctx, action, resource) => resource.status === "published"
|
|
130
|
-
);
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
#### `or(...conditions)`
|
|
134
|
-
|
|
135
|
-
Returns a condition that is true if any condition is true.
|
|
91
|
+
Via `and`, `or`, and `not`.
|
|
136
92
|
|
|
137
93
|
```ts
|
|
138
94
|
const isOwnerOrAdmin = or(
|
|
@@ -141,130 +97,52 @@ const isOwnerOrAdmin = or(
|
|
|
141
97
|
);
|
|
142
98
|
```
|
|
143
99
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
Returns a condition that negates another condition.
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
const isNotOwner = not(
|
|
150
|
-
(ctx, action, resource) => ctx.user.id === resource.authorId
|
|
151
|
-
);
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Context Helpers
|
|
155
|
-
|
|
156
|
-
#### `has(key, value)`
|
|
157
|
-
|
|
158
|
-
Checks if a context property equals a specific value.
|
|
159
|
-
|
|
160
|
-
```ts
|
|
161
|
-
rules: {
|
|
162
|
-
post: {
|
|
163
|
-
write: when(has("role", "admin")),
|
|
164
|
-
},
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
#### `hasRole(role, hierarchy?)`
|
|
169
|
-
|
|
170
|
-
Checks if the user has a specific role, with optional hierarchy support.
|
|
171
|
-
|
|
172
|
-
```ts
|
|
173
|
-
const hierarchy = {
|
|
174
|
-
guest: [],
|
|
175
|
-
user: ["guest"],
|
|
176
|
-
admin: ["user"],
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
rules: {
|
|
180
|
-
post: {
|
|
181
|
-
read: when(hasRole("guest", hierarchy)), // Admins and users inherit guest permissions
|
|
182
|
-
},
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Policy Merging
|
|
187
|
-
|
|
188
|
-
#### `mergePolicies(...policies)`
|
|
100
|
+
## Policy Merging Strategies
|
|
189
101
|
|
|
190
|
-
|
|
102
|
+
Via `mergePoliciesAnd` and `mergePoliciesOr`.
|
|
191
103
|
|
|
192
104
|
```ts
|
|
193
|
-
const merged =
|
|
105
|
+
const merged = mergePoliciesAnd(basePolicy, restrictivePolicy);
|
|
194
106
|
```
|
|
195
107
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Merges policies with "allow-overrides" strategy. Any policy allowing is sufficient.
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
const merged = mergePoliciesAny(guestPolicy, memberPolicy);
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Type Helpers
|
|
205
|
-
|
|
206
|
-
### `Resources`
|
|
108
|
+
## Standard Schema Support
|
|
207
109
|
|
|
208
|
-
|
|
110
|
+
Works with Zod, Valibot, ArkType, or any compatible library.
|
|
209
111
|
|
|
210
112
|
```ts
|
|
113
|
+
// Zod, Valibot, ArkType, or any Standard Schema-compatible library
|
|
211
114
|
const resources = {
|
|
212
115
|
post: z.object({ id: z.string() }),
|
|
213
116
|
} satisfies Resources;
|
|
214
117
|
```
|
|
215
118
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Type helper for defining actions with `satisfies`. Ensures action keys match resource keys.
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
const actions = {
|
|
222
|
-
post: ["read", "write"],
|
|
223
|
-
} as const satisfies Actions<typeof resources>;
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### `InferResource<TResources, K>`
|
|
119
|
+
## Structured Errors
|
|
227
120
|
|
|
228
|
-
|
|
121
|
+
`PolicyError` for invalid configuration or evaluation failures.
|
|
229
122
|
|
|
230
123
|
```ts
|
|
231
|
-
|
|
232
|
-
// { id: string }
|
|
233
|
-
```
|
|
124
|
+
import { PolicyError } from "@zap-studio/permit";
|
|
234
125
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// "read" | "write"
|
|
126
|
+
try {
|
|
127
|
+
const policy = createPolicy(config);
|
|
128
|
+
await policy.can(ctx, "post:read", post);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error instanceof PolicyError) console.error(error.message);
|
|
131
|
+
}
|
|
242
132
|
```
|
|
243
133
|
|
|
244
|
-
##
|
|
245
|
-
|
|
246
|
-
This library uses [Standard Schema](https://standardschema.dev/) for resource validation, which means it works with any compatible schema library:
|
|
134
|
+
## Runtime Support
|
|
247
135
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
136
|
+
| Runtime | Minimum version |
|
|
137
|
+
| ------------------ | ------------------------------------------------ |
|
|
138
|
+
| Node.js | 18.0.0 |
|
|
139
|
+
| Bun | 1.0.0 |
|
|
140
|
+
| Deno | 1.42 |
|
|
141
|
+
| Cloudflare Workers | Any current release |
|
|
142
|
+
| Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
|
|
251
143
|
|
|
252
|
-
|
|
253
|
-
// With Zod
|
|
254
|
-
import { z } from "zod";
|
|
255
|
-
const resources = {
|
|
256
|
-
post: z.object({ id: z.string() }),
|
|
257
|
-
} satisfies Resources;
|
|
144
|
+
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`).
|
|
258
145
|
|
|
259
|
-
|
|
260
|
-
import * as v from "valibot";
|
|
261
|
-
const resources = {
|
|
262
|
-
post: v.object({ id: v.string() }),
|
|
263
|
-
} satisfies Resources;
|
|
146
|
+
## License
|
|
264
147
|
|
|
265
|
-
|
|
266
|
-
import { type } from "arktype";
|
|
267
|
-
const resources = {
|
|
268
|
-
post: type({ id: "string" }),
|
|
269
|
-
} satisfies Resources;
|
|
270
|
-
```
|
|
148
|
+
MIT
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/
|
|
1
|
+
import { ConditionFn, Context, HasRoleFn, 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
|
*
|
|
@@ -133,17 +133,6 @@ declare const has: <TContext extends Context, K extends keyof TContext>(key: K,
|
|
|
133
133
|
* ```
|
|
134
134
|
*/
|
|
135
135
|
declare const collectInheritedRoles: <TRole extends Role = Role>(roles: TRole[], hierarchy: RoleHierarchy<TRole>) => Set<TRole>;
|
|
136
|
-
/**
|
|
137
|
-
* Call signatures for {@link hasRole}, preserving the with/without hierarchy overloads.
|
|
138
|
-
*/
|
|
139
|
-
interface HasRoleFn {
|
|
140
|
-
<TContext extends {
|
|
141
|
-
role: Role | Role[];
|
|
142
|
-
}, TAction extends string = string, TResource = unknown>(role: Role): ConditionFn<TContext, TAction, TResource>;
|
|
143
|
-
<TContext extends {
|
|
144
|
-
role: TRole | TRole[];
|
|
145
|
-
}, TAction extends string = string, TResource = unknown, TRole extends Role = Role>(role: TRole, hierarchy: RoleHierarchy<TRole>): ConditionFn<TContext, TAction, TResource>;
|
|
146
|
-
}
|
|
147
136
|
/**
|
|
148
137
|
* Returns a condition function that checks if the user has a specific role.
|
|
149
138
|
* Supports role hierarchy for inherited permissions.
|
|
@@ -172,90 +161,6 @@ interface HasRoleFn {
|
|
|
172
161
|
* ```
|
|
173
162
|
*/
|
|
174
163
|
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
164
|
//#endregion
|
|
260
|
-
export { allow, and, collectInheritedRoles,
|
|
261
|
-
//# sourceMappingURL=
|
|
165
|
+
export { allow, and, collectInheritedRoles, deny, has, hasRole, not, or, when };
|
|
166
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","names":[],"sources":["../src/conditions.ts"],"mappings":";;;;;;;;;;;;;;;;;;cAgCa,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgDM,SAAS"}
|