@zap-studio/permit 0.2.1 → 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,5 +1,12 @@
1
1
  # @zap-studio/permit
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - e26293e: Updated dependencies.
8
+ - @zap-studio/validation@0.3.2
9
+
3
10
  ## 0.2.1
4
11
 
5
12
  ### Patch Changes
@@ -7,7 +14,6 @@
7
14
  - 5ea3d3b: Updated dependencies.
8
15
  - @zap-studio/validation@0.3.1
9
16
 
10
-
11
17
  ## 0.2.0
12
18
 
13
19
  ### Changed
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.1",
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.1"
41
+ "@zap-studio/validation": "0.3.2"
39
42
  },
40
43
  "devDependencies": {
41
44
  "@types/node": "^25.0.2",
@@ -57,6 +60,9 @@
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
68
  "typecheck": "tsc --noEmit",
@@ -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