@zap-studio/permit 0.2.0 → 0.2.2

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 CHANGED
@@ -1,57 +1,69 @@
1
1
  # @zap-studio/permit
2
2
 
3
- ## 0.2.0
3
+ ## 0.2.2
4
4
 
5
- ### Minor Changes
5
+ ### Patch Changes
6
6
 
7
- - f0f503e: Make policy evaluation async by default.
7
+ - e26293e: Updated dependencies.
8
+ - @zap-studio/validation@0.3.2
8
9
 
9
- `policy.can(...)` now returns a `Promise<boolean>` and must be awaited.
10
+ ## 0.2.1
10
11
 
11
- `createPolicy` now uses async-safe Standard Schema validation internally, so resource schemas with async validation are supported.
12
+ ### Patch Changes
12
13
 
13
- `mergePolicies` and `mergePoliciesAny` are also async through the shared `Policy` interface.
14
+ - 5ea3d3b: Updated dependencies.
15
+ - @zap-studio/validation@0.3.1
14
16
 
15
- ### Patch Changes
17
+ ## 0.2.0
18
+
19
+ ### Changed
20
+
21
+ - f0f503e: Made policy evaluation asynchronous by default.
22
+
23
+ ### Breaking Changes
24
+
25
+ - f0f503e: `policy.can(...)` now returns `Promise<boolean>`.
26
+ - f0f503e: `createPolicy()` now uses async-safe Standard Schema validation for resource schemas (including async resource schemas); this is not a separate `Policy` schema API.
27
+ - f0f503e: The `Policy` interface changed so `can()` is async, and `mergePolicies`/`mergePoliciesAny` are async accordingly.
28
+ - Action required: callers must `await policy.can(...)` and handle `mergePolicies`/`mergePoliciesAny` as async operations; also account for async-safe resource schema validation in `createPolicy()`.
16
29
 
17
- - Updated dependencies [f75b984]
18
- - @zap-studio/validation@0.3.0
30
+ ### Dependencies
31
+
32
+ - f75b984: Updated dependency `@zap-studio/validation` to `0.3.0`.
19
33
 
20
34
  ## 0.1.3
21
35
 
22
- ### Patch Changes
36
+ ### Dependencies
23
37
 
24
- - Updated dependencies [e4542bb]
25
- - @zap-studio/validation@0.2.1
38
+ - e4542bb: Updated dependency `@zap-studio/validation` to `0.2.1`.
26
39
 
27
40
  ## 0.1.2
28
41
 
29
- ### Patch Changes
42
+ ### Changed
30
43
 
31
- - 2de8183: Add a reusable synchronous Standard Schema validator helper in `@zap-studio/validation` and refactor `@zap-studio/permit` to use it for resource schema validation in `createPolicy`.
32
- - Updated dependencies [2de8183]
33
- - @zap-studio/validation@0.2.0
44
+ - 2de8183: Adopted shared synchronous Standard Schema validator utilities from `@zap-studio/validation` for resource schema validation in `createPolicy`.
34
45
 
35
- ## 0.1.1
46
+ ### Dependencies
36
47
 
37
- ### Patch Changes
48
+ - 2de8183: Updated dependency `@zap-studio/validation` to `0.2.0`.
38
49
 
39
- - 907d903: Add runtime resource validation and fail-closed behavior, including denying access when merges are called with no policies.
40
-
41
- ## 0.1.0
50
+ ## 0.1.1
42
51
 
43
- ### Minor Changes
52
+ ### Fixed
44
53
 
45
- - 0627885: Initial release of @zap-studio/permit - a type-safe, declarative authorization library for TypeScript.
54
+ - 907d903: Added runtime resource validation and fail-closed behavior, including deny-by-default when merges are invoked with no policies.
46
55
 
47
- Features:
56
+ ## 0.1.0
48
57
 
49
- - Declarative policy creation with `createPolicy()`
50
- - Policy rules: `allow()`, `deny()`, and `when()` for conditional access
51
- - Condition combinators: `and()`, `or()`, `not()`, and `has()`
52
- - Role-based access control with `hasRole()` and role hierarchies
53
- - Policy merging with `mergePolicies()` (deny-overrides) and `mergePoliciesAny()` (allow-overrides)
54
- - Standard Schema support (Zod, Valibot, ArkType, etc.)
55
- - Full TypeScript support with type inference
56
- - `PolicyError` class for authorization errors
57
- - `assertNever()` helper for exhaustive type checking
58
+ ### Added
59
+
60
+ - 0627885: Initial release of `@zap-studio/permit`.
61
+ - Declarative policy creation with `createPolicy()`.
62
+ - Policy rules: `allow()`, `deny()`, and `when()` for conditional access.
63
+ - Condition combinators: `and()`, `or()`, `not()`, and `has()`.
64
+ - Role-based access control with `hasRole()` and role hierarchies.
65
+ - Policy merging with `mergePolicies()` (deny-overrides) and `mergePoliciesAny()` (allow-overrides).
66
+ - Standard Schema support (Zod, Valibot, ArkType, and more).
67
+ - Full TypeScript type inference support.
68
+ - `PolicyError` for authorization failures.
69
+ - `assertNever()` for exhaustive checks.
package/bin/intent.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ // Auto-generated by @tanstack/intent setup
3
+ // Exposes the intent end-user CLI for consumers of this library.
4
+ await import("@tanstack/intent/intent-library");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-studio/permit",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -30,12 +30,15 @@
30
30
  "files": [
31
31
  "dist",
32
32
  "CHANGELOG.md",
33
- "LICENSE.md",
34
- "README.md"
33
+ "LICENSE",
34
+ "README.md",
35
+ "skills",
36
+ "bin",
37
+ "!skills/_artifacts"
35
38
  ],
36
39
  "dependencies": {
37
40
  "@standard-schema/spec": "^1.1.0",
38
- "@zap-studio/validation": "0.3.0"
41
+ "@zap-studio/validation": "0.3.2"
39
42
  },
40
43
  "devDependencies": {
41
44
  "@types/node": "^25.0.2",
@@ -43,9 +46,9 @@
43
46
  "tsdown": "^0.18.0",
44
47
  "typescript": "^5.9.3",
45
48
  "vitest": "^4.0.18",
49
+ "@zap-studio/vitest-config": "0.0.0",
46
50
  "@zap-studio/typescript-config": "0.0.0",
47
- "@zap-studio/tsdown-config": "0.0.0",
48
- "@zap-studio/vitest-config": "0.0.0"
51
+ "@zap-studio/tsdown-config": "0.0.0"
49
52
  },
50
53
  "exports": {
51
54
  ".": "./dist/index.mjs",
@@ -57,9 +60,12 @@
57
60
  "main": "./dist/index.mjs",
58
61
  "module": "./dist/index.mjs",
59
62
  "types": "./dist/index.d.mts",
63
+ "bin": {
64
+ "intent": "./bin/intent.js"
65
+ },
60
66
  "scripts": {
61
67
  "build": "tsdown --config tsdown.config.ts",
62
- "check": "tsc --noEmit",
68
+ "typecheck": "tsc --noEmit",
63
69
  "test": "vitest run",
64
70
  "test:watch": "vitest --watch"
65
71
  }
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: zap-permit-policy-authoring
3
+ description: >
4
+ Author typed authorization policies with @zap-studio/permit using
5
+ createPolicy, allow/deny/when, condition combinators, has/hasRole,
6
+ and mergePolicies vs mergePoliciesAny decision strategies.
7
+ type: core
8
+ library: '@zap-studio/permit'
9
+ library_version: '0.2.1'
10
+ sources:
11
+ - 'zap-studio/monorepo:packages/permit/README.md'
12
+ - 'zap-studio/monorepo:packages/permit/src/index.ts'
13
+ - 'zap-studio/monorepo:packages/permit/src/types.ts'
14
+ ---
15
+
16
+ # @zap-studio/permit — Policy Authoring
17
+
18
+ ## Setup
19
+
20
+ ```ts
21
+ import { z } from 'zod';
22
+ import { createPolicy, allow, when } from '@zap-studio/permit';
23
+ import type { Resources, Actions } from '@zap-studio/permit/types';
24
+
25
+ const resources = {
26
+ post: z.object({ id: z.string(), authorId: z.string() }),
27
+ } satisfies Resources;
28
+
29
+ const actions = {
30
+ post: ['read', 'write'],
31
+ } as const satisfies Actions<typeof resources>;
32
+
33
+ type AppContext = { user: { id: string; role: 'user' | 'admin' } };
34
+
35
+ const policy = createPolicy<AppContext>({
36
+ resources,
37
+ actions,
38
+ rules: {
39
+ post: {
40
+ read: allow(),
41
+ write: when((ctx, _action, post) => ctx.user.id === post.authorId),
42
+ },
43
+ },
44
+ });
45
+ ```
46
+
47
+ ## Core Patterns
48
+
49
+ ### Compose conditions with `and`, `or`, and `not`
50
+
51
+ ```ts
52
+ import { when, and, or, not } from '@zap-studio/permit';
53
+
54
+ const canEdit = when(
55
+ and(
56
+ (ctx, _action, post) => ctx.user.id === post.authorId,
57
+ not((_ctx, _action, post) => post.locked === true),
58
+ ),
59
+ );
60
+
61
+ const canRead = when(
62
+ or(
63
+ (_ctx, _action, post) => post.visibility === 'public',
64
+ (ctx, _action, post) => ctx.user.id === post.authorId,
65
+ ),
66
+ );
67
+ ```
68
+
69
+ ### Add role inheritance checks with `hasRole`
70
+
71
+ ```ts
72
+ import { when, hasRole } from '@zap-studio/permit';
73
+
74
+ const hierarchy = {
75
+ guest: [],
76
+ user: ['guest'],
77
+ admin: ['user'],
78
+ } as const;
79
+
80
+ const adminOnly = when(hasRole('admin', hierarchy));
81
+ ```
82
+
83
+ ### Choose merge strategy explicitly
84
+
85
+ ```ts
86
+ import { mergePolicies, mergePoliciesAny } from '@zap-studio/permit';
87
+
88
+ const strict = mergePolicies(basePolicy, tenantPolicy); // all must allow
89
+ const permissive = mergePoliciesAny(basePolicy, temporaryOverridePolicy); // any can allow
90
+ ```
91
+
92
+ ## Common Mistakes
93
+
94
+ ### HIGH Using action missing from actions map
95
+
96
+ Wrong:
97
+
98
+ ```ts
99
+ await policy.can(ctx, 'publish', 'post', post);
100
+ ```
101
+
102
+ Correct:
103
+
104
+ ```ts
105
+ const actions = {
106
+ post: ['read', 'write', 'publish'],
107
+ } as const;
108
+
109
+ await policy.can(ctx, 'publish', 'post', post);
110
+ ```
111
+
112
+ `can()` first checks `actions[resourceType]`; missing actions always resolve to `false`.
113
+
114
+ Source: zap-studio/monorepo:packages/permit/src/index.ts
115
+
116
+ ### HIGH Assuming invalid resources still hit rule function
117
+
118
+ Wrong:
119
+
120
+ ```ts
121
+ await policy.can(ctx, 'write', 'post', { id: 123 } as any);
122
+ ```
123
+
124
+ Correct:
125
+
126
+ ```ts
127
+ await policy.can(ctx, 'write', 'post', {
128
+ id: '123',
129
+ authorId: ctx.user.id,
130
+ });
131
+ ```
132
+
133
+ Resource validation runs before policy evaluation; invalid payloads short-circuit to deny.
134
+
135
+ Source: zap-studio/monorepo:packages/permit/src/index.ts
136
+
137
+ ### MEDIUM Expecting `mergePoliciesAny` to enforce deny-overrides
138
+
139
+ Wrong:
140
+
141
+ ```ts
142
+ const merged = mergePoliciesAny(basePolicy, restrictivePolicy);
143
+ // expecting restrictivePolicy to always win
144
+ ```
145
+
146
+ Correct:
147
+
148
+ ```ts
149
+ const merged = mergePolicies(basePolicy, restrictivePolicy);
150
+ // deny-overrides behavior
151
+ ```
152
+
153
+ `mergePoliciesAny` returns allowed when any policy allows; use `mergePolicies` for strict composition.
154
+
155
+ Source: zap-studio/monorepo:packages/permit/src/index.ts
156
+
157
+ See also: zap-validation-standard-schema/SKILL.md — invalid resource payload behavior.
File without changes