@dspranger/react-permissions 0.1.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/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # @dspranger/react-permissions
2
+
3
+ Action-based permission checking for React.
4
+
5
+ ## Overview
6
+
7
+ `@dspranger/react-permissions` lets you check user permissions in React components and hooks. You supply a list of permission strings (from your auth system, API, or wherever) via a provider, then check against them anywhere in your component tree.
8
+
9
+ Permissions are plain strings. The library doesn't dictate format. A common convention is `resource:action` (e.g. `post:edit`, `comment:delete`) but any string works.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @dspranger/react-permissions -S
15
+ ```
16
+
17
+ Requires React 19 or later as a peer dependency.
18
+
19
+ ## Quick Start
20
+
21
+ ```tsx
22
+ import { PermissionsProvider, Can, usePermission } from '@dspranger/react-permissions';
23
+
24
+ const userPermissions = ['post:edit', 'comment:delete'];
25
+
26
+ function App() {
27
+ return (
28
+ <PermissionsProvider permissions={userPermissions}>
29
+ <Dashboard />
30
+ </PermissionsProvider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ---
36
+
37
+ ## API
38
+
39
+ ### `<PermissionsProvider>`
40
+
41
+ Wraps your app (or any subtree) and makes the permissions list available to all descendants.
42
+
43
+ ```tsx
44
+ <PermissionsProvider permissions={userPermissions}>
45
+ {children}
46
+ </PermissionsProvider>
47
+ ```
48
+
49
+ | Prop | Type | Description |
50
+ |------|------|-------------|
51
+ | `permissions` | `string[]` | The full list of permissions the current user has |
52
+ | `children` | `ReactNode` | Your component tree |
53
+
54
+ ---
55
+
56
+ ### `usePermission`
57
+
58
+ Hook that returns `true` if the current user has the given permission.
59
+
60
+ **Type-level check:** Does this user have this permission at all?
61
+
62
+ ```tsx
63
+ const canEdit = usePermission('post:edit');
64
+ ```
65
+
66
+ **Type-level check with condition:** Permission plus an extra check that isn't specific to any resource (e.g. a global flag, feature toggle, or user-level state) :
67
+
68
+ ```tsx
69
+ // App-wide read-only mode
70
+ const canEdit = usePermission('post:edit', () => !isReadOnlyMode);
71
+
72
+ // User must have a verified email to publish
73
+ const canPublish = usePermission('post:publish', () => currentUser.emailVerified);
74
+ ```
75
+
76
+ **Instance-level check:** Permission plus a check against a specific resource. TypeScript infers the resource type from whatever you pass.
77
+
78
+ ```tsx
79
+ const canEdit = usePermission('post:edit', post, (p) => p.authorId === currentUser.id);
80
+ ```
81
+
82
+ | Signature | Parameters |
83
+ |-----------|-----------|
84
+ | `usePermission(action)` | `action: string` |
85
+ | `usePermission(action, condition)` | `action: string`, `condition: () => boolean` |
86
+ | `usePermission(action, resource, condition)` | `action: string`, `resource: R`, `condition: (resource: R) => boolean` |
87
+
88
+ Returns `boolean`. Throws if called outside a `<PermissionsProvider>`.
89
+
90
+ ---
91
+
92
+ ### `<Can>`
93
+
94
+ Component wrapper around `usePermission`. Renders `children` when the check passes, nothing otherwise.
95
+
96
+ **Type-level check:**
97
+
98
+ ```tsx
99
+ <Can action="post:edit">
100
+ <EditButton />
101
+ </Can>
102
+ ```
103
+
104
+ **Type-level check with condition:** Permission plus a global or user-level check, not tied to any specific resource:
105
+
106
+ ```tsx
107
+ <Can action="post:edit" condition={() => !isReadOnlyMode}>
108
+ <EditButton />
109
+ </Can>
110
+ ```
111
+
112
+ **Instance-level check:**
113
+
114
+ ```tsx
115
+ <Can action="post:edit" resource={post} condition={(p) => p.authorId === currentUser.id}>
116
+ <EditButton />
117
+ </Can>
118
+ ```
119
+
120
+ | Prop | Type | Required | Description |
121
+ |------|------|----------|-------------|
122
+ | `action` | `string` | Yes | The permission string to check |
123
+ | `children` | `ReactNode` | Yes | Rendered when the check passes |
124
+ | `resource` | `R` | No | A specific resource instance to check against |
125
+ | `condition` | `() => boolean` or `(resource: R) => boolean` | No | Extra check run after the action check passes. Required when `resource` is provided. |
126
+
127
+ > When `resource` is provided, `condition` is required and TypeScript enforces that it accepts the same type as `resource`.
128
+
129
+ ---
130
+
131
+ ## Full Example
132
+
133
+ ```tsx
134
+ import { PermissionsProvider, Can, usePermission } from '@dspranger/react-permissions';
135
+
136
+ type Post = {
137
+ id: string;
138
+ authorId: string;
139
+ title: string;
140
+ };
141
+
142
+ function PostActions({ post, currentUserId }: { post: Post; currentUserId: string }) {
143
+ // Hook for imperative checks
144
+ const canDelete = usePermission(
145
+ 'post:delete',
146
+ post,
147
+ (p) => p.authorId === currentUserId,
148
+ );
149
+
150
+ return (
151
+ <div>
152
+ {/* Type-level: can this user edit any post? */}
153
+ <Can action="post:edit">
154
+ <button>Edit</button>
155
+ </Can>
156
+
157
+ {/* Instance-level via hook */}
158
+ {canDelete && <button>Delete</button>}
159
+
160
+ {/* Instance-level via component: own posts only */}
161
+ <Can
162
+ action="post:edit"
163
+ resource={post}
164
+ condition={(p) => p.authorId === currentUserId}
165
+ >
166
+ <button>Edit (owner only)</button>
167
+ </Can>
168
+ </div>
169
+ );
170
+ }
171
+
172
+ function App({ currentUser }: { currentUser: { id: string; permissions: string[] } }) {
173
+ return (
174
+ <PermissionsProvider permissions={currentUser.permissions}>
175
+ <PostActions post={post} currentUserId={currentUser.id} />
176
+ </PermissionsProvider>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ## How Permission Strings Work
184
+
185
+ The library treats permission strings as opaque. It checks whether the string exists in the list, nothing more. The format is entirely up to you:
186
+
187
+ ```ts
188
+ // resource:action (most common)
189
+ ['post:read', 'post:edit', 'comment:delete']
190
+
191
+ // verb-first
192
+ ['read:posts', 'edit:posts']
193
+
194
+ // flat
195
+ ['edit_posts', 'delete_comments']
196
+ ```
197
+
198
+ The `condition` function is where instance-level logic lives. Ownership checks, status checks, or anything else specific to a particular resource. The library passes the resource through to your condition and stays out of the way.
199
+
200
+ ---
201
+
202
+ ## License
203
+
204
+ ISC
package/dist/index.cjs ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Can: () => Can,
24
+ PermissionsProvider: () => PermissionsProvider,
25
+ usePermission: () => usePermission
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/PermissionsContext.ts
30
+ var import_react = require("react");
31
+ var PermissionsContext = (0, import_react.createContext)(null);
32
+ var usePermissionsContext = () => (0, import_react.useContext)(PermissionsContext);
33
+
34
+ // src/usePermission.ts
35
+ function usePermission(action, resourceOrCondition, condition) {
36
+ const permissions = usePermissionsContext();
37
+ if (permissions === null)
38
+ throw new Error("usePermission must be used within a PermissionsProvider");
39
+ if (!permissions.includes(action)) return false;
40
+ if (resourceOrCondition === void 0) return true;
41
+ if (typeof resourceOrCondition === "function")
42
+ return resourceOrCondition();
43
+ return condition !== void 0 ? condition(resourceOrCondition) : true;
44
+ }
45
+
46
+ // src/Can.tsx
47
+ var import_jsx_runtime = require("react/jsx-runtime");
48
+ function hasResource(props) {
49
+ return "resource" in props;
50
+ }
51
+ function Can(props) {
52
+ const condition = hasResource(props) ? () => props.condition(props.resource) : props.condition;
53
+ const allowed = usePermission(props.action, condition);
54
+ return allowed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: props.children }) : null;
55
+ }
56
+
57
+ // src/PermissionsProvider.tsx
58
+ var import_jsx_runtime2 = require("react/jsx-runtime");
59
+ function PermissionsProvider({
60
+ permissions,
61
+ children
62
+ }) {
63
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PermissionsContext.Provider, { value: permissions, children });
64
+ }
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ Can,
68
+ PermissionsProvider,
69
+ usePermission
70
+ });
71
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/PermissionsContext.ts","../src/usePermission.ts","../src/Can.tsx","../src/PermissionsProvider.tsx"],"sourcesContent":["export { Can } from './Can';\nexport { PermissionsProvider } from './PermissionsProvider';\nexport { usePermission } from './usePermission';\n","import { createContext, useContext } from 'react';\n\n// string[] | null — null lets the hook detect a missing PermissionsProvider\n// and throw a clear error rather than silently returning an empty array.\nexport const PermissionsContext = createContext<string[] | null>(null);\n\n// Pre-bound hook — call this inside components instead of useContext directly\nexport const usePermissionsContext = () => useContext(PermissionsContext);\n","import { usePermissionsContext } from './PermissionsContext';\n\n// Overload 1:\n// Type-level check. No resource. Optional no-arg condition.\nexport function usePermission(\n action: string,\n condition?: () => boolean,\n): boolean;\n\n// Overload 2:\n// Instance-level check. the type of R is inferred from resource.\n// Optional condition receives it as an argument.\nexport function usePermission<R>(\n action: string,\n resource: R,\n condition: (resource: R) => boolean,\n): boolean;\n\n// Function implementation:\n// TypeScript hides this signature from callers, only the overloads above are public\nexport function usePermission<R>(\n action: string,\n resourceOrCondition?: R | (() => boolean),\n condition?: (resource: R) => boolean,\n): boolean {\n const permissions = usePermissionsContext();\n\n if (permissions === null)\n throw new Error('usePermission must be used within a PermissionsProvider');\n\n if (!permissions.includes(action)) return false;\n\n if (resourceOrCondition === undefined) return true;\n\n // resourceOrCondition is a function, this is overload 1's condition being passed in the second slot.\n // Cast needed because typeof can't narrow `R | (() => boolean)` to `() => boolean on its own.\n if (typeof resourceOrCondition === 'function')\n return (resourceOrCondition as () => boolean)();\n\n // resourceOrCondition is a resource. Pass it to condition, or allow if no condition was given.\n return condition !== undefined ? condition(resourceOrCondition) : true;\n}\n","import type { ReactNode } from 'react';\nimport { usePermission } from './usePermission';\n\n// Two separate prop shapes are needed to enforce the relationship between resource and condition at the type level. If\n// you pass one, you must pass the other.\n\n// Shape 1:\n// Instance-level check. resource and a matching condition are both required.\ntype CanWithResource<R> = {\n action: string;\n resource: R;\n condition: (resource: R) => boolean;\n children: ReactNode;\n};\n\n// Shape 2:\n// Type-level check. Resource is forbidden (`never`), condition is optional. `resource?: never` means the prop cannot be\n// passed at all in this shape, which is stronger than `resource?: undefined`.\ntype CanWithoutResource = {\n action: string;\n resource?: never;\n condition?: () => boolean;\n children: ReactNode;\n};\n\n// The union of both shapes. TypeScript treats Can as accepting either one.\ntype CanProps<R> = CanWithResource<R> | CanWithoutResource;\n\n// Type Predicate:\n// The return type `props is CanWithResource<R>` is a promise to TypeScript: \"When this returns true, props is\n// definitely CanWithResource<R>.\" This is needed because `'resource' in props` alone doesn't reliably narrow a union\n// where one member has `resource?: never`.\nfunction hasResource<R>(props: CanProps<R>): props is CanWithResource<R> {\n return 'resource' in props;\n}\n\n// R is inferred from whatever is passed as `resource`. If no `resource` is passed, `R` is unused and `condition` stays\n// as `() => boolean`.\nexport function Can<R>(props: CanProps<R>) {\n // Normalize both shapes into a no-arg condition so `usePermission` can be called unconditionally (calling hooks inside\n // conditionals is not allowed in React). If `resource` was provided, wrap it in a closure so it gets passed to\n // `condition` at call time.\n const condition = hasResource(props)\n ? () => props.condition(props.resource)\n : props.condition;\n\n const allowed = usePermission(props.action, condition);\n\n return allowed ? <>{props.children}</> : null;\n}\n","import { PermissionsContext } from './PermissionsContext';\nimport type { ReactNode } from 'react';\n\ntype PermissionsProviderProps = {\n permissions: string[];\n children: ReactNode;\n};\n\nexport function PermissionsProvider({\n permissions,\n children,\n}: PermissionsProviderProps) {\n return (\n <PermissionsContext.Provider value={permissions}>\n {children}\n </PermissionsContext.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA0C;AAInC,IAAM,yBAAqB,4BAA+B,IAAI;AAG9D,IAAM,wBAAwB,UAAM,yBAAW,kBAAkB;;;ACajE,SAAS,cACd,QACA,qBACA,WACS;AACT,QAAM,cAAc,sBAAsB;AAE1C,MAAI,gBAAgB;AAClB,UAAM,IAAI,MAAM,yDAAyD;AAE3E,MAAI,CAAC,YAAY,SAAS,MAAM,EAAG,QAAO;AAE1C,MAAI,wBAAwB,OAAW,QAAO;AAI9C,MAAI,OAAO,wBAAwB;AACjC,WAAQ,oBAAsC;AAGhD,SAAO,cAAc,SAAY,UAAU,mBAAmB,IAAI;AACpE;;;ACOmB;AAhBnB,SAAS,YAAe,OAAiD;AACvE,SAAO,cAAc;AACvB;AAIO,SAAS,IAAO,OAAoB;AAIzC,QAAM,YAAY,YAAY,KAAK,IAC/B,MAAM,MAAM,UAAU,MAAM,QAAQ,IACpC,MAAM;AAEV,QAAM,UAAU,cAAc,MAAM,QAAQ,SAAS;AAErD,SAAO,UAAU,2EAAG,gBAAM,UAAS,IAAM;AAC3C;;;ACpCI,IAAAA,sBAAA;AALG,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAA6B;AAC3B,SACE,6CAAC,mBAAmB,UAAnB,EAA4B,OAAO,aACjC,UACH;AAEJ;","names":["import_jsx_runtime"]}
@@ -0,0 +1,28 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type CanWithResource<R> = {
5
+ action: string;
6
+ resource: R;
7
+ condition: (resource: R) => boolean;
8
+ children: ReactNode;
9
+ };
10
+ type CanWithoutResource = {
11
+ action: string;
12
+ resource?: never;
13
+ condition?: () => boolean;
14
+ children: ReactNode;
15
+ };
16
+ type CanProps<R> = CanWithResource<R> | CanWithoutResource;
17
+ declare function Can<R>(props: CanProps<R>): react.JSX.Element | null;
18
+
19
+ type PermissionsProviderProps = {
20
+ permissions: string[];
21
+ children: ReactNode;
22
+ };
23
+ declare function PermissionsProvider({ permissions, children, }: PermissionsProviderProps): react.JSX.Element;
24
+
25
+ declare function usePermission(action: string, condition?: () => boolean): boolean;
26
+ declare function usePermission<R>(action: string, resource: R, condition: (resource: R) => boolean): boolean;
27
+
28
+ export { Can, PermissionsProvider, usePermission };
@@ -0,0 +1,28 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type CanWithResource<R> = {
5
+ action: string;
6
+ resource: R;
7
+ condition: (resource: R) => boolean;
8
+ children: ReactNode;
9
+ };
10
+ type CanWithoutResource = {
11
+ action: string;
12
+ resource?: never;
13
+ condition?: () => boolean;
14
+ children: ReactNode;
15
+ };
16
+ type CanProps<R> = CanWithResource<R> | CanWithoutResource;
17
+ declare function Can<R>(props: CanProps<R>): react.JSX.Element | null;
18
+
19
+ type PermissionsProviderProps = {
20
+ permissions: string[];
21
+ children: ReactNode;
22
+ };
23
+ declare function PermissionsProvider({ permissions, children, }: PermissionsProviderProps): react.JSX.Element;
24
+
25
+ declare function usePermission(action: string, condition?: () => boolean): boolean;
26
+ declare function usePermission<R>(action: string, resource: R, condition: (resource: R) => boolean): boolean;
27
+
28
+ export { Can, PermissionsProvider, usePermission };
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ // src/PermissionsContext.ts
2
+ import { createContext, useContext } from "react";
3
+ var PermissionsContext = createContext(null);
4
+ var usePermissionsContext = () => useContext(PermissionsContext);
5
+
6
+ // src/usePermission.ts
7
+ function usePermission(action, resourceOrCondition, condition) {
8
+ const permissions = usePermissionsContext();
9
+ if (permissions === null)
10
+ throw new Error("usePermission must be used within a PermissionsProvider");
11
+ if (!permissions.includes(action)) return false;
12
+ if (resourceOrCondition === void 0) return true;
13
+ if (typeof resourceOrCondition === "function")
14
+ return resourceOrCondition();
15
+ return condition !== void 0 ? condition(resourceOrCondition) : true;
16
+ }
17
+
18
+ // src/Can.tsx
19
+ import { Fragment, jsx } from "react/jsx-runtime";
20
+ function hasResource(props) {
21
+ return "resource" in props;
22
+ }
23
+ function Can(props) {
24
+ const condition = hasResource(props) ? () => props.condition(props.resource) : props.condition;
25
+ const allowed = usePermission(props.action, condition);
26
+ return allowed ? /* @__PURE__ */ jsx(Fragment, { children: props.children }) : null;
27
+ }
28
+
29
+ // src/PermissionsProvider.tsx
30
+ import { jsx as jsx2 } from "react/jsx-runtime";
31
+ function PermissionsProvider({
32
+ permissions,
33
+ children
34
+ }) {
35
+ return /* @__PURE__ */ jsx2(PermissionsContext.Provider, { value: permissions, children });
36
+ }
37
+ export {
38
+ Can,
39
+ PermissionsProvider,
40
+ usePermission
41
+ };
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PermissionsContext.ts","../src/usePermission.ts","../src/Can.tsx","../src/PermissionsProvider.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n// string[] | null — null lets the hook detect a missing PermissionsProvider\n// and throw a clear error rather than silently returning an empty array.\nexport const PermissionsContext = createContext<string[] | null>(null);\n\n// Pre-bound hook — call this inside components instead of useContext directly\nexport const usePermissionsContext = () => useContext(PermissionsContext);\n","import { usePermissionsContext } from './PermissionsContext';\n\n// Overload 1:\n// Type-level check. No resource. Optional no-arg condition.\nexport function usePermission(\n action: string,\n condition?: () => boolean,\n): boolean;\n\n// Overload 2:\n// Instance-level check. the type of R is inferred from resource.\n// Optional condition receives it as an argument.\nexport function usePermission<R>(\n action: string,\n resource: R,\n condition: (resource: R) => boolean,\n): boolean;\n\n// Function implementation:\n// TypeScript hides this signature from callers, only the overloads above are public\nexport function usePermission<R>(\n action: string,\n resourceOrCondition?: R | (() => boolean),\n condition?: (resource: R) => boolean,\n): boolean {\n const permissions = usePermissionsContext();\n\n if (permissions === null)\n throw new Error('usePermission must be used within a PermissionsProvider');\n\n if (!permissions.includes(action)) return false;\n\n if (resourceOrCondition === undefined) return true;\n\n // resourceOrCondition is a function, this is overload 1's condition being passed in the second slot.\n // Cast needed because typeof can't narrow `R | (() => boolean)` to `() => boolean on its own.\n if (typeof resourceOrCondition === 'function')\n return (resourceOrCondition as () => boolean)();\n\n // resourceOrCondition is a resource. Pass it to condition, or allow if no condition was given.\n return condition !== undefined ? condition(resourceOrCondition) : true;\n}\n","import type { ReactNode } from 'react';\nimport { usePermission } from './usePermission';\n\n// Two separate prop shapes are needed to enforce the relationship between resource and condition at the type level. If\n// you pass one, you must pass the other.\n\n// Shape 1:\n// Instance-level check. resource and a matching condition are both required.\ntype CanWithResource<R> = {\n action: string;\n resource: R;\n condition: (resource: R) => boolean;\n children: ReactNode;\n};\n\n// Shape 2:\n// Type-level check. Resource is forbidden (`never`), condition is optional. `resource?: never` means the prop cannot be\n// passed at all in this shape, which is stronger than `resource?: undefined`.\ntype CanWithoutResource = {\n action: string;\n resource?: never;\n condition?: () => boolean;\n children: ReactNode;\n};\n\n// The union of both shapes. TypeScript treats Can as accepting either one.\ntype CanProps<R> = CanWithResource<R> | CanWithoutResource;\n\n// Type Predicate:\n// The return type `props is CanWithResource<R>` is a promise to TypeScript: \"When this returns true, props is\n// definitely CanWithResource<R>.\" This is needed because `'resource' in props` alone doesn't reliably narrow a union\n// where one member has `resource?: never`.\nfunction hasResource<R>(props: CanProps<R>): props is CanWithResource<R> {\n return 'resource' in props;\n}\n\n// R is inferred from whatever is passed as `resource`. If no `resource` is passed, `R` is unused and `condition` stays\n// as `() => boolean`.\nexport function Can<R>(props: CanProps<R>) {\n // Normalize both shapes into a no-arg condition so `usePermission` can be called unconditionally (calling hooks inside\n // conditionals is not allowed in React). If `resource` was provided, wrap it in a closure so it gets passed to\n // `condition` at call time.\n const condition = hasResource(props)\n ? () => props.condition(props.resource)\n : props.condition;\n\n const allowed = usePermission(props.action, condition);\n\n return allowed ? <>{props.children}</> : null;\n}\n","import { PermissionsContext } from './PermissionsContext';\nimport type { ReactNode } from 'react';\n\ntype PermissionsProviderProps = {\n permissions: string[];\n children: ReactNode;\n};\n\nexport function PermissionsProvider({\n permissions,\n children,\n}: PermissionsProviderProps) {\n return (\n <PermissionsContext.Provider value={permissions}>\n {children}\n </PermissionsContext.Provider>\n );\n}\n"],"mappings":";AAAA,SAAS,eAAe,kBAAkB;AAInC,IAAM,qBAAqB,cAA+B,IAAI;AAG9D,IAAM,wBAAwB,MAAM,WAAW,kBAAkB;;;ACajE,SAAS,cACd,QACA,qBACA,WACS;AACT,QAAM,cAAc,sBAAsB;AAE1C,MAAI,gBAAgB;AAClB,UAAM,IAAI,MAAM,yDAAyD;AAE3E,MAAI,CAAC,YAAY,SAAS,MAAM,EAAG,QAAO;AAE1C,MAAI,wBAAwB,OAAW,QAAO;AAI9C,MAAI,OAAO,wBAAwB;AACjC,WAAQ,oBAAsC;AAGhD,SAAO,cAAc,SAAY,UAAU,mBAAmB,IAAI;AACpE;;;ACOmB;AAhBnB,SAAS,YAAe,OAAiD;AACvE,SAAO,cAAc;AACvB;AAIO,SAAS,IAAO,OAAoB;AAIzC,QAAM,YAAY,YAAY,KAAK,IAC/B,MAAM,MAAM,UAAU,MAAM,QAAQ,IACpC,MAAM;AAEV,QAAM,UAAU,cAAc,MAAM,QAAQ,SAAS;AAErD,SAAO,UAAU,gCAAG,gBAAM,UAAS,IAAM;AAC3C;;;ACpCI,gBAAAA,YAAA;AALG,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAA6B;AAC3B,SACE,gBAAAA,KAAC,mBAAmB,UAAnB,EAA4B,OAAO,aACjC,UACH;AAEJ;","names":["jsx"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@dspranger/react-permissions",
3
+ "author": "dspranger",
4
+ "version": "0.1.0",
5
+ "description": "Action-based permission checking for React",
6
+ "keywords": [
7
+ "typescript",
8
+ "react",
9
+ "reactjs",
10
+ "hooks",
11
+ "permissions",
12
+ "permission",
13
+ "authorization"
14
+ ],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.mjs",
20
+ "require": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "sideEffects": false,
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "prepublishOnly": "npm run typecheck && npm test && npm run build",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "devDependencies": {
35
+ "@testing-library/jest-dom": "^7.0.0",
36
+ "@testing-library/react": "^16.3.2",
37
+ "@types/react": "^19.2.17",
38
+ "jsdom": "^29.1.1",
39
+ "react": "*",
40
+ "react-dom": "^19.2.8",
41
+ "tsup": "^8.5.1",
42
+ "vitest": "^4.1.10"
43
+ },
44
+ "peerDependencies": {
45
+ "react": "^19.2.8"
46
+ }
47
+ }