com-angel-authorization 1.0.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.
@@ -0,0 +1,156 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react/index.ts
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ Can: () => Can,
25
+ PermissionProvider: () => PermissionProvider,
26
+ PermissionStore: () => import_core2.PermissionStore,
27
+ createPermissionStore: () => import_core2.createPermissionStore,
28
+ useHasPermission: () => useHasPermission,
29
+ useHasRole: () => useHasRole,
30
+ usePermission: () => usePermission
31
+ });
32
+ module.exports = __toCommonJS(react_exports);
33
+
34
+ // src/react/context.tsx
35
+ var import_react = require("react");
36
+ var import_core = require("../core");
37
+ var PermissionContext = (0, import_react.createContext)(null);
38
+ function PermissionProvider({
39
+ children,
40
+ store: externalStore,
41
+ initialState
42
+ }) {
43
+ const store = (0, import_react.useMemo)(
44
+ () => externalStore ?? (0, import_core.createPermissionStore)(initialState),
45
+ // store identity is intentional; initialState only applies on first create
46
+ // eslint-disable-next-line react-hooks/exhaustive-deps
47
+ [externalStore]
48
+ );
49
+ return (0, import_react.createElement)(PermissionContext.Provider, { value: store }, children);
50
+ }
51
+ function useStore() {
52
+ const store = (0, import_react.useContext)(PermissionContext);
53
+ if (!store) {
54
+ throw new Error(
55
+ "[com-angel-authorization/react] usePermission* hooks must be used within <PermissionProvider>."
56
+ );
57
+ }
58
+ return store;
59
+ }
60
+ function usePermissionSnapshot(store) {
61
+ const [state, setState] = (0, import_react.useState)(() => store.getState());
62
+ (0, import_react.useEffect)(() => {
63
+ setState(store.getState());
64
+ return store.subscribe(setState);
65
+ }, [store]);
66
+ return state;
67
+ }
68
+ function usePermission() {
69
+ const store = useStore();
70
+ const state = usePermissionSnapshot(store);
71
+ const hasPermission = (0, import_react.useCallback)(
72
+ (codes, options) => store.hasPermission(codes, options),
73
+ [store]
74
+ );
75
+ const hasRole = (0, import_react.useCallback)(
76
+ (codes, options) => store.hasRole(codes, options),
77
+ [store]
78
+ );
79
+ const can = (0, import_react.useCallback)(
80
+ (codes, options) => store.can(codes, options),
81
+ [store]
82
+ );
83
+ return {
84
+ permissions: state.permissions,
85
+ roles: state.roles,
86
+ isSuperAdmin: Boolean(state.isSuperAdmin),
87
+ hasPermission,
88
+ hasRole,
89
+ can,
90
+ setPermissions: store.setPermissions.bind(store),
91
+ setRoles: store.setRoles.bind(store),
92
+ setSuperAdmin: store.setSuperAdmin.bind(store),
93
+ hydrate: store.hydrate.bind(store),
94
+ clear: store.clear.bind(store),
95
+ store
96
+ };
97
+ }
98
+ function useHasPermission(codes, options) {
99
+ const { hasPermission, permissions, isSuperAdmin } = usePermission();
100
+ void permissions;
101
+ void isSuperAdmin;
102
+ return hasPermission(codes, options);
103
+ }
104
+ function useHasRole(codes, options) {
105
+ const { hasRole, roles, isSuperAdmin } = usePermission();
106
+ void roles;
107
+ void isSuperAdmin;
108
+ return hasRole(codes, options);
109
+ }
110
+
111
+ // src/react/Can.tsx
112
+ var import_jsx_runtime = require("react/jsx-runtime");
113
+ function evaluateAccess(hasPermission, hasRole, props) {
114
+ const { permission, role, mode, combine = "and" } = props;
115
+ const options = { mode };
116
+ const hasPermCheck = permission !== void 0;
117
+ const hasRoleCheck = role !== void 0;
118
+ if (!hasPermCheck && !hasRoleCheck) return true;
119
+ const permOk = hasPermCheck ? hasPermission(permission, options) : true;
120
+ const roleOk = hasRoleCheck ? hasRole(role, options) : true;
121
+ if (hasPermCheck && hasRoleCheck) {
122
+ return combine === "or" ? permOk || roleOk : permOk && roleOk;
123
+ }
124
+ return hasPermCheck ? permOk : roleOk;
125
+ }
126
+ function Can({
127
+ permission,
128
+ role,
129
+ mode,
130
+ combine,
131
+ children,
132
+ fallback = null
133
+ }) {
134
+ const { hasPermission, hasRole } = usePermission();
135
+ const allowed = evaluateAccess(hasPermission, hasRole, {
136
+ permission,
137
+ role,
138
+ mode,
139
+ combine
140
+ });
141
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: allowed ? children : fallback });
142
+ }
143
+
144
+ // src/react/index.ts
145
+ var import_core2 = require("../core");
146
+ // Annotate the CommonJS export names for ESM import in node:
147
+ 0 && (module.exports = {
148
+ Can,
149
+ PermissionProvider,
150
+ PermissionStore,
151
+ createPermissionStore,
152
+ useHasPermission,
153
+ useHasRole,
154
+ usePermission
155
+ });
156
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/context.tsx","../../src/react/Can.tsx"],"sourcesContent":["export { PermissionProvider, usePermission, useHasPermission, useHasRole } from './context';\nexport type { PermissionProviderProps, UsePermissionResult } from './context';\n\nexport { Can } from './Can';\nexport type { CanProps } from './Can';\n\nexport type {\n CheckOptions,\n MatchMode,\n PermissionChecker,\n PermissionCode,\n PermissionListener,\n PermissionState,\n RoleCode,\n} from '../core';\n\nexport { PermissionStore, createPermissionStore } from '../core';\n","import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\nimport {\n createPermissionStore,\n type CheckOptions,\n type PermissionCode,\n type PermissionState,\n type PermissionStore,\n type RoleCode,\n} from '../core';\n\nconst PermissionContext = createContext<PermissionStore | null>(null);\n\nexport interface PermissionProviderProps {\n children: ReactNode;\n /** Pass an existing store to share across trees, or omit to create one. */\n store?: PermissionStore;\n /** Initial state when the provider creates its own store. */\n initialState?: Partial<PermissionState>;\n}\n\nexport function PermissionProvider({\n children,\n store: externalStore,\n initialState,\n}: PermissionProviderProps) {\n const store = useMemo(\n () => externalStore ?? createPermissionStore(initialState),\n // store identity is intentional; initialState only applies on first create\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [externalStore],\n );\n\n return createElement(PermissionContext.Provider, { value: store }, children);\n}\n\nfunction useStore(): PermissionStore {\n const store = useContext(PermissionContext);\n if (!store) {\n throw new Error(\n '[com-angel-authorization/react] usePermission* hooks must be used within <PermissionProvider>.',\n );\n }\n return store;\n}\n\nfunction usePermissionSnapshot(store: PermissionStore): PermissionState {\n const [state, setState] = useState(() => store.getState());\n\n useEffect(() => {\n setState(store.getState());\n return store.subscribe(setState);\n }, [store]);\n\n return state;\n}\n\nexport interface UsePermissionResult {\n permissions: PermissionCode[];\n roles: RoleCode[];\n isSuperAdmin: boolean;\n hasPermission: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;\n hasRole: (codes: RoleCode | RoleCode[], options?: CheckOptions) => boolean;\n can: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;\n setPermissions: (permissions: PermissionCode[]) => void;\n setRoles: (roles: RoleCode[]) => void;\n setSuperAdmin: (isSuperAdmin: boolean) => void;\n hydrate: (payload: Partial<PermissionState>) => void;\n clear: () => void;\n store: PermissionStore;\n}\n\nexport function usePermission(): UsePermissionResult {\n const store = useStore();\n const state = usePermissionSnapshot(store);\n\n const hasPermission = useCallback(\n (codes: PermissionCode | PermissionCode[], options?: CheckOptions) =>\n store.hasPermission(codes, options),\n [store],\n );\n\n const hasRole = useCallback(\n (codes: RoleCode | RoleCode[], options?: CheckOptions) => store.hasRole(codes, options),\n [store],\n );\n\n const can = useCallback(\n (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => store.can(codes, options),\n [store],\n );\n\n return {\n permissions: state.permissions,\n roles: state.roles,\n isSuperAdmin: Boolean(state.isSuperAdmin),\n hasPermission,\n hasRole,\n can,\n setPermissions: store.setPermissions.bind(store),\n setRoles: store.setRoles.bind(store),\n setSuperAdmin: store.setSuperAdmin.bind(store),\n hydrate: store.hydrate.bind(store),\n clear: store.clear.bind(store),\n store,\n };\n}\n\n/** Returns whether the current user has the given permission(s). */\nexport function useHasPermission(\n codes: PermissionCode | PermissionCode[],\n options?: CheckOptions,\n): boolean {\n const { hasPermission, permissions, isSuperAdmin } = usePermission();\n // re-compute when permission list / super-admin changes\n void permissions;\n void isSuperAdmin;\n return hasPermission(codes, options);\n}\n\n/** Returns whether the current user has the given role(s). */\nexport function useHasRole(\n codes: RoleCode | RoleCode[],\n options?: CheckOptions,\n): boolean {\n const { hasRole, roles, isSuperAdmin } = usePermission();\n void roles;\n void isSuperAdmin;\n return hasRole(codes, options);\n}\n","import type { ReactNode } from 'react';\nimport type { CheckOptions, PermissionCode, RoleCode } from '../core';\nimport { usePermission } from './context';\n\nexport interface CanProps {\n /** Permission codes to check. */\n permission?: PermissionCode | PermissionCode[];\n /** Role codes to check. */\n role?: RoleCode | RoleCode[];\n /**\n * `any` — pass when at least one code matches (default)\n * `all` — pass only when every code matches\n */\n mode?: CheckOptions['mode'];\n /**\n * How permission and role conditions combine when both are provided.\n * Default: `and` (must satisfy both).\n */\n combine?: 'and' | 'or';\n children: ReactNode;\n /** Fallback when access is denied. */\n fallback?: ReactNode;\n}\n\nfunction evaluateAccess(\n hasPermission: (c: PermissionCode | PermissionCode[], o?: CheckOptions) => boolean,\n hasRole: (c: RoleCode | RoleCode[], o?: CheckOptions) => boolean,\n props: Pick<CanProps, 'permission' | 'role' | 'mode' | 'combine'>,\n): boolean {\n const { permission, role, mode, combine = 'and' } = props;\n const options: CheckOptions = { mode };\n\n const hasPermCheck = permission !== undefined;\n const hasRoleCheck = role !== undefined;\n\n if (!hasPermCheck && !hasRoleCheck) return true;\n\n const permOk = hasPermCheck ? hasPermission(permission!, options) : true;\n const roleOk = hasRoleCheck ? hasRole(role!, options) : true;\n\n if (hasPermCheck && hasRoleCheck) {\n return combine === 'or' ? permOk || roleOk : permOk && roleOk;\n }\n\n return hasPermCheck ? permOk : roleOk;\n}\n\n/**\n * Conditionally render children based on permission / role.\n *\n * @example\n * <Can permission=\"user:edit\">\n * <button>Edit</button>\n * </Can>\n *\n * <Can permission={['user:edit', 'user:delete']} mode=\"all\" fallback={null}>\n * <AdminPanel />\n * </Can>\n */\nexport function Can({\n permission,\n role,\n mode,\n combine,\n children,\n fallback = null,\n}: CanProps) {\n const { hasPermission, hasRole } = usePermission();\n const allowed = evaluateAccess(hasPermission, hasRole, {\n permission,\n role,\n mode,\n combine,\n });\n\n return <>{allowed ? children : fallback}</>;\n}\n"],"mappings":"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","names":["import_core"]}
@@ -0,0 +1,119 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type PermissionCode = string;
5
+ type RoleCode = string;
6
+ interface PermissionState {
7
+ /** Permission codes owned by the current user */
8
+ permissions: PermissionCode[];
9
+ /** Roles owned by the current user */
10
+ roles: RoleCode[];
11
+ /** Super admin bypasses all checks when true */
12
+ isSuperAdmin?: boolean;
13
+ }
14
+ type MatchMode = 'all' | 'any';
15
+ interface CheckOptions {
16
+ /**
17
+ * `any` — pass when at least one code matches (default)
18
+ * `all` — pass only when every code matches
19
+ */
20
+ mode?: MatchMode;
21
+ }
22
+ type PermissionListener = (state: PermissionState) => void;
23
+ interface PermissionChecker {
24
+ hasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
25
+ hasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
26
+ can(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
27
+ getState(): PermissionState;
28
+ }
29
+
30
+ /**
31
+ * Mutable permission store shared by React / Vue adapters.
32
+ * Framework-agnostic — safe to use in Node, browser, or any UI lib.
33
+ */
34
+ declare class PermissionStore implements PermissionChecker {
35
+ private state;
36
+ private permissionSet;
37
+ private roleSet;
38
+ private listeners;
39
+ constructor(initial?: Partial<PermissionState>);
40
+ getState(): PermissionState;
41
+ setState(next: Partial<PermissionState>): void;
42
+ setPermissions(permissions: PermissionCode[]): void;
43
+ setRoles(roles: RoleCode[]): void;
44
+ setSuperAdmin(isSuperAdmin: boolean): void;
45
+ /** Replace permissions, roles and super-admin flag in one shot (typical after login). */
46
+ hydrate(payload: Partial<PermissionState>): void;
47
+ /** Clear all authz data (typical after logout). */
48
+ clear(): void;
49
+ hasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
50
+ hasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
51
+ /** Alias of `hasPermission` for template-friendly APIs. */
52
+ can(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
53
+ subscribe(listener: PermissionListener): () => void;
54
+ private emit;
55
+ }
56
+ declare function createPermissionStore(initial?: Partial<PermissionState>): PermissionStore;
57
+
58
+ interface PermissionProviderProps {
59
+ children: ReactNode;
60
+ /** Pass an existing store to share across trees, or omit to create one. */
61
+ store?: PermissionStore;
62
+ /** Initial state when the provider creates its own store. */
63
+ initialState?: Partial<PermissionState>;
64
+ }
65
+ declare function PermissionProvider({ children, store: externalStore, initialState, }: PermissionProviderProps): react.FunctionComponentElement<react.ProviderProps<PermissionStore | null>>;
66
+ interface UsePermissionResult {
67
+ permissions: PermissionCode[];
68
+ roles: RoleCode[];
69
+ isSuperAdmin: boolean;
70
+ hasPermission: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;
71
+ hasRole: (codes: RoleCode | RoleCode[], options?: CheckOptions) => boolean;
72
+ can: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;
73
+ setPermissions: (permissions: PermissionCode[]) => void;
74
+ setRoles: (roles: RoleCode[]) => void;
75
+ setSuperAdmin: (isSuperAdmin: boolean) => void;
76
+ hydrate: (payload: Partial<PermissionState>) => void;
77
+ clear: () => void;
78
+ store: PermissionStore;
79
+ }
80
+ declare function usePermission(): UsePermissionResult;
81
+ /** Returns whether the current user has the given permission(s). */
82
+ declare function useHasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
83
+ /** Returns whether the current user has the given role(s). */
84
+ declare function useHasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
85
+
86
+ interface CanProps {
87
+ /** Permission codes to check. */
88
+ permission?: PermissionCode | PermissionCode[];
89
+ /** Role codes to check. */
90
+ role?: RoleCode | RoleCode[];
91
+ /**
92
+ * `any` — pass when at least one code matches (default)
93
+ * `all` — pass only when every code matches
94
+ */
95
+ mode?: CheckOptions['mode'];
96
+ /**
97
+ * How permission and role conditions combine when both are provided.
98
+ * Default: `and` (must satisfy both).
99
+ */
100
+ combine?: 'and' | 'or';
101
+ children: ReactNode;
102
+ /** Fallback when access is denied. */
103
+ fallback?: ReactNode;
104
+ }
105
+ /**
106
+ * Conditionally render children based on permission / role.
107
+ *
108
+ * @example
109
+ * <Can permission="user:edit">
110
+ * <button>Edit</button>
111
+ * </Can>
112
+ *
113
+ * <Can permission={['user:edit', 'user:delete']} mode="all" fallback={null}>
114
+ * <AdminPanel />
115
+ * </Can>
116
+ */
117
+ declare function Can({ permission, role, mode, combine, children, fallback, }: CanProps): react.JSX.Element;
118
+
119
+ export { Can, type CanProps, type CheckOptions, type MatchMode, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, type RoleCode, type UsePermissionResult, createPermissionStore, useHasPermission, useHasRole, usePermission };
@@ -0,0 +1,119 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type PermissionCode = string;
5
+ type RoleCode = string;
6
+ interface PermissionState {
7
+ /** Permission codes owned by the current user */
8
+ permissions: PermissionCode[];
9
+ /** Roles owned by the current user */
10
+ roles: RoleCode[];
11
+ /** Super admin bypasses all checks when true */
12
+ isSuperAdmin?: boolean;
13
+ }
14
+ type MatchMode = 'all' | 'any';
15
+ interface CheckOptions {
16
+ /**
17
+ * `any` — pass when at least one code matches (default)
18
+ * `all` — pass only when every code matches
19
+ */
20
+ mode?: MatchMode;
21
+ }
22
+ type PermissionListener = (state: PermissionState) => void;
23
+ interface PermissionChecker {
24
+ hasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
25
+ hasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
26
+ can(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
27
+ getState(): PermissionState;
28
+ }
29
+
30
+ /**
31
+ * Mutable permission store shared by React / Vue adapters.
32
+ * Framework-agnostic — safe to use in Node, browser, or any UI lib.
33
+ */
34
+ declare class PermissionStore implements PermissionChecker {
35
+ private state;
36
+ private permissionSet;
37
+ private roleSet;
38
+ private listeners;
39
+ constructor(initial?: Partial<PermissionState>);
40
+ getState(): PermissionState;
41
+ setState(next: Partial<PermissionState>): void;
42
+ setPermissions(permissions: PermissionCode[]): void;
43
+ setRoles(roles: RoleCode[]): void;
44
+ setSuperAdmin(isSuperAdmin: boolean): void;
45
+ /** Replace permissions, roles and super-admin flag in one shot (typical after login). */
46
+ hydrate(payload: Partial<PermissionState>): void;
47
+ /** Clear all authz data (typical after logout). */
48
+ clear(): void;
49
+ hasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
50
+ hasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
51
+ /** Alias of `hasPermission` for template-friendly APIs. */
52
+ can(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
53
+ subscribe(listener: PermissionListener): () => void;
54
+ private emit;
55
+ }
56
+ declare function createPermissionStore(initial?: Partial<PermissionState>): PermissionStore;
57
+
58
+ interface PermissionProviderProps {
59
+ children: ReactNode;
60
+ /** Pass an existing store to share across trees, or omit to create one. */
61
+ store?: PermissionStore;
62
+ /** Initial state when the provider creates its own store. */
63
+ initialState?: Partial<PermissionState>;
64
+ }
65
+ declare function PermissionProvider({ children, store: externalStore, initialState, }: PermissionProviderProps): react.FunctionComponentElement<react.ProviderProps<PermissionStore | null>>;
66
+ interface UsePermissionResult {
67
+ permissions: PermissionCode[];
68
+ roles: RoleCode[];
69
+ isSuperAdmin: boolean;
70
+ hasPermission: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;
71
+ hasRole: (codes: RoleCode | RoleCode[], options?: CheckOptions) => boolean;
72
+ can: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;
73
+ setPermissions: (permissions: PermissionCode[]) => void;
74
+ setRoles: (roles: RoleCode[]) => void;
75
+ setSuperAdmin: (isSuperAdmin: boolean) => void;
76
+ hydrate: (payload: Partial<PermissionState>) => void;
77
+ clear: () => void;
78
+ store: PermissionStore;
79
+ }
80
+ declare function usePermission(): UsePermissionResult;
81
+ /** Returns whether the current user has the given permission(s). */
82
+ declare function useHasPermission(codes: PermissionCode | PermissionCode[], options?: CheckOptions): boolean;
83
+ /** Returns whether the current user has the given role(s). */
84
+ declare function useHasRole(codes: RoleCode | RoleCode[], options?: CheckOptions): boolean;
85
+
86
+ interface CanProps {
87
+ /** Permission codes to check. */
88
+ permission?: PermissionCode | PermissionCode[];
89
+ /** Role codes to check. */
90
+ role?: RoleCode | RoleCode[];
91
+ /**
92
+ * `any` — pass when at least one code matches (default)
93
+ * `all` — pass only when every code matches
94
+ */
95
+ mode?: CheckOptions['mode'];
96
+ /**
97
+ * How permission and role conditions combine when both are provided.
98
+ * Default: `and` (must satisfy both).
99
+ */
100
+ combine?: 'and' | 'or';
101
+ children: ReactNode;
102
+ /** Fallback when access is denied. */
103
+ fallback?: ReactNode;
104
+ }
105
+ /**
106
+ * Conditionally render children based on permission / role.
107
+ *
108
+ * @example
109
+ * <Can permission="user:edit">
110
+ * <button>Edit</button>
111
+ * </Can>
112
+ *
113
+ * <Can permission={['user:edit', 'user:delete']} mode="all" fallback={null}>
114
+ * <AdminPanel />
115
+ * </Can>
116
+ */
117
+ declare function Can({ permission, role, mode, combine, children, fallback, }: CanProps): react.JSX.Element;
118
+
119
+ export { Can, type CanProps, type CheckOptions, type MatchMode, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, type RoleCode, type UsePermissionResult, createPermissionStore, useHasPermission, useHasRole, usePermission };
@@ -0,0 +1,134 @@
1
+ "use client";
2
+
3
+ // src/react/context.tsx
4
+ import {
5
+ createContext,
6
+ createElement,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useMemo,
11
+ useState
12
+ } from "react";
13
+ import {
14
+ createPermissionStore
15
+ } from "../core";
16
+ var PermissionContext = createContext(null);
17
+ function PermissionProvider({
18
+ children,
19
+ store: externalStore,
20
+ initialState
21
+ }) {
22
+ const store = useMemo(
23
+ () => externalStore ?? createPermissionStore(initialState),
24
+ // store identity is intentional; initialState only applies on first create
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ [externalStore]
27
+ );
28
+ return createElement(PermissionContext.Provider, { value: store }, children);
29
+ }
30
+ function useStore() {
31
+ const store = useContext(PermissionContext);
32
+ if (!store) {
33
+ throw new Error(
34
+ "[com-angel-authorization/react] usePermission* hooks must be used within <PermissionProvider>."
35
+ );
36
+ }
37
+ return store;
38
+ }
39
+ function usePermissionSnapshot(store) {
40
+ const [state, setState] = useState(() => store.getState());
41
+ useEffect(() => {
42
+ setState(store.getState());
43
+ return store.subscribe(setState);
44
+ }, [store]);
45
+ return state;
46
+ }
47
+ function usePermission() {
48
+ const store = useStore();
49
+ const state = usePermissionSnapshot(store);
50
+ const hasPermission = useCallback(
51
+ (codes, options) => store.hasPermission(codes, options),
52
+ [store]
53
+ );
54
+ const hasRole = useCallback(
55
+ (codes, options) => store.hasRole(codes, options),
56
+ [store]
57
+ );
58
+ const can = useCallback(
59
+ (codes, options) => store.can(codes, options),
60
+ [store]
61
+ );
62
+ return {
63
+ permissions: state.permissions,
64
+ roles: state.roles,
65
+ isSuperAdmin: Boolean(state.isSuperAdmin),
66
+ hasPermission,
67
+ hasRole,
68
+ can,
69
+ setPermissions: store.setPermissions.bind(store),
70
+ setRoles: store.setRoles.bind(store),
71
+ setSuperAdmin: store.setSuperAdmin.bind(store),
72
+ hydrate: store.hydrate.bind(store),
73
+ clear: store.clear.bind(store),
74
+ store
75
+ };
76
+ }
77
+ function useHasPermission(codes, options) {
78
+ const { hasPermission, permissions, isSuperAdmin } = usePermission();
79
+ void permissions;
80
+ void isSuperAdmin;
81
+ return hasPermission(codes, options);
82
+ }
83
+ function useHasRole(codes, options) {
84
+ const { hasRole, roles, isSuperAdmin } = usePermission();
85
+ void roles;
86
+ void isSuperAdmin;
87
+ return hasRole(codes, options);
88
+ }
89
+
90
+ // src/react/Can.tsx
91
+ import { Fragment, jsx } from "react/jsx-runtime";
92
+ function evaluateAccess(hasPermission, hasRole, props) {
93
+ const { permission, role, mode, combine = "and" } = props;
94
+ const options = { mode };
95
+ const hasPermCheck = permission !== void 0;
96
+ const hasRoleCheck = role !== void 0;
97
+ if (!hasPermCheck && !hasRoleCheck) return true;
98
+ const permOk = hasPermCheck ? hasPermission(permission, options) : true;
99
+ const roleOk = hasRoleCheck ? hasRole(role, options) : true;
100
+ if (hasPermCheck && hasRoleCheck) {
101
+ return combine === "or" ? permOk || roleOk : permOk && roleOk;
102
+ }
103
+ return hasPermCheck ? permOk : roleOk;
104
+ }
105
+ function Can({
106
+ permission,
107
+ role,
108
+ mode,
109
+ combine,
110
+ children,
111
+ fallback = null
112
+ }) {
113
+ const { hasPermission, hasRole } = usePermission();
114
+ const allowed = evaluateAccess(hasPermission, hasRole, {
115
+ permission,
116
+ role,
117
+ mode,
118
+ combine
119
+ });
120
+ return /* @__PURE__ */ jsx(Fragment, { children: allowed ? children : fallback });
121
+ }
122
+
123
+ // src/react/index.ts
124
+ import { PermissionStore, createPermissionStore as createPermissionStore2 } from "../core";
125
+ export {
126
+ Can,
127
+ PermissionProvider,
128
+ PermissionStore,
129
+ createPermissionStore2 as createPermissionStore,
130
+ useHasPermission,
131
+ useHasRole,
132
+ usePermission
133
+ };
134
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/context.tsx","../../src/react/Can.tsx","../../src/react/index.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\nimport {\n createPermissionStore,\n type CheckOptions,\n type PermissionCode,\n type PermissionState,\n type PermissionStore,\n type RoleCode,\n} from '../core';\n\nconst PermissionContext = createContext<PermissionStore | null>(null);\n\nexport interface PermissionProviderProps {\n children: ReactNode;\n /** Pass an existing store to share across trees, or omit to create one. */\n store?: PermissionStore;\n /** Initial state when the provider creates its own store. */\n initialState?: Partial<PermissionState>;\n}\n\nexport function PermissionProvider({\n children,\n store: externalStore,\n initialState,\n}: PermissionProviderProps) {\n const store = useMemo(\n () => externalStore ?? createPermissionStore(initialState),\n // store identity is intentional; initialState only applies on first create\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [externalStore],\n );\n\n return createElement(PermissionContext.Provider, { value: store }, children);\n}\n\nfunction useStore(): PermissionStore {\n const store = useContext(PermissionContext);\n if (!store) {\n throw new Error(\n '[com-angel-authorization/react] usePermission* hooks must be used within <PermissionProvider>.',\n );\n }\n return store;\n}\n\nfunction usePermissionSnapshot(store: PermissionStore): PermissionState {\n const [state, setState] = useState(() => store.getState());\n\n useEffect(() => {\n setState(store.getState());\n return store.subscribe(setState);\n }, [store]);\n\n return state;\n}\n\nexport interface UsePermissionResult {\n permissions: PermissionCode[];\n roles: RoleCode[];\n isSuperAdmin: boolean;\n hasPermission: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;\n hasRole: (codes: RoleCode | RoleCode[], options?: CheckOptions) => boolean;\n can: (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => boolean;\n setPermissions: (permissions: PermissionCode[]) => void;\n setRoles: (roles: RoleCode[]) => void;\n setSuperAdmin: (isSuperAdmin: boolean) => void;\n hydrate: (payload: Partial<PermissionState>) => void;\n clear: () => void;\n store: PermissionStore;\n}\n\nexport function usePermission(): UsePermissionResult {\n const store = useStore();\n const state = usePermissionSnapshot(store);\n\n const hasPermission = useCallback(\n (codes: PermissionCode | PermissionCode[], options?: CheckOptions) =>\n store.hasPermission(codes, options),\n [store],\n );\n\n const hasRole = useCallback(\n (codes: RoleCode | RoleCode[], options?: CheckOptions) => store.hasRole(codes, options),\n [store],\n );\n\n const can = useCallback(\n (codes: PermissionCode | PermissionCode[], options?: CheckOptions) => store.can(codes, options),\n [store],\n );\n\n return {\n permissions: state.permissions,\n roles: state.roles,\n isSuperAdmin: Boolean(state.isSuperAdmin),\n hasPermission,\n hasRole,\n can,\n setPermissions: store.setPermissions.bind(store),\n setRoles: store.setRoles.bind(store),\n setSuperAdmin: store.setSuperAdmin.bind(store),\n hydrate: store.hydrate.bind(store),\n clear: store.clear.bind(store),\n store,\n };\n}\n\n/** Returns whether the current user has the given permission(s). */\nexport function useHasPermission(\n codes: PermissionCode | PermissionCode[],\n options?: CheckOptions,\n): boolean {\n const { hasPermission, permissions, isSuperAdmin } = usePermission();\n // re-compute when permission list / super-admin changes\n void permissions;\n void isSuperAdmin;\n return hasPermission(codes, options);\n}\n\n/** Returns whether the current user has the given role(s). */\nexport function useHasRole(\n codes: RoleCode | RoleCode[],\n options?: CheckOptions,\n): boolean {\n const { hasRole, roles, isSuperAdmin } = usePermission();\n void roles;\n void isSuperAdmin;\n return hasRole(codes, options);\n}\n","import type { ReactNode } from 'react';\nimport type { CheckOptions, PermissionCode, RoleCode } from '../core';\nimport { usePermission } from './context';\n\nexport interface CanProps {\n /** Permission codes to check. */\n permission?: PermissionCode | PermissionCode[];\n /** Role codes to check. */\n role?: RoleCode | RoleCode[];\n /**\n * `any` — pass when at least one code matches (default)\n * `all` — pass only when every code matches\n */\n mode?: CheckOptions['mode'];\n /**\n * How permission and role conditions combine when both are provided.\n * Default: `and` (must satisfy both).\n */\n combine?: 'and' | 'or';\n children: ReactNode;\n /** Fallback when access is denied. */\n fallback?: ReactNode;\n}\n\nfunction evaluateAccess(\n hasPermission: (c: PermissionCode | PermissionCode[], o?: CheckOptions) => boolean,\n hasRole: (c: RoleCode | RoleCode[], o?: CheckOptions) => boolean,\n props: Pick<CanProps, 'permission' | 'role' | 'mode' | 'combine'>,\n): boolean {\n const { permission, role, mode, combine = 'and' } = props;\n const options: CheckOptions = { mode };\n\n const hasPermCheck = permission !== undefined;\n const hasRoleCheck = role !== undefined;\n\n if (!hasPermCheck && !hasRoleCheck) return true;\n\n const permOk = hasPermCheck ? hasPermission(permission!, options) : true;\n const roleOk = hasRoleCheck ? hasRole(role!, options) : true;\n\n if (hasPermCheck && hasRoleCheck) {\n return combine === 'or' ? permOk || roleOk : permOk && roleOk;\n }\n\n return hasPermCheck ? permOk : roleOk;\n}\n\n/**\n * Conditionally render children based on permission / role.\n *\n * @example\n * <Can permission=\"user:edit\">\n * <button>Edit</button>\n * </Can>\n *\n * <Can permission={['user:edit', 'user:delete']} mode=\"all\" fallback={null}>\n * <AdminPanel />\n * </Can>\n */\nexport function Can({\n permission,\n role,\n mode,\n combine,\n children,\n fallback = null,\n}: CanProps) {\n const { hasPermission, hasRole } = usePermission();\n const allowed = evaluateAccess(hasPermission, hasRole, {\n permission,\n role,\n mode,\n combine,\n });\n\n return <>{allowed ? children : fallback}</>;\n}\n","export { PermissionProvider, usePermission, useHasPermission, useHasRole } from './context';\nexport type { PermissionProviderProps, UsePermissionResult } from './context';\n\nexport { Can } from './Can';\nexport type { CanProps } from './Can';\n\nexport type {\n CheckOptions,\n MatchMode,\n PermissionChecker,\n PermissionCode,\n PermissionListener,\n PermissionState,\n RoleCode,\n} from '../core';\n\nexport { PermissionStore, createPermissionStore } from '../core';\n"],"mappings":"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","names":["createPermissionStore"]}