react-ability-kit 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/README.md +192 -125
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,22 +1,35 @@
1
1
  # React Ability
2
2
 
3
- A small, typed permission layer for React that keeps authorization logic **out of your components** and **in one place**.
3
+ **A small, strongly-typed permission layer for React**
4
+ Keep authorization logic **out of your components** and **in one place**.
4
5
 
5
6
  ---
6
7
 
7
- ## Core idea (in one sentence)
8
+ ## Why this exists
8
9
 
9
- > The package centralizes and standardizes permission logic so your UI doesn’t turn into a mess of `if (user.role === …)` checks scattered everywhere.
10
+ Most React apps don’t plan to become permission nightmares they just grow into one.
10
11
 
11
- That’s it. Everything else is implementation details.
12
+ Permissions slowly spread across components as ad-hoc checks:
13
+
14
+ - `if (user.role === "admin")`
15
+ - `if (invoice.ownerId === user.id)`
16
+ - `if (permissions.includes("invoice:update"))`
17
+
18
+ This package introduces a **policy-first** approach to permissions, so your UI stays clean and your rules stay auditable.
12
19
 
13
20
  ---
14
21
 
15
- ## The real problem (what goes wrong in real apps)
22
+ ## Core idea (one sentence)
23
+
24
+ > Define permission rules once, then query them everywhere — instead of scattering fragile `if` checks across your UI.
25
+
26
+ Everything else is just implementation details.
27
+
28
+ ---
16
29
 
17
- Let’s start with how apps usually look **without** a permission layer.
30
+ ## The real problem (what goes wrong in real apps)
18
31
 
19
- ### ❌ Without a package (today’s reality)
32
+ ### ❌ Without a permission layer
20
33
 
21
34
  ```tsx
22
35
  // Button.tsx
@@ -41,69 +54,144 @@ if (user && user.role !== "guest") {
41
54
  }
42
55
  ```
43
56
 
57
+ ### Problems this creates
44
58
 
45
- Problems this creates
46
- Logic duplication
47
- The same rules are written differently in many files.
48
-
49
- Rules drift
50
- Someone updates one condition but forgets others.
59
+ - **Logic duplication** – same rules rewritten in different places
60
+ - **Rules drift** – one condition changes, others don’t
61
+ - **Impossible to audit** “Who can edit invoices?” → grep the whole repo
62
+ - ❌ **UI inconsistencies**
63
+ - Button visible but API rejects
64
+ - Button hidden but API allows
65
+ - ❌ **No type safety**
51
66
 
52
- ❌ Impossible to audit
53
- “Who can edit invoices?” you must search the entire codebase.
67
+ ```ts
68
+ "inovice:update" // typo = silent bug
69
+ ```
54
70
 
55
- UI bugs
56
- Button visible but API rejects
71
+ - **Hard to evolve roles** – adding a new role breaks logic everywhere
57
72
 
58
- Button hidden but API allows
73
+ ---
59
74
 
60
- No type safety
61
- ts
62
- Copier le code
63
- "inovice:update" // typo = silent bug
64
- ❌ Hard to change roles
65
- Adding a new role breaks logic everywhere.
75
+ ## The missing abstraction: policy-first permissions
66
76
 
67
- What this package introduces (the missing abstraction)
68
- Key idea: policy-first permissions
69
77
  Instead of asking:
70
78
 
71
- “Can the user do this?”
72
-
73
- everywhere in the UI…
79
+ > “Can the user do this?”
80
+ > everywhere in the UI…
74
81
 
75
82
  You define rules once, then query them everywhere.
76
83
 
77
- Mental model (important)
78
- Think of your app like this:
84
+ ### Mental model
79
85
 
80
- ```bash
86
+ ```text
81
87
  User + Context → Ability → UI decisions
82
88
  ```
83
89
 
84
- react-ability-kit only handles the Ability part.
90
+ ```text
91
+ User ──► Policy ──► Ability ──► UI / Components
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Installation
85
97
 
86
98
  ```bash
87
- User ──► Policy ──► Ability ──► UI / Components
99
+ npm install react-ability
88
100
  ```
89
101
 
90
- What the package actually solves (concretely)
91
- 1️⃣ Single source of truth for permissions
92
- Instead of scattered checks, you get one policy file:
102
+ or
103
+
104
+ ```bash
105
+ pnpm add react-ability
106
+ ```
107
+
108
+ or
109
+
110
+ ```bash
111
+ yarn add react-ability
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Quick start (5 minutes)
117
+
118
+ ### 1️⃣ Define your abilities (policy-first)
119
+
120
+ Create a single policy file.
121
+
122
+ ```ts
123
+ // ability.ts
124
+ import { defineAbility } from "react-ability";
125
+
126
+ export const ability = defineAbility((allow, deny, user) => {
127
+ allow("read", "Invoice");
128
+
129
+ allow(
130
+ "update",
131
+ "Invoice",
132
+ invoice => invoice.ownerId === user.id && invoice.status === "draft"
133
+ );
134
+
135
+ deny("delete", "Invoice");
136
+ });
137
+ ```
138
+
139
+ This file is your **single source of truth**.
140
+
141
+ ---
142
+
143
+ ### 2️⃣ Provide the ability to your app
144
+
145
+ ```tsx
146
+ import { AbilityProvider } from "react-ability";
147
+ import { ability } from "./ability";
148
+
149
+ export function App() {
150
+ return (
151
+ <AbilityProvider ability={ability}>
152
+ <YourApp />
153
+ </AbilityProvider>
154
+ );
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ### 3️⃣ Use permissions anywhere
161
+
162
+ #### Using the `<Can />` component
163
+
164
+ ```tsx
165
+ <Can I="update" a="Invoice" this={invoice}>
166
+ <EditButton />
167
+ </Can>
168
+ ```
169
+
170
+ #### Using the `can()` function
171
+
172
+ ```ts
173
+ const canEdit = can("update", "Invoice", invoice);
174
+ ```
175
+
176
+ ---
177
+
178
+ ## What this package actually solves
179
+
180
+ ### 1️⃣ Single source of truth for permissions
93
181
 
94
182
  ```ts
95
- // policy.ts
96
183
  allow("update", "Invoice", invoice => invoice.ownerId === user.id);
97
184
  deny("delete", "Invoice");
98
185
  ```
99
186
 
100
- Result:
187
+ - All rules live in one place
188
+ - Easy to review, change, and reason about
189
+ - No scattered conditionals
101
190
 
102
- All permission logic lives in one place
191
+ ---
103
192
 
104
- Easy to review, change, and reason about
193
+ ### 2️⃣ Business rules become readable policies
105
194
 
106
- 2️⃣ Turns business rules into readable policies
107
195
  ❌ Before
108
196
 
109
197
  ```ts
@@ -115,7 +203,7 @@ if (
115
203
  )
116
204
  ```
117
205
 
118
- With the package
206
+ After
119
207
 
120
208
  ```ts
121
209
  allow(
@@ -125,12 +213,15 @@ allow(
125
213
  );
126
214
  ```
127
215
 
128
- This is domain language, not UI logic.
216
+ This is **domain language**, not UI logic.
217
+
218
+ ---
219
+
220
+ ### 3️⃣ Removes permission logic from components
129
221
 
130
- 3️⃣ Removes permission logic from components
131
222
  ❌ Before
132
223
 
133
- ```ts
224
+ ```tsx
134
225
  {user?.role === "admin" && <DeleteButton />}
135
226
  ```
136
227
 
@@ -142,10 +233,11 @@ This is domain language, not UI logic.
142
233
  </Can>
143
234
  ```
144
235
 
145
- Components now care only about UI, not authorization details.
236
+ Your components focus on **rendering**, not authorization.
237
+
238
+ ---
146
239
 
147
- 4️⃣ Prevents permission bugs at compile time (TypeScript win)
148
- This is huge.
240
+ ### 4️⃣ Type-safe permissions (TypeScript win)
149
241
 
150
242
  ❌ Without typing
151
243
 
@@ -160,56 +252,39 @@ can("updtae", "Invioce");
160
252
  // ❌ TypeScript error immediately
161
253
  ```
162
254
 
163
- This eliminates an entire class of bugs.
164
-
165
- 5️⃣ Makes ownership rules first-class (not hacks)
166
- Ownership checks are usually scattered:
255
+ This removes an entire class of bugs.
167
256
 
168
- ```ts
169
- if (invoice.ownerId === user.id)
170
- ```
257
+ ---
171
258
 
172
- With this package:
259
+ ### 5️⃣ Ownership rules become first-class
173
260
 
174
261
  ```ts
175
262
  allow("update", "Invoice", invoice => invoice.ownerId === user.id);
176
263
  ```
177
264
 
178
- Ownership logic becomes:
179
-
180
- consistent
265
+ Ownership logic is now:
181
266
 
182
- reusable
267
+ - consistent
268
+ - reusable
269
+ - testable
183
270
 
184
- testable
185
-
186
- 6️⃣ Makes SSR and hydration predictable
187
- Without a system:
188
-
189
- UI flickers
190
-
191
- Buttons appear/disappear after hydration
271
+ ---
192
272
 
193
- Different logic runs on server vs client
273
+ ### 6️⃣ Predictable SSR & hydration
194
274
 
195
- With this package:
275
+ - No permission flicker
276
+ - No server/client mismatch
277
+ - Same rules, same result everywhere
196
278
 
197
- Ability is created once from the same user data
279
+ ---
198
280
 
199
- Server and client render the same decisions
281
+ ## What `<Can />` actually does
200
282
 
201
- What the <Can /> component really is
202
283
  It’s not magic.
203
284
 
204
285
  It simply means:
205
286
 
206
- Render children only if a permission rule passes.”
207
-
208
- Instead of:
209
- ```tsx
210
- if (!canEdit) return null;
211
- ```
212
- You write:
287
+ > Render children **only if the permission passes**
213
288
 
214
289
  ```tsx
215
290
  <Can I="update" a="Invoice" this={invoice}>
@@ -219,68 +294,60 @@ You write:
219
294
 
220
295
  That’s it.
221
296
 
222
- What this package is NOT
223
- This is important.
224
-
225
- ❌ Not an auth system
226
- ❌ Not a backend security layer
227
- ❌ Not a role manager UI
228
- ❌ Not a permission database
229
-
230
- This package:
231
-
232
- does not replace backend checks
233
-
234
- does not handle authentication
297
+ ---
235
298
 
236
- does not store roles
299
+ ## What this package is NOT
237
300
 
238
- It only answers one question:
301
+ Not an authentication system
302
+ ❌ Not a backend security layer
303
+ ❌ Not a role management UI
304
+ ❌ Not a permission database
239
305
 
240
- “Given a user and a resource, is this action allowed?”
306
+ This package **does not**:
241
307
 
242
- When this package makes sense
243
- SaaS dashboards
244
- ✅ Multi-role apps
245
- ✅ B2B products
246
- ✅ Apps with ownership rules
247
- ✅ Teams larger than 1 developer
308
+ - replace backend checks
309
+ - handle authentication
310
+ - store users or roles
248
311
 
249
- When it’s overkill
250
- ❌ Landing pages
251
- ❌ Simple blogs
252
- ❌ Apps with only admin / non-admin logic
312
+ It answers one question only:
253
313
 
254
- Why this is worth publishing
255
- Most developers:
314
+ > **“Given a user and a resource, is this action allowed?”**
256
315
 
257
- feel this pain
316
+ ---
258
317
 
259
- write ad-hoc permission logic
318
+ ## When this package makes sense
260
319
 
261
- never extract it cleanly
320
+ - SaaS dashboards
321
+ - ✅ Multi-role applications
322
+ - ✅ B2B products
323
+ - ✅ Ownership-based rules
324
+ - ✅ Teams larger than one developer
262
325
 
263
- This package:
326
+ ---
264
327
 
265
- gives a clear, repeatable pattern
328
+ ## When it’s overkill
266
329
 
267
- provides excellent TypeScript DX
330
+ - Landing pages
331
+ - ❌ Simple blogs
332
+ - ❌ Admin / non-admin only apps
268
333
 
269
- keeps the API small and focused
334
+ ---
270
335
 
271
- That’s exactly what successful small libraries do.
336
+ ## Final summary
272
337
 
273
- Final simplified summary
274
- This package solves one problem:
338
+ **React Ability solves one problem:**
275
339
 
276
- How do I express and use permissions in React without scattering fragile conditional logic everywhere?”
340
+ > How do I express and use permissions in React without scattering fragile conditional logic everywhere?
277
341
 
278
- It solves it by:
342
+ It does this by:
279
343
 
280
- centralizing permission rules
344
+ - centralizing permission rules
345
+ - typing actions and resources
346
+ - exposing a clean `can()` API
347
+ - providing `<Can />` for UI rendering
281
348
 
282
- typing actions and resources
349
+ ---
283
350
 
284
- exposing a clean can() API
351
+ ## Credits
285
352
 
286
- providing <Can /> for UI rendering
353
+ Created by **Mohamed Ali Sraieb**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-ability-kit",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",