react-permission-gate 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/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/core/index.cjs +82 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +73 -0
- package/dist/core/index.d.ts +73 -0
- package/dist/core/index.js +54 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.cjs +169 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +94 -0
- package/dist/react/index.d.ts +94 -0
- package/dist/react/index.js +140 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react-router/index.cjs +101 -0
- package/dist/react-router/index.cjs.map +1 -0
- package/dist/react-router/index.d.cts +31 -0
- package/dist/react-router/index.d.ts +31 -0
- package/dist/react-router/index.js +74 -0
- package/dist/react-router/index.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,169 @@
|
|
|
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/react/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PermissionGate: () => PermissionGate,
|
|
24
|
+
PermissionProvider: () => PermissionProvider,
|
|
25
|
+
usePermissions: () => usePermissions
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/react/provider.tsx
|
|
30
|
+
var import_react2 = require("react");
|
|
31
|
+
|
|
32
|
+
// src/core/permission-checker.ts
|
|
33
|
+
var PermissionChecker = class {
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.permissions = new Set(config.permissions);
|
|
36
|
+
const adminTypes = config.adminTypes ?? ["ADMIN"];
|
|
37
|
+
const bypass = config.adminBypass ?? true;
|
|
38
|
+
this.isAdmin = bypass && adminTypes.includes(config.userType ?? "");
|
|
39
|
+
}
|
|
40
|
+
/** Whether this checker was created with an admin-type user */
|
|
41
|
+
get admin() {
|
|
42
|
+
return this.isAdmin;
|
|
43
|
+
}
|
|
44
|
+
/** Check if user has a specific permission. Admins always return true. */
|
|
45
|
+
hasPermission(permission) {
|
|
46
|
+
if (this.isAdmin) return true;
|
|
47
|
+
return this.permissions.has(permission);
|
|
48
|
+
}
|
|
49
|
+
/** Check if user has ANY of the given permissions. Admins always return true. */
|
|
50
|
+
hasAnyPermission(permissionList) {
|
|
51
|
+
if (this.isAdmin) return true;
|
|
52
|
+
return permissionList.some((p) => this.permissions.has(p));
|
|
53
|
+
}
|
|
54
|
+
/** Check if user has ALL of the given permissions. Admins always return true. */
|
|
55
|
+
hasAllPermissions(permissionList) {
|
|
56
|
+
if (this.isAdmin) return true;
|
|
57
|
+
return permissionList.every((p) => this.permissions.has(p));
|
|
58
|
+
}
|
|
59
|
+
/** Detailed permission check with reason for the result */
|
|
60
|
+
check(permission) {
|
|
61
|
+
if (this.isAdmin) {
|
|
62
|
+
return { granted: true, reason: "admin_bypass" };
|
|
63
|
+
}
|
|
64
|
+
if (this.permissions.has(permission)) {
|
|
65
|
+
return { granted: true, reason: "permission_found" };
|
|
66
|
+
}
|
|
67
|
+
return { granted: false, reason: "permission_missing" };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/react/context.ts
|
|
72
|
+
var import_react = require("react");
|
|
73
|
+
var PermissionContext = (0, import_react.createContext)(
|
|
74
|
+
null
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// src/react/provider.tsx
|
|
78
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
79
|
+
function PermissionProvider({
|
|
80
|
+
permissions,
|
|
81
|
+
userType = "",
|
|
82
|
+
adminTypes,
|
|
83
|
+
adminBypass,
|
|
84
|
+
loading = false,
|
|
85
|
+
children
|
|
86
|
+
}) {
|
|
87
|
+
const checker = (0, import_react2.useMemo)(
|
|
88
|
+
() => new PermissionChecker({
|
|
89
|
+
permissions,
|
|
90
|
+
userType,
|
|
91
|
+
adminTypes,
|
|
92
|
+
adminBypass
|
|
93
|
+
}),
|
|
94
|
+
[permissions, userType, adminTypes, adminBypass]
|
|
95
|
+
);
|
|
96
|
+
const value = (0, import_react2.useMemo)(
|
|
97
|
+
() => ({
|
|
98
|
+
checker,
|
|
99
|
+
permissions,
|
|
100
|
+
userType,
|
|
101
|
+
isAdmin: checker.admin,
|
|
102
|
+
isLoading: loading
|
|
103
|
+
}),
|
|
104
|
+
[checker, permissions, userType, loading]
|
|
105
|
+
);
|
|
106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PermissionContext.Provider, { value, children });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/react/use-permissions.ts
|
|
110
|
+
var import_react3 = require("react");
|
|
111
|
+
function usePermissions() {
|
|
112
|
+
const context = (0, import_react3.useContext)(PermissionContext);
|
|
113
|
+
if (!context) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"usePermissions must be used within a <PermissionProvider>"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const { checker, permissions, userType, isAdmin, isLoading } = context;
|
|
119
|
+
return {
|
|
120
|
+
/** Raw permission strings */
|
|
121
|
+
permissions,
|
|
122
|
+
/** User type string */
|
|
123
|
+
userType,
|
|
124
|
+
/** Whether the user is an admin type */
|
|
125
|
+
isAdmin,
|
|
126
|
+
/** Whether permission data is still loading */
|
|
127
|
+
isLoading,
|
|
128
|
+
/** Check if user has a specific permission */
|
|
129
|
+
hasPermission: (permission) => checker.hasPermission(permission),
|
|
130
|
+
/** Check if user has ANY of the given permissions */
|
|
131
|
+
hasAnyPermission: (permissionList) => checker.hasAnyPermission(permissionList),
|
|
132
|
+
/** Check if user has ALL of the given permissions */
|
|
133
|
+
hasAllPermissions: (permissionList) => checker.hasAllPermissions(permissionList),
|
|
134
|
+
/** Detailed permission check with reason */
|
|
135
|
+
check: (permission) => checker.check(permission)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/react/permission-gate.tsx
|
|
140
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
141
|
+
function PermissionGate({
|
|
142
|
+
permission,
|
|
143
|
+
anyOf,
|
|
144
|
+
allOf,
|
|
145
|
+
children,
|
|
146
|
+
fallback = null
|
|
147
|
+
}) {
|
|
148
|
+
const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin } = usePermissions();
|
|
149
|
+
if (isAdmin) {
|
|
150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
|
|
151
|
+
}
|
|
152
|
+
if (permission && !hasPermission(permission)) {
|
|
153
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
|
|
154
|
+
}
|
|
155
|
+
if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {
|
|
156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
|
|
157
|
+
}
|
|
158
|
+
if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {
|
|
159
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
|
|
160
|
+
}
|
|
161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
|
|
162
|
+
}
|
|
163
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
164
|
+
0 && (module.exports = {
|
|
165
|
+
PermissionGate,
|
|
166
|
+
PermissionProvider,
|
|
167
|
+
usePermissions
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts","../../src/react/provider.tsx","../../src/core/permission-checker.ts","../../src/react/context.ts","../../src/react/use-permissions.ts","../../src/react/permission-gate.tsx"],"sourcesContent":["export { PermissionProvider } from \"./provider\";\nexport type { PermissionProviderProps } from \"./provider\";\nexport { usePermissions } from \"./use-permissions\";\nexport { PermissionGate } from \"./permission-gate\";\nexport type { PermissionGateProps } from \"./permission-gate\";\n","import { useMemo, type ReactNode } from \"react\";\nimport { PermissionChecker } from \"../core/permission-checker\";\nimport { PermissionContext } from \"./context\";\n\nexport interface PermissionProviderProps {\n /** List of permission strings the user has */\n permissions: string[];\n /** User's type/role identifier */\n userType?: string;\n /** User types that bypass all permission checks (default: [\"ADMIN\"]) */\n adminTypes?: string[];\n /** Whether admin types bypass checks (default: true) */\n adminBypass?: boolean;\n /** External loading state (e.g., while fetching user data) */\n loading?: boolean;\n children: ReactNode;\n}\n\n/**\n * Provides permission context to the component tree.\n *\n * @example\n * <PermissionProvider\n * permissions={user.permissions}\n * userType={user.user_type}\n * loading={isLoading}\n * >\n * <App />\n * </PermissionProvider>\n */\nexport function PermissionProvider({\n permissions,\n userType = \"\",\n adminTypes,\n adminBypass,\n loading = false,\n children,\n}: PermissionProviderProps) {\n const checker = useMemo(\n () =>\n new PermissionChecker({\n permissions,\n userType,\n adminTypes,\n adminBypass,\n }),\n [permissions, userType, adminTypes, adminBypass],\n );\n\n const value = useMemo(\n () => ({\n checker,\n permissions,\n userType,\n isAdmin: checker.admin,\n isLoading: loading,\n }),\n [checker, permissions, userType, loading],\n );\n\n return (\n <PermissionContext.Provider value={value}>\n {children}\n </PermissionContext.Provider>\n );\n}\n","import type { PermissionCheckResult, PermissionConfig } from \"./types\";\n\n/**\n * Pure TypeScript permission checker — no React dependency.\n *\n * @example\n * const checker = new PermissionChecker({\n * permissions: [\"users.read\", \"users.create\"],\n * userType: \"USER\",\n * });\n *\n * checker.hasPermission(\"users.read\"); // true\n * checker.hasPermission(\"users.delete\"); // false\n */\nexport class PermissionChecker {\n private readonly permissions: Set<string>;\n private readonly isAdmin: boolean;\n\n constructor(config: PermissionConfig) {\n this.permissions = new Set(config.permissions);\n const adminTypes = config.adminTypes ?? [\"ADMIN\"];\n const bypass = config.adminBypass ?? true;\n this.isAdmin = bypass && adminTypes.includes(config.userType ?? \"\");\n }\n\n /** Whether this checker was created with an admin-type user */\n get admin(): boolean {\n return this.isAdmin;\n }\n\n /** Check if user has a specific permission. Admins always return true. */\n hasPermission(permission: string): boolean {\n if (this.isAdmin) return true;\n return this.permissions.has(permission);\n }\n\n /** Check if user has ANY of the given permissions. Admins always return true. */\n hasAnyPermission(permissionList: string[]): boolean {\n if (this.isAdmin) return true;\n return permissionList.some((p) => this.permissions.has(p));\n }\n\n /** Check if user has ALL of the given permissions. Admins always return true. */\n hasAllPermissions(permissionList: string[]): boolean {\n if (this.isAdmin) return true;\n return permissionList.every((p) => this.permissions.has(p));\n }\n\n /** Detailed permission check with reason for the result */\n check(permission: string): PermissionCheckResult {\n if (this.isAdmin) {\n return { granted: true, reason: \"admin_bypass\" };\n }\n\n if (this.permissions.has(permission)) {\n return { granted: true, reason: \"permission_found\" };\n }\n\n return { granted: false, reason: \"permission_missing\" };\n }\n}\n","import { createContext } from \"react\";\nimport type { PermissionChecker } from \"../core/permission-checker\";\n\nexport interface PermissionContextValue {\n checker: PermissionChecker;\n permissions: string[];\n userType: string;\n isAdmin: boolean;\n isLoading: boolean;\n}\n\nexport const PermissionContext = createContext<PermissionContextValue | null>(\n null,\n);\n","import { useContext } from \"react\";\nimport type { PermissionCheckResult } from \"../core/types\";\nimport { PermissionContext } from \"./context\";\n\n/**\n * Hook for checking user permissions within a PermissionProvider.\n *\n * @example\n * const { hasPermission, isAdmin } = usePermissions();\n *\n * if (hasPermission(\"users.read\")) { ... }\n * if (isAdmin) { ... }\n */\nexport function usePermissions() {\n const context = useContext(PermissionContext);\n\n if (!context) {\n throw new Error(\n \"usePermissions must be used within a <PermissionProvider>\",\n );\n }\n\n const { checker, permissions, userType, isAdmin, isLoading } = context;\n\n return {\n /** Raw permission strings */\n permissions,\n /** User type string */\n userType,\n /** Whether the user is an admin type */\n isAdmin,\n /** Whether permission data is still loading */\n isLoading,\n /** Check if user has a specific permission */\n hasPermission: (permission: string): boolean =>\n checker.hasPermission(permission),\n /** Check if user has ANY of the given permissions */\n hasAnyPermission: (permissionList: string[]): boolean =>\n checker.hasAnyPermission(permissionList),\n /** Check if user has ALL of the given permissions */\n hasAllPermissions: (permissionList: string[]): boolean =>\n checker.hasAllPermissions(permissionList),\n /** Detailed permission check with reason */\n check: (permission: string): PermissionCheckResult =>\n checker.check(permission),\n };\n}\n","import type { ReactNode } from \"react\";\nimport { usePermissions } from \"./use-permissions\";\n\nexport interface PermissionGateProps {\n /** Required permission to show children */\n permission?: string;\n /** Show children if user has ANY of these permissions */\n anyOf?: string[];\n /** Show children if user has ALL of these permissions */\n allOf?: string[];\n /** Content to show when permission is granted */\n children: ReactNode;\n /** Content to show when permission is denied */\n fallback?: ReactNode;\n}\n\n/**\n * Conditionally renders content based on user permissions.\n * Admin users bypass all permission checks.\n *\n * @example\n * <PermissionGate permission=\"users.create\">\n * <CreateButton />\n * </PermissionGate>\n *\n * <PermissionGate anyOf={[\"users.create\", \"users.update\"]} fallback={<NoAccess />}>\n * <EditPanel />\n * </PermissionGate>\n */\nexport function PermissionGate({\n permission,\n anyOf,\n allOf,\n children,\n fallback = null,\n}: PermissionGateProps) {\n const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin } =\n usePermissions();\n\n if (isAdmin) {\n return <>{children}</>;\n }\n\n if (permission && !hasPermission(permission)) {\n return <>{fallback}</>;\n }\n\n if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {\n return <>{fallback}</>;\n }\n\n if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {\n return <>{fallback}</>;\n }\n\n return <>{children}</>;\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface PermissionProviderProps {
|
|
5
|
+
/** List of permission strings the user has */
|
|
6
|
+
permissions: string[];
|
|
7
|
+
/** User's type/role identifier */
|
|
8
|
+
userType?: string;
|
|
9
|
+
/** User types that bypass all permission checks (default: ["ADMIN"]) */
|
|
10
|
+
adminTypes?: string[];
|
|
11
|
+
/** Whether admin types bypass checks (default: true) */
|
|
12
|
+
adminBypass?: boolean;
|
|
13
|
+
/** External loading state (e.g., while fetching user data) */
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Provides permission context to the component tree.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <PermissionProvider
|
|
22
|
+
* permissions={user.permissions}
|
|
23
|
+
* userType={user.user_type}
|
|
24
|
+
* loading={isLoading}
|
|
25
|
+
* >
|
|
26
|
+
* <App />
|
|
27
|
+
* </PermissionProvider>
|
|
28
|
+
*/
|
|
29
|
+
declare function PermissionProvider({ permissions, userType, adminTypes, adminBypass, loading, children, }: PermissionProviderProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
/** Reason for a permission check result */
|
|
32
|
+
type PermissionCheckReason = "admin_bypass" | "permission_found" | "permission_missing" | "no_check";
|
|
33
|
+
/** Result of a permission check with detailed reason */
|
|
34
|
+
interface PermissionCheckResult {
|
|
35
|
+
granted: boolean;
|
|
36
|
+
reason: PermissionCheckReason;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hook for checking user permissions within a PermissionProvider.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const { hasPermission, isAdmin } = usePermissions();
|
|
44
|
+
*
|
|
45
|
+
* if (hasPermission("users.read")) { ... }
|
|
46
|
+
* if (isAdmin) { ... }
|
|
47
|
+
*/
|
|
48
|
+
declare function usePermissions(): {
|
|
49
|
+
/** Raw permission strings */
|
|
50
|
+
permissions: string[];
|
|
51
|
+
/** User type string */
|
|
52
|
+
userType: string;
|
|
53
|
+
/** Whether the user is an admin type */
|
|
54
|
+
isAdmin: boolean;
|
|
55
|
+
/** Whether permission data is still loading */
|
|
56
|
+
isLoading: boolean;
|
|
57
|
+
/** Check if user has a specific permission */
|
|
58
|
+
hasPermission: (permission: string) => boolean;
|
|
59
|
+
/** Check if user has ANY of the given permissions */
|
|
60
|
+
hasAnyPermission: (permissionList: string[]) => boolean;
|
|
61
|
+
/** Check if user has ALL of the given permissions */
|
|
62
|
+
hasAllPermissions: (permissionList: string[]) => boolean;
|
|
63
|
+
/** Detailed permission check with reason */
|
|
64
|
+
check: (permission: string) => PermissionCheckResult;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
interface PermissionGateProps {
|
|
68
|
+
/** Required permission to show children */
|
|
69
|
+
permission?: string;
|
|
70
|
+
/** Show children if user has ANY of these permissions */
|
|
71
|
+
anyOf?: string[];
|
|
72
|
+
/** Show children if user has ALL of these permissions */
|
|
73
|
+
allOf?: string[];
|
|
74
|
+
/** Content to show when permission is granted */
|
|
75
|
+
children: ReactNode;
|
|
76
|
+
/** Content to show when permission is denied */
|
|
77
|
+
fallback?: ReactNode;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Conditionally renders content based on user permissions.
|
|
81
|
+
* Admin users bypass all permission checks.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <PermissionGate permission="users.create">
|
|
85
|
+
* <CreateButton />
|
|
86
|
+
* </PermissionGate>
|
|
87
|
+
*
|
|
88
|
+
* <PermissionGate anyOf={["users.create", "users.update"]} fallback={<NoAccess />}>
|
|
89
|
+
* <EditPanel />
|
|
90
|
+
* </PermissionGate>
|
|
91
|
+
*/
|
|
92
|
+
declare function PermissionGate({ permission, anyOf, allOf, children, fallback, }: PermissionGateProps): react_jsx_runtime.JSX.Element;
|
|
93
|
+
|
|
94
|
+
export { PermissionGate, type PermissionGateProps, PermissionProvider, type PermissionProviderProps, usePermissions };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface PermissionProviderProps {
|
|
5
|
+
/** List of permission strings the user has */
|
|
6
|
+
permissions: string[];
|
|
7
|
+
/** User's type/role identifier */
|
|
8
|
+
userType?: string;
|
|
9
|
+
/** User types that bypass all permission checks (default: ["ADMIN"]) */
|
|
10
|
+
adminTypes?: string[];
|
|
11
|
+
/** Whether admin types bypass checks (default: true) */
|
|
12
|
+
adminBypass?: boolean;
|
|
13
|
+
/** External loading state (e.g., while fetching user data) */
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Provides permission context to the component tree.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <PermissionProvider
|
|
22
|
+
* permissions={user.permissions}
|
|
23
|
+
* userType={user.user_type}
|
|
24
|
+
* loading={isLoading}
|
|
25
|
+
* >
|
|
26
|
+
* <App />
|
|
27
|
+
* </PermissionProvider>
|
|
28
|
+
*/
|
|
29
|
+
declare function PermissionProvider({ permissions, userType, adminTypes, adminBypass, loading, children, }: PermissionProviderProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
/** Reason for a permission check result */
|
|
32
|
+
type PermissionCheckReason = "admin_bypass" | "permission_found" | "permission_missing" | "no_check";
|
|
33
|
+
/** Result of a permission check with detailed reason */
|
|
34
|
+
interface PermissionCheckResult {
|
|
35
|
+
granted: boolean;
|
|
36
|
+
reason: PermissionCheckReason;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hook for checking user permissions within a PermissionProvider.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const { hasPermission, isAdmin } = usePermissions();
|
|
44
|
+
*
|
|
45
|
+
* if (hasPermission("users.read")) { ... }
|
|
46
|
+
* if (isAdmin) { ... }
|
|
47
|
+
*/
|
|
48
|
+
declare function usePermissions(): {
|
|
49
|
+
/** Raw permission strings */
|
|
50
|
+
permissions: string[];
|
|
51
|
+
/** User type string */
|
|
52
|
+
userType: string;
|
|
53
|
+
/** Whether the user is an admin type */
|
|
54
|
+
isAdmin: boolean;
|
|
55
|
+
/** Whether permission data is still loading */
|
|
56
|
+
isLoading: boolean;
|
|
57
|
+
/** Check if user has a specific permission */
|
|
58
|
+
hasPermission: (permission: string) => boolean;
|
|
59
|
+
/** Check if user has ANY of the given permissions */
|
|
60
|
+
hasAnyPermission: (permissionList: string[]) => boolean;
|
|
61
|
+
/** Check if user has ALL of the given permissions */
|
|
62
|
+
hasAllPermissions: (permissionList: string[]) => boolean;
|
|
63
|
+
/** Detailed permission check with reason */
|
|
64
|
+
check: (permission: string) => PermissionCheckResult;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
interface PermissionGateProps {
|
|
68
|
+
/** Required permission to show children */
|
|
69
|
+
permission?: string;
|
|
70
|
+
/** Show children if user has ANY of these permissions */
|
|
71
|
+
anyOf?: string[];
|
|
72
|
+
/** Show children if user has ALL of these permissions */
|
|
73
|
+
allOf?: string[];
|
|
74
|
+
/** Content to show when permission is granted */
|
|
75
|
+
children: ReactNode;
|
|
76
|
+
/** Content to show when permission is denied */
|
|
77
|
+
fallback?: ReactNode;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Conditionally renders content based on user permissions.
|
|
81
|
+
* Admin users bypass all permission checks.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <PermissionGate permission="users.create">
|
|
85
|
+
* <CreateButton />
|
|
86
|
+
* </PermissionGate>
|
|
87
|
+
*
|
|
88
|
+
* <PermissionGate anyOf={["users.create", "users.update"]} fallback={<NoAccess />}>
|
|
89
|
+
* <EditPanel />
|
|
90
|
+
* </PermissionGate>
|
|
91
|
+
*/
|
|
92
|
+
declare function PermissionGate({ permission, anyOf, allOf, children, fallback, }: PermissionGateProps): react_jsx_runtime.JSX.Element;
|
|
93
|
+
|
|
94
|
+
export { PermissionGate, type PermissionGateProps, PermissionProvider, type PermissionProviderProps, usePermissions };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/react/provider.tsx
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
|
|
4
|
+
// src/core/permission-checker.ts
|
|
5
|
+
var PermissionChecker = class {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.permissions = new Set(config.permissions);
|
|
8
|
+
const adminTypes = config.adminTypes ?? ["ADMIN"];
|
|
9
|
+
const bypass = config.adminBypass ?? true;
|
|
10
|
+
this.isAdmin = bypass && adminTypes.includes(config.userType ?? "");
|
|
11
|
+
}
|
|
12
|
+
/** Whether this checker was created with an admin-type user */
|
|
13
|
+
get admin() {
|
|
14
|
+
return this.isAdmin;
|
|
15
|
+
}
|
|
16
|
+
/** Check if user has a specific permission. Admins always return true. */
|
|
17
|
+
hasPermission(permission) {
|
|
18
|
+
if (this.isAdmin) return true;
|
|
19
|
+
return this.permissions.has(permission);
|
|
20
|
+
}
|
|
21
|
+
/** Check if user has ANY of the given permissions. Admins always return true. */
|
|
22
|
+
hasAnyPermission(permissionList) {
|
|
23
|
+
if (this.isAdmin) return true;
|
|
24
|
+
return permissionList.some((p) => this.permissions.has(p));
|
|
25
|
+
}
|
|
26
|
+
/** Check if user has ALL of the given permissions. Admins always return true. */
|
|
27
|
+
hasAllPermissions(permissionList) {
|
|
28
|
+
if (this.isAdmin) return true;
|
|
29
|
+
return permissionList.every((p) => this.permissions.has(p));
|
|
30
|
+
}
|
|
31
|
+
/** Detailed permission check with reason for the result */
|
|
32
|
+
check(permission) {
|
|
33
|
+
if (this.isAdmin) {
|
|
34
|
+
return { granted: true, reason: "admin_bypass" };
|
|
35
|
+
}
|
|
36
|
+
if (this.permissions.has(permission)) {
|
|
37
|
+
return { granted: true, reason: "permission_found" };
|
|
38
|
+
}
|
|
39
|
+
return { granted: false, reason: "permission_missing" };
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/react/context.ts
|
|
44
|
+
import { createContext } from "react";
|
|
45
|
+
var PermissionContext = createContext(
|
|
46
|
+
null
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// src/react/provider.tsx
|
|
50
|
+
import { jsx } from "react/jsx-runtime";
|
|
51
|
+
function PermissionProvider({
|
|
52
|
+
permissions,
|
|
53
|
+
userType = "",
|
|
54
|
+
adminTypes,
|
|
55
|
+
adminBypass,
|
|
56
|
+
loading = false,
|
|
57
|
+
children
|
|
58
|
+
}) {
|
|
59
|
+
const checker = useMemo(
|
|
60
|
+
() => new PermissionChecker({
|
|
61
|
+
permissions,
|
|
62
|
+
userType,
|
|
63
|
+
adminTypes,
|
|
64
|
+
adminBypass
|
|
65
|
+
}),
|
|
66
|
+
[permissions, userType, adminTypes, adminBypass]
|
|
67
|
+
);
|
|
68
|
+
const value = useMemo(
|
|
69
|
+
() => ({
|
|
70
|
+
checker,
|
|
71
|
+
permissions,
|
|
72
|
+
userType,
|
|
73
|
+
isAdmin: checker.admin,
|
|
74
|
+
isLoading: loading
|
|
75
|
+
}),
|
|
76
|
+
[checker, permissions, userType, loading]
|
|
77
|
+
);
|
|
78
|
+
return /* @__PURE__ */ jsx(PermissionContext.Provider, { value, children });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/react/use-permissions.ts
|
|
82
|
+
import { useContext } from "react";
|
|
83
|
+
function usePermissions() {
|
|
84
|
+
const context = useContext(PermissionContext);
|
|
85
|
+
if (!context) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"usePermissions must be used within a <PermissionProvider>"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const { checker, permissions, userType, isAdmin, isLoading } = context;
|
|
91
|
+
return {
|
|
92
|
+
/** Raw permission strings */
|
|
93
|
+
permissions,
|
|
94
|
+
/** User type string */
|
|
95
|
+
userType,
|
|
96
|
+
/** Whether the user is an admin type */
|
|
97
|
+
isAdmin,
|
|
98
|
+
/** Whether permission data is still loading */
|
|
99
|
+
isLoading,
|
|
100
|
+
/** Check if user has a specific permission */
|
|
101
|
+
hasPermission: (permission) => checker.hasPermission(permission),
|
|
102
|
+
/** Check if user has ANY of the given permissions */
|
|
103
|
+
hasAnyPermission: (permissionList) => checker.hasAnyPermission(permissionList),
|
|
104
|
+
/** Check if user has ALL of the given permissions */
|
|
105
|
+
hasAllPermissions: (permissionList) => checker.hasAllPermissions(permissionList),
|
|
106
|
+
/** Detailed permission check with reason */
|
|
107
|
+
check: (permission) => checker.check(permission)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/react/permission-gate.tsx
|
|
112
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
113
|
+
function PermissionGate({
|
|
114
|
+
permission,
|
|
115
|
+
anyOf,
|
|
116
|
+
allOf,
|
|
117
|
+
children,
|
|
118
|
+
fallback = null
|
|
119
|
+
}) {
|
|
120
|
+
const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin } = usePermissions();
|
|
121
|
+
if (isAdmin) {
|
|
122
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
123
|
+
}
|
|
124
|
+
if (permission && !hasPermission(permission)) {
|
|
125
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
|
|
126
|
+
}
|
|
127
|
+
if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {
|
|
128
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
|
|
129
|
+
}
|
|
130
|
+
if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {
|
|
131
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
|
|
132
|
+
}
|
|
133
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
PermissionGate,
|
|
137
|
+
PermissionProvider,
|
|
138
|
+
usePermissions
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/provider.tsx","../../src/core/permission-checker.ts","../../src/react/context.ts","../../src/react/use-permissions.ts","../../src/react/permission-gate.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { PermissionChecker } from \"../core/permission-checker\";\nimport { PermissionContext } from \"./context\";\n\nexport interface PermissionProviderProps {\n /** List of permission strings the user has */\n permissions: string[];\n /** User's type/role identifier */\n userType?: string;\n /** User types that bypass all permission checks (default: [\"ADMIN\"]) */\n adminTypes?: string[];\n /** Whether admin types bypass checks (default: true) */\n adminBypass?: boolean;\n /** External loading state (e.g., while fetching user data) */\n loading?: boolean;\n children: ReactNode;\n}\n\n/**\n * Provides permission context to the component tree.\n *\n * @example\n * <PermissionProvider\n * permissions={user.permissions}\n * userType={user.user_type}\n * loading={isLoading}\n * >\n * <App />\n * </PermissionProvider>\n */\nexport function PermissionProvider({\n permissions,\n userType = \"\",\n adminTypes,\n adminBypass,\n loading = false,\n children,\n}: PermissionProviderProps) {\n const checker = useMemo(\n () =>\n new PermissionChecker({\n permissions,\n userType,\n adminTypes,\n adminBypass,\n }),\n [permissions, userType, adminTypes, adminBypass],\n );\n\n const value = useMemo(\n () => ({\n checker,\n permissions,\n userType,\n isAdmin: checker.admin,\n isLoading: loading,\n }),\n [checker, permissions, userType, loading],\n );\n\n return (\n <PermissionContext.Provider value={value}>\n {children}\n </PermissionContext.Provider>\n );\n}\n","import type { PermissionCheckResult, PermissionConfig } from \"./types\";\n\n/**\n * Pure TypeScript permission checker — no React dependency.\n *\n * @example\n * const checker = new PermissionChecker({\n * permissions: [\"users.read\", \"users.create\"],\n * userType: \"USER\",\n * });\n *\n * checker.hasPermission(\"users.read\"); // true\n * checker.hasPermission(\"users.delete\"); // false\n */\nexport class PermissionChecker {\n private readonly permissions: Set<string>;\n private readonly isAdmin: boolean;\n\n constructor(config: PermissionConfig) {\n this.permissions = new Set(config.permissions);\n const adminTypes = config.adminTypes ?? [\"ADMIN\"];\n const bypass = config.adminBypass ?? true;\n this.isAdmin = bypass && adminTypes.includes(config.userType ?? \"\");\n }\n\n /** Whether this checker was created with an admin-type user */\n get admin(): boolean {\n return this.isAdmin;\n }\n\n /** Check if user has a specific permission. Admins always return true. */\n hasPermission(permission: string): boolean {\n if (this.isAdmin) return true;\n return this.permissions.has(permission);\n }\n\n /** Check if user has ANY of the given permissions. Admins always return true. */\n hasAnyPermission(permissionList: string[]): boolean {\n if (this.isAdmin) return true;\n return permissionList.some((p) => this.permissions.has(p));\n }\n\n /** Check if user has ALL of the given permissions. Admins always return true. */\n hasAllPermissions(permissionList: string[]): boolean {\n if (this.isAdmin) return true;\n return permissionList.every((p) => this.permissions.has(p));\n }\n\n /** Detailed permission check with reason for the result */\n check(permission: string): PermissionCheckResult {\n if (this.isAdmin) {\n return { granted: true, reason: \"admin_bypass\" };\n }\n\n if (this.permissions.has(permission)) {\n return { granted: true, reason: \"permission_found\" };\n }\n\n return { granted: false, reason: \"permission_missing\" };\n }\n}\n","import { createContext } from \"react\";\nimport type { PermissionChecker } from \"../core/permission-checker\";\n\nexport interface PermissionContextValue {\n checker: PermissionChecker;\n permissions: string[];\n userType: string;\n isAdmin: boolean;\n isLoading: boolean;\n}\n\nexport const PermissionContext = createContext<PermissionContextValue | null>(\n null,\n);\n","import { useContext } from \"react\";\nimport type { PermissionCheckResult } from \"../core/types\";\nimport { PermissionContext } from \"./context\";\n\n/**\n * Hook for checking user permissions within a PermissionProvider.\n *\n * @example\n * const { hasPermission, isAdmin } = usePermissions();\n *\n * if (hasPermission(\"users.read\")) { ... }\n * if (isAdmin) { ... }\n */\nexport function usePermissions() {\n const context = useContext(PermissionContext);\n\n if (!context) {\n throw new Error(\n \"usePermissions must be used within a <PermissionProvider>\",\n );\n }\n\n const { checker, permissions, userType, isAdmin, isLoading } = context;\n\n return {\n /** Raw permission strings */\n permissions,\n /** User type string */\n userType,\n /** Whether the user is an admin type */\n isAdmin,\n /** Whether permission data is still loading */\n isLoading,\n /** Check if user has a specific permission */\n hasPermission: (permission: string): boolean =>\n checker.hasPermission(permission),\n /** Check if user has ANY of the given permissions */\n hasAnyPermission: (permissionList: string[]): boolean =>\n checker.hasAnyPermission(permissionList),\n /** Check if user has ALL of the given permissions */\n hasAllPermissions: (permissionList: string[]): boolean =>\n checker.hasAllPermissions(permissionList),\n /** Detailed permission check with reason */\n check: (permission: string): PermissionCheckResult =>\n checker.check(permission),\n };\n}\n","import type { ReactNode } from \"react\";\nimport { usePermissions } from \"./use-permissions\";\n\nexport interface PermissionGateProps {\n /** Required permission to show children */\n permission?: string;\n /** Show children if user has ANY of these permissions */\n anyOf?: string[];\n /** Show children if user has ALL of these permissions */\n allOf?: string[];\n /** Content to show when permission is granted */\n children: ReactNode;\n /** Content to show when permission is denied */\n fallback?: ReactNode;\n}\n\n/**\n * Conditionally renders content based on user permissions.\n * Admin users bypass all permission checks.\n *\n * @example\n * <PermissionGate permission=\"users.create\">\n * <CreateButton />\n * </PermissionGate>\n *\n * <PermissionGate anyOf={[\"users.create\", \"users.update\"]} fallback={<NoAccess />}>\n * <EditPanel />\n * </PermissionGate>\n */\nexport function PermissionGate({\n permission,\n anyOf,\n allOf,\n children,\n fallback = null,\n}: PermissionGateProps) {\n const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin } =\n usePermissions();\n\n if (isAdmin) {\n return <>{children}</>;\n }\n\n if (permission && !hasPermission(permission)) {\n return <>{fallback}</>;\n }\n\n if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {\n return <>{fallback}</>;\n }\n\n if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {\n return <>{fallback}</>;\n }\n\n return <>{children}</>;\n}\n"],"mappings":"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","names":["jsx"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
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/react-router/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ProtectedRoute: () => ProtectedRoute
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/react-router/protected-route.tsx
|
|
28
|
+
var import_react_router_dom = require("react-router-dom");
|
|
29
|
+
|
|
30
|
+
// src/react/use-permissions.ts
|
|
31
|
+
var import_react2 = require("react");
|
|
32
|
+
|
|
33
|
+
// src/react/context.ts
|
|
34
|
+
var import_react = require("react");
|
|
35
|
+
var PermissionContext = (0, import_react.createContext)(
|
|
36
|
+
null
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// src/react/use-permissions.ts
|
|
40
|
+
function usePermissions() {
|
|
41
|
+
const context = (0, import_react2.useContext)(PermissionContext);
|
|
42
|
+
if (!context) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"usePermissions must be used within a <PermissionProvider>"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const { checker, permissions, userType, isAdmin, isLoading } = context;
|
|
48
|
+
return {
|
|
49
|
+
/** Raw permission strings */
|
|
50
|
+
permissions,
|
|
51
|
+
/** User type string */
|
|
52
|
+
userType,
|
|
53
|
+
/** Whether the user is an admin type */
|
|
54
|
+
isAdmin,
|
|
55
|
+
/** Whether permission data is still loading */
|
|
56
|
+
isLoading,
|
|
57
|
+
/** Check if user has a specific permission */
|
|
58
|
+
hasPermission: (permission) => checker.hasPermission(permission),
|
|
59
|
+
/** Check if user has ANY of the given permissions */
|
|
60
|
+
hasAnyPermission: (permissionList) => checker.hasAnyPermission(permissionList),
|
|
61
|
+
/** Check if user has ALL of the given permissions */
|
|
62
|
+
hasAllPermissions: (permissionList) => checker.hasAllPermissions(permissionList),
|
|
63
|
+
/** Detailed permission check with reason */
|
|
64
|
+
check: (permission) => checker.check(permission)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/react-router/protected-route.tsx
|
|
69
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
70
|
+
function ProtectedRoute({
|
|
71
|
+
permission,
|
|
72
|
+
anyOf,
|
|
73
|
+
allOf,
|
|
74
|
+
children,
|
|
75
|
+
redirectTo = "/",
|
|
76
|
+
loadingComponent = null
|
|
77
|
+
}) {
|
|
78
|
+
const location = (0, import_react_router_dom.useLocation)();
|
|
79
|
+
const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin, isLoading } = usePermissions();
|
|
80
|
+
if (isLoading) {
|
|
81
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent });
|
|
82
|
+
}
|
|
83
|
+
if (isAdmin) {
|
|
84
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
85
|
+
}
|
|
86
|
+
if (permission && !hasPermission(permission)) {
|
|
87
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_router_dom.Navigate, { to: redirectTo, state: { from: location }, replace: true });
|
|
88
|
+
}
|
|
89
|
+
if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {
|
|
90
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_router_dom.Navigate, { to: redirectTo, state: { from: location }, replace: true });
|
|
91
|
+
}
|
|
92
|
+
if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {
|
|
93
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_router_dom.Navigate, { to: redirectTo, state: { from: location }, replace: true });
|
|
94
|
+
}
|
|
95
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
96
|
+
}
|
|
97
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
98
|
+
0 && (module.exports = {
|
|
99
|
+
ProtectedRoute
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react-router/index.ts","../../src/react-router/protected-route.tsx","../../src/react/use-permissions.ts","../../src/react/context.ts"],"sourcesContent":["export { ProtectedRoute } from \"./protected-route\";\nexport type { ProtectedRouteProps } from \"./protected-route\";\n","import type { ReactNode } from \"react\";\nimport { Navigate, useLocation } from \"react-router-dom\";\nimport { usePermissions } from \"../react/use-permissions\";\n\nexport interface ProtectedRouteProps {\n /** Required permission to access route */\n permission?: string;\n /** Allow access if user has ANY of these permissions */\n anyOf?: string[];\n /** Allow access if user has ALL of these permissions */\n allOf?: string[];\n /** Route content */\n children: ReactNode;\n /** Redirect path when access is denied (default: \"/\") */\n redirectTo?: string;\n /** Component to show while loading (default: null) */\n loadingComponent?: ReactNode;\n}\n\n/**\n * Route wrapper that checks permissions before rendering.\n * Redirects when permission check fails. Admin users bypass all checks.\n *\n * @example\n * <Route path=\"/users\" element={\n * <ProtectedRoute permission=\"users.read\">\n * <UsersPage />\n * </ProtectedRoute>\n * } />\n */\nexport function ProtectedRoute({\n permission,\n anyOf,\n allOf,\n children,\n redirectTo = \"/\",\n loadingComponent = null,\n}: ProtectedRouteProps) {\n const location = useLocation();\n const { hasPermission, hasAnyPermission, hasAllPermissions, isAdmin, isLoading } =\n usePermissions();\n\n if (isLoading) {\n return <>{loadingComponent}</>;\n }\n\n if (isAdmin) {\n return <>{children}</>;\n }\n\n if (permission && !hasPermission(permission)) {\n return <Navigate to={redirectTo} state={{ from: location }} replace />;\n }\n\n if (anyOf && anyOf.length > 0 && !hasAnyPermission(anyOf)) {\n return <Navigate to={redirectTo} state={{ from: location }} replace />;\n }\n\n if (allOf && allOf.length > 0 && !hasAllPermissions(allOf)) {\n return <Navigate to={redirectTo} state={{ from: location }} replace />;\n }\n\n return <>{children}</>;\n}\n","import { useContext } from \"react\";\nimport type { PermissionCheckResult } from \"../core/types\";\nimport { PermissionContext } from \"./context\";\n\n/**\n * Hook for checking user permissions within a PermissionProvider.\n *\n * @example\n * const { hasPermission, isAdmin } = usePermissions();\n *\n * if (hasPermission(\"users.read\")) { ... }\n * if (isAdmin) { ... }\n */\nexport function usePermissions() {\n const context = useContext(PermissionContext);\n\n if (!context) {\n throw new Error(\n \"usePermissions must be used within a <PermissionProvider>\",\n );\n }\n\n const { checker, permissions, userType, isAdmin, isLoading } = context;\n\n return {\n /** Raw permission strings */\n permissions,\n /** User type string */\n userType,\n /** Whether the user is an admin type */\n isAdmin,\n /** Whether permission data is still loading */\n isLoading,\n /** Check if user has a specific permission */\n hasPermission: (permission: string): boolean =>\n checker.hasPermission(permission),\n /** Check if user has ANY of the given permissions */\n hasAnyPermission: (permissionList: string[]): boolean =>\n checker.hasAnyPermission(permissionList),\n /** Check if user has ALL of the given permissions */\n hasAllPermissions: (permissionList: string[]): boolean =>\n checker.hasAllPermissions(permissionList),\n /** Detailed permission check with reason */\n check: (permission: string): PermissionCheckResult =>\n checker.check(permission),\n };\n}\n","import { createContext } from \"react\";\nimport type { PermissionChecker } from \"../core/permission-checker\";\n\nexport interface PermissionContextValue {\n checker: PermissionChecker;\n permissions: string[];\n userType: string;\n isAdmin: boolean;\n isLoading: boolean;\n}\n\nexport const PermissionContext = createContext<PermissionContextValue | null>(\n null,\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,8BAAsC;;;ACDtC,IAAAA,gBAA2B;;;ACA3B,mBAA8B;AAWvB,IAAM,wBAAoB;AAAA,EAC/B;AACF;;;ADAO,SAAS,iBAAiB;AAC/B,QAAM,cAAU,0BAAW,iBAAiB;AAE5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,aAAa,UAAU,SAAS,UAAU,IAAI;AAE/D,SAAO;AAAA;AAAA,IAEL;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA;AAAA;AAAA,IAEA,eAAe,CAAC,eACd,QAAQ,cAAc,UAAU;AAAA;AAAA,IAElC,kBAAkB,CAAC,mBACjB,QAAQ,iBAAiB,cAAc;AAAA;AAAA,IAEzC,mBAAmB,CAAC,mBAClB,QAAQ,kBAAkB,cAAc;AAAA;AAAA,IAE1C,OAAO,CAAC,eACN,QAAQ,MAAM,UAAU;AAAA,EAC5B;AACF;;;ADHW;AAbJ,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,mBAAmB;AACrB,GAAwB;AACtB,QAAM,eAAW,qCAAY;AAC7B,QAAM,EAAE,eAAe,kBAAkB,mBAAmB,SAAS,UAAU,IAC7E,eAAe;AAEjB,MAAI,WAAW;AACb,WAAO,2EAAG,4BAAiB;AAAA,EAC7B;AAEA,MAAI,SAAS;AACX,WAAO,2EAAG,UAAS;AAAA,EACrB;AAEA,MAAI,cAAc,CAAC,cAAc,UAAU,GAAG;AAC5C,WAAO,4CAAC,oCAAS,IAAI,YAAY,OAAO,EAAE,MAAM,SAAS,GAAG,SAAO,MAAC;AAAA,EACtE;AAEA,MAAI,SAAS,MAAM,SAAS,KAAK,CAAC,iBAAiB,KAAK,GAAG;AACzD,WAAO,4CAAC,oCAAS,IAAI,YAAY,OAAO,EAAE,MAAM,SAAS,GAAG,SAAO,MAAC;AAAA,EACtE;AAEA,MAAI,SAAS,MAAM,SAAS,KAAK,CAAC,kBAAkB,KAAK,GAAG;AAC1D,WAAO,4CAAC,oCAAS,IAAI,YAAY,OAAO,EAAE,MAAM,SAAS,GAAG,SAAO,MAAC;AAAA,EACtE;AAEA,SAAO,2EAAG,UAAS;AACrB;","names":["import_react"]}
|