@pactor-app/permission 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pactor Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @pactor-app/permission
2
+
3
+ [Pactor](https://github.com/426-330/pactor) 的权限模块:权限码集合存储(PermissionStore)、适配 @pactor-app/runtime 的权限适配器、Menu / Page 守卫纯函数与表达式函数 `permission(code)`。
4
+
5
+ 浏览器只持有 Permission Set,DSL 中不出现 Role;前端权限只控制 UI(隐藏 / 禁用 / 拦截),后端必须再次鉴权。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @pactor-app/permission
11
+ # 或
12
+ pnpm add @pactor-app/permission
13
+ ```
14
+
15
+ 零运行时依赖(仅 @pactor-app/core 类型),不依赖 React 与 @pactor-app/runtime,可纯 Node 使用与测试。
16
+
17
+ ## 示例
18
+
19
+ ```ts
20
+ import {
21
+ PermissionStore,
22
+ createPermissionAdapter,
23
+ createPermissionFunction,
24
+ filterMenusByPermission,
25
+ checkPagePermission,
26
+ } from '@pactor-app/permission';
27
+
28
+ // bootstrap 阶段一次性注入权限码集合
29
+ const store = new PermissionStore();
30
+ store.setPermissions(['customer:view', 'customer:edit']);
31
+
32
+ // 1. 注入 @pactor-app/runtime(结构化兼容 PermissionAdapter)
33
+ const runtime = createPageRuntime(pageDsl, {
34
+ permission: createPermissionAdapter(store),
35
+ });
36
+ // - 节点 permission: 'customer:edit' 无权限时默认隐藏;
37
+ // - permission: { code: 'customer:edit', denied: 'disabled' } 时注入 disabled: true;
38
+ // - 动作配置无权限的 permission 时被 Action 引擎拦截({ ok: false })。
39
+
40
+ // 2. Menu / Page 守卫(纯函数)
41
+ const menus = filterMenusByPermission(rawMenus, store);
42
+ const canEnter = checkPagePermission(pageMeta, store);
43
+
44
+ // 3. 表达式函数(注入求值 options.functions 或作用域)
45
+ const functions = { permission: createPermissionFunction(store) };
46
+ // 支持 `${!permission('customer:edit')}` 形式
47
+
48
+ // 4. 权限变更通知
49
+ const off = store.subscribe(() => {/* 重新过滤菜单等 */});
50
+ ```
51
+
52
+ ## 功能一览
53
+
54
+ - **PermissionStore**:`setPermissions(codes)`(整体替换)/ `clear()` / `has(code)` / `hasAny(codes)` / `hasAll(codes)` / `subscribe(listener)`(返回退订函数)。V1 精确匹配,不做通配符。
55
+ - **`createPermissionAdapter(store)`**:结构化兼容 @pactor-app/runtime 的 `PermissionAdapter`,`check` 支持 string / string[](hasAny)/ `{ code, denied }` 三种配置形态(对象形式只判定 `code`)。
56
+ - **`resolvePermissionCheck(config, checker)`**:三种形态归一为 `{ allowed, denied }`(denied 缺省 `'hidden'`,未配置 permission 视为放行),Renderer 与 Guards 复用同一逻辑。
57
+ - **`filterMenusByPermission(menus, checker)`**:递归移除无权限菜单项与空目录,不修改原数组,泛型保持菜单项类型。
58
+ - **`checkPagePermission(page, checker)`**:页面无 permission 字段时放行。
59
+ - **`createPermissionFunction(store)`**:返回 `(code: string) => boolean`,作为白名单函数注入表达式求值。
60
+
61
+ ## 文档
62
+
63
+ 完整文档见 <https://github.com/426-330/pactor/tree/main/docs>(`pnpm docs:dev` 本地启动文档站)。
64
+
65
+ ## License
66
+
67
+ [MIT](../../LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1,138 @@
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
+ PermissionStore: () => PermissionStore,
24
+ checkPagePermission: () => checkPagePermission,
25
+ createPermissionAdapter: () => createPermissionAdapter,
26
+ createPermissionFunction: () => createPermissionFunction,
27
+ filterMenusByPermission: () => filterMenusByPermission,
28
+ resolvePermissionCheck: () => resolvePermissionCheck,
29
+ resolvePermissionCodes: () => resolvePermissionCodes
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/store.ts
34
+ var PermissionStore = class {
35
+ constructor() {
36
+ this.codes = /* @__PURE__ */ new Set();
37
+ this.listeners = /* @__PURE__ */ new Set();
38
+ }
39
+ /** 整体替换权限码集合并通知订阅者 */
40
+ setPermissions(codes) {
41
+ this.codes = new Set(codes);
42
+ this.notify();
43
+ }
44
+ /** 清空权限码并通知订阅者 */
45
+ clear() {
46
+ this.codes.clear();
47
+ this.notify();
48
+ }
49
+ /** 是否持有某权限码;未设置(含空集合)一律返回 false */
50
+ has(code) {
51
+ return this.codes.has(code);
52
+ }
53
+ /** 任一命中即返回 true(组件 / 动作配置数组的语义) */
54
+ hasAny(codes) {
55
+ return codes.some((code) => this.codes.has(code));
56
+ }
57
+ /** 全部命中才返回 true */
58
+ hasAll(codes) {
59
+ return codes.every((code) => this.codes.has(code));
60
+ }
61
+ /** 订阅权限变更,返回退订函数 */
62
+ subscribe(listener) {
63
+ this.listeners.add(listener);
64
+ return () => {
65
+ this.listeners.delete(listener);
66
+ };
67
+ }
68
+ notify() {
69
+ for (const listener of [...this.listeners]) {
70
+ listener();
71
+ }
72
+ }
73
+ };
74
+
75
+ // src/resolve.ts
76
+ function resolvePermissionCodes(config) {
77
+ if (typeof config === "string") {
78
+ return [config];
79
+ }
80
+ if (Array.isArray(config)) {
81
+ return config;
82
+ }
83
+ return Array.isArray(config.code) ? config.code : [config.code];
84
+ }
85
+ function resolvePermissionCheck(config, checker) {
86
+ if (config === void 0) {
87
+ return { allowed: true, denied: "hidden" };
88
+ }
89
+ const denied = typeof config === "object" && !Array.isArray(config) ? config.denied ?? "hidden" : "hidden";
90
+ return { allowed: checker.hasAny(resolvePermissionCodes(config)), denied };
91
+ }
92
+
93
+ // src/adapter.ts
94
+ function createPermissionAdapter(store) {
95
+ return {
96
+ check(permission) {
97
+ return resolvePermissionCheck(permission, store).allowed;
98
+ }
99
+ };
100
+ }
101
+
102
+ // src/guards.ts
103
+ function filterMenusByPermission(menus, checker) {
104
+ const result = [];
105
+ for (const menu of menus) {
106
+ if (!resolvePermissionCheck(menu.permission, checker).allowed) {
107
+ continue;
108
+ }
109
+ if (menu.children === void 0) {
110
+ result.push(menu);
111
+ continue;
112
+ }
113
+ const children = filterMenusByPermission(menu.children, checker);
114
+ if (menu.children.length > 0 && children.length === 0) {
115
+ continue;
116
+ }
117
+ result.push({ ...menu, children });
118
+ }
119
+ return result;
120
+ }
121
+ function checkPagePermission(page, checker) {
122
+ return resolvePermissionCheck(page.permission, checker).allowed;
123
+ }
124
+
125
+ // src/expression.ts
126
+ function createPermissionFunction(store) {
127
+ return (code) => store.has(code);
128
+ }
129
+ // Annotate the CommonJS export names for ESM import in node:
130
+ 0 && (module.exports = {
131
+ PermissionStore,
132
+ checkPagePermission,
133
+ createPermissionAdapter,
134
+ createPermissionFunction,
135
+ filterMenusByPermission,
136
+ resolvePermissionCheck,
137
+ resolvePermissionCodes
138
+ });
@@ -0,0 +1,112 @@
1
+ import { PermissionDeniedStrategy, PermissionConfig } from '@pactor-app/core';
2
+
3
+ /** 权限变更监听器 */
4
+ type PermissionListener = () => void;
5
+ /**
6
+ * PermissionStore(设计文档第 43 节,决策 D1):
7
+ * 浏览器端只持有权限码集合(Permission Set),DSL 中不出现 Role。
8
+ *
9
+ * 内部为 `Set<string>`;`setPermissions` 整体替换(bootstrap 阶段一次性
10
+ * 注入),`clear` 清空;两者都会通知订阅者。精确匹配,不做通配符。
11
+ */
12
+ declare class PermissionStore {
13
+ private codes;
14
+ private listeners;
15
+ /** 整体替换权限码集合并通知订阅者 */
16
+ setPermissions(codes: string[]): void;
17
+ /** 清空权限码并通知订阅者 */
18
+ clear(): void;
19
+ /** 是否持有某权限码;未设置(含空集合)一律返回 false */
20
+ has(code: string): boolean;
21
+ /** 任一命中即返回 true(组件 / 动作配置数组的语义) */
22
+ hasAny(codes: string[]): boolean;
23
+ /** 全部命中才返回 true */
24
+ hasAll(codes: string[]): boolean;
25
+ /** 订阅权限变更,返回退订函数 */
26
+ subscribe(listener: PermissionListener): () => void;
27
+ private notify;
28
+ }
29
+
30
+ /**
31
+ * 结构化权限判定接口(决策 D3):只需 `hasAny`。
32
+ * PermissionStore 天然满足该接口;runtime 的 PermissionAdapter
33
+ * 也可经简单包装适配。避免 permission → runtime 的依赖。
34
+ */
35
+ interface PermissionChecker {
36
+ hasAny(codes: string[]): boolean;
37
+ }
38
+ /** 权限解析结果:是否放行 + 无权限时的处理策略 */
39
+ interface ResolvedPermission {
40
+ allowed: boolean;
41
+ denied: PermissionDeniedStrategy;
42
+ }
43
+ /** 提取权限码数组:string / string[] / `{ code }` 三种形态归一 */
44
+ declare function resolvePermissionCodes(config: PermissionConfig): string[];
45
+ /**
46
+ * 权限配置统一解析(决策 D3):
47
+ * string / string[] / `{ code, denied }` 三种形态归一为
48
+ * `{ allowed, denied }`;数组按 hasAny;denied 缺省 `'hidden'`;
49
+ * 未配置 permission(undefined)视为放行。
50
+ *
51
+ * Renderer 与 Menu / Page Guard 复用同一逻辑,避免各处重复解释配置形状。
52
+ */
53
+ declare function resolvePermissionCheck(config: PermissionConfig | undefined, checker: PermissionChecker): ResolvedPermission;
54
+
55
+ /**
56
+ * 创建适配 @pactor-app/runtime `PermissionAdapter` 的权限适配器(决策 D4)。
57
+ *
58
+ * 返回对象结构化兼容 runtime 的 `PermissionAdapter` 接口
59
+ * (`check(permission: PermissionConfig): boolean`),但不 import
60
+ * @pactor-app/runtime 的任何运行时值,本包保持零 React / 零 runtime 依赖。
61
+ * 类型兼容性由测试中的编译期断言锁定(devDependency 仅用于类型)。
62
+ *
63
+ * 三种配置形态均只判定"有没有权限":对象形式只检查 `code`,
64
+ * denied 策略由 Renderer / Guard 解释。
65
+ */
66
+ declare function createPermissionAdapter(store: PermissionStore): {
67
+ check(permission: PermissionConfig): boolean;
68
+ };
69
+
70
+ /**
71
+ * 菜单项结构约束:带可选 permission 与递归 children。
72
+ * 泛型保持调用方的菜单项类型(额外字段原样保留)。
73
+ */
74
+ type MenuItemWithPermission<T> = T & {
75
+ permission?: PermissionConfig;
76
+ children?: MenuItemWithPermission<T>[];
77
+ };
78
+ /**
79
+ * Menu Guard(决策 D5):递归过滤无权限的菜单项。
80
+ *
81
+ * - 未配置 permission 的项保留;
82
+ * - children 被全部移除的空目录一并移除;
83
+ * - 返回新数组,原数组与原菜单项对象不被修改
84
+ * (children 被过滤的项返回浅拷贝)。
85
+ *
86
+ * checker 为结构化接口,直接传 PermissionStore 即可。
87
+ */
88
+ declare function filterMenusByPermission<T extends {
89
+ permission?: PermissionConfig;
90
+ children?: T[];
91
+ }>(menus: readonly T[], checker: PermissionChecker): T[];
92
+ /**
93
+ * Page Guard(决策 D5):页面元信息含 permission 时判定是否放行;
94
+ * 无 permission 字段一律放行。checker 为结构化接口,直接传
95
+ * PermissionStore 即可。
96
+ */
97
+ declare function checkPagePermission<T extends {
98
+ permission?: PermissionConfig;
99
+ }>(page: T, checker: PermissionChecker): boolean;
100
+
101
+ /**
102
+ * 表达式函数 `permission(code)` 工厂(决策 D6):
103
+ * 返回 `(code: string) => boolean`,可作为白名单函数注入
104
+ * @pactor-app/expression 求值(`options.functions`)或求值作用域,
105
+ * 支持 `${!permission('customer:edit')}` 形式。
106
+ *
107
+ * PageRuntime 的自动注入由上层组合层(@pactor-app/app)决定,本包不改变
108
+ * PageRuntime 的既有行为。
109
+ */
110
+ declare function createPermissionFunction(store: PermissionStore): (code: string) => boolean;
111
+
112
+ export { type MenuItemWithPermission, type PermissionChecker, type PermissionListener, PermissionStore, type ResolvedPermission, checkPagePermission, createPermissionAdapter, createPermissionFunction, filterMenusByPermission, resolvePermissionCheck, resolvePermissionCodes };
@@ -0,0 +1,112 @@
1
+ import { PermissionDeniedStrategy, PermissionConfig } from '@pactor-app/core';
2
+
3
+ /** 权限变更监听器 */
4
+ type PermissionListener = () => void;
5
+ /**
6
+ * PermissionStore(设计文档第 43 节,决策 D1):
7
+ * 浏览器端只持有权限码集合(Permission Set),DSL 中不出现 Role。
8
+ *
9
+ * 内部为 `Set<string>`;`setPermissions` 整体替换(bootstrap 阶段一次性
10
+ * 注入),`clear` 清空;两者都会通知订阅者。精确匹配,不做通配符。
11
+ */
12
+ declare class PermissionStore {
13
+ private codes;
14
+ private listeners;
15
+ /** 整体替换权限码集合并通知订阅者 */
16
+ setPermissions(codes: string[]): void;
17
+ /** 清空权限码并通知订阅者 */
18
+ clear(): void;
19
+ /** 是否持有某权限码;未设置(含空集合)一律返回 false */
20
+ has(code: string): boolean;
21
+ /** 任一命中即返回 true(组件 / 动作配置数组的语义) */
22
+ hasAny(codes: string[]): boolean;
23
+ /** 全部命中才返回 true */
24
+ hasAll(codes: string[]): boolean;
25
+ /** 订阅权限变更,返回退订函数 */
26
+ subscribe(listener: PermissionListener): () => void;
27
+ private notify;
28
+ }
29
+
30
+ /**
31
+ * 结构化权限判定接口(决策 D3):只需 `hasAny`。
32
+ * PermissionStore 天然满足该接口;runtime 的 PermissionAdapter
33
+ * 也可经简单包装适配。避免 permission → runtime 的依赖。
34
+ */
35
+ interface PermissionChecker {
36
+ hasAny(codes: string[]): boolean;
37
+ }
38
+ /** 权限解析结果:是否放行 + 无权限时的处理策略 */
39
+ interface ResolvedPermission {
40
+ allowed: boolean;
41
+ denied: PermissionDeniedStrategy;
42
+ }
43
+ /** 提取权限码数组:string / string[] / `{ code }` 三种形态归一 */
44
+ declare function resolvePermissionCodes(config: PermissionConfig): string[];
45
+ /**
46
+ * 权限配置统一解析(决策 D3):
47
+ * string / string[] / `{ code, denied }` 三种形态归一为
48
+ * `{ allowed, denied }`;数组按 hasAny;denied 缺省 `'hidden'`;
49
+ * 未配置 permission(undefined)视为放行。
50
+ *
51
+ * Renderer 与 Menu / Page Guard 复用同一逻辑,避免各处重复解释配置形状。
52
+ */
53
+ declare function resolvePermissionCheck(config: PermissionConfig | undefined, checker: PermissionChecker): ResolvedPermission;
54
+
55
+ /**
56
+ * 创建适配 @pactor-app/runtime `PermissionAdapter` 的权限适配器(决策 D4)。
57
+ *
58
+ * 返回对象结构化兼容 runtime 的 `PermissionAdapter` 接口
59
+ * (`check(permission: PermissionConfig): boolean`),但不 import
60
+ * @pactor-app/runtime 的任何运行时值,本包保持零 React / 零 runtime 依赖。
61
+ * 类型兼容性由测试中的编译期断言锁定(devDependency 仅用于类型)。
62
+ *
63
+ * 三种配置形态均只判定"有没有权限":对象形式只检查 `code`,
64
+ * denied 策略由 Renderer / Guard 解释。
65
+ */
66
+ declare function createPermissionAdapter(store: PermissionStore): {
67
+ check(permission: PermissionConfig): boolean;
68
+ };
69
+
70
+ /**
71
+ * 菜单项结构约束:带可选 permission 与递归 children。
72
+ * 泛型保持调用方的菜单项类型(额外字段原样保留)。
73
+ */
74
+ type MenuItemWithPermission<T> = T & {
75
+ permission?: PermissionConfig;
76
+ children?: MenuItemWithPermission<T>[];
77
+ };
78
+ /**
79
+ * Menu Guard(决策 D5):递归过滤无权限的菜单项。
80
+ *
81
+ * - 未配置 permission 的项保留;
82
+ * - children 被全部移除的空目录一并移除;
83
+ * - 返回新数组,原数组与原菜单项对象不被修改
84
+ * (children 被过滤的项返回浅拷贝)。
85
+ *
86
+ * checker 为结构化接口,直接传 PermissionStore 即可。
87
+ */
88
+ declare function filterMenusByPermission<T extends {
89
+ permission?: PermissionConfig;
90
+ children?: T[];
91
+ }>(menus: readonly T[], checker: PermissionChecker): T[];
92
+ /**
93
+ * Page Guard(决策 D5):页面元信息含 permission 时判定是否放行;
94
+ * 无 permission 字段一律放行。checker 为结构化接口,直接传
95
+ * PermissionStore 即可。
96
+ */
97
+ declare function checkPagePermission<T extends {
98
+ permission?: PermissionConfig;
99
+ }>(page: T, checker: PermissionChecker): boolean;
100
+
101
+ /**
102
+ * 表达式函数 `permission(code)` 工厂(决策 D6):
103
+ * 返回 `(code: string) => boolean`,可作为白名单函数注入
104
+ * @pactor-app/expression 求值(`options.functions`)或求值作用域,
105
+ * 支持 `${!permission('customer:edit')}` 形式。
106
+ *
107
+ * PageRuntime 的自动注入由上层组合层(@pactor-app/app)决定,本包不改变
108
+ * PageRuntime 的既有行为。
109
+ */
110
+ declare function createPermissionFunction(store: PermissionStore): (code: string) => boolean;
111
+
112
+ export { type MenuItemWithPermission, type PermissionChecker, type PermissionListener, PermissionStore, type ResolvedPermission, checkPagePermission, createPermissionAdapter, createPermissionFunction, filterMenusByPermission, resolvePermissionCheck, resolvePermissionCodes };
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ // src/store.ts
2
+ var PermissionStore = class {
3
+ constructor() {
4
+ this.codes = /* @__PURE__ */ new Set();
5
+ this.listeners = /* @__PURE__ */ new Set();
6
+ }
7
+ /** 整体替换权限码集合并通知订阅者 */
8
+ setPermissions(codes) {
9
+ this.codes = new Set(codes);
10
+ this.notify();
11
+ }
12
+ /** 清空权限码并通知订阅者 */
13
+ clear() {
14
+ this.codes.clear();
15
+ this.notify();
16
+ }
17
+ /** 是否持有某权限码;未设置(含空集合)一律返回 false */
18
+ has(code) {
19
+ return this.codes.has(code);
20
+ }
21
+ /** 任一命中即返回 true(组件 / 动作配置数组的语义) */
22
+ hasAny(codes) {
23
+ return codes.some((code) => this.codes.has(code));
24
+ }
25
+ /** 全部命中才返回 true */
26
+ hasAll(codes) {
27
+ return codes.every((code) => this.codes.has(code));
28
+ }
29
+ /** 订阅权限变更,返回退订函数 */
30
+ subscribe(listener) {
31
+ this.listeners.add(listener);
32
+ return () => {
33
+ this.listeners.delete(listener);
34
+ };
35
+ }
36
+ notify() {
37
+ for (const listener of [...this.listeners]) {
38
+ listener();
39
+ }
40
+ }
41
+ };
42
+
43
+ // src/resolve.ts
44
+ function resolvePermissionCodes(config) {
45
+ if (typeof config === "string") {
46
+ return [config];
47
+ }
48
+ if (Array.isArray(config)) {
49
+ return config;
50
+ }
51
+ return Array.isArray(config.code) ? config.code : [config.code];
52
+ }
53
+ function resolvePermissionCheck(config, checker) {
54
+ if (config === void 0) {
55
+ return { allowed: true, denied: "hidden" };
56
+ }
57
+ const denied = typeof config === "object" && !Array.isArray(config) ? config.denied ?? "hidden" : "hidden";
58
+ return { allowed: checker.hasAny(resolvePermissionCodes(config)), denied };
59
+ }
60
+
61
+ // src/adapter.ts
62
+ function createPermissionAdapter(store) {
63
+ return {
64
+ check(permission) {
65
+ return resolvePermissionCheck(permission, store).allowed;
66
+ }
67
+ };
68
+ }
69
+
70
+ // src/guards.ts
71
+ function filterMenusByPermission(menus, checker) {
72
+ const result = [];
73
+ for (const menu of menus) {
74
+ if (!resolvePermissionCheck(menu.permission, checker).allowed) {
75
+ continue;
76
+ }
77
+ if (menu.children === void 0) {
78
+ result.push(menu);
79
+ continue;
80
+ }
81
+ const children = filterMenusByPermission(menu.children, checker);
82
+ if (menu.children.length > 0 && children.length === 0) {
83
+ continue;
84
+ }
85
+ result.push({ ...menu, children });
86
+ }
87
+ return result;
88
+ }
89
+ function checkPagePermission(page, checker) {
90
+ return resolvePermissionCheck(page.permission, checker).allowed;
91
+ }
92
+
93
+ // src/expression.ts
94
+ function createPermissionFunction(store) {
95
+ return (code) => store.has(code);
96
+ }
97
+ export {
98
+ PermissionStore,
99
+ checkPagePermission,
100
+ createPermissionAdapter,
101
+ createPermissionFunction,
102
+ filterMenusByPermission,
103
+ resolvePermissionCheck,
104
+ resolvePermissionCodes
105
+ };
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@pactor-app/permission",
3
+ "version": "0.1.0",
4
+ "description": "Pactor permission: permission code store, runtime permission adapter, menu/page guards and expression function",
5
+ "keywords": [
6
+ "pactor",
7
+ "dsl",
8
+ "server-driven-ui",
9
+ "permission",
10
+ "access-control"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/426-330/pactor.git",
16
+ "directory": "packages/permission"
17
+ },
18
+ "homepage": "https://github.com/426-330/pactor/tree/main/packages/permission",
19
+ "bugs": "https://github.com/426-330/pactor/issues",
20
+ "type": "module",
21
+ "main": "./dist/index.cjs",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "import": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "require": {
31
+ "types": "./dist/index.d.cts",
32
+ "default": "./dist/index.cjs"
33
+ }
34
+ }
35
+ },
36
+ "sideEffects": false,
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "dependencies": {
47
+ "@pactor-app/core": "0.1.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/react": "^19.2.18",
51
+ "react": "^19.2.8",
52
+ "@pactor-app/runtime": "0.1.0",
53
+ "@pactor-app/expression": "0.1.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsup",
57
+ "test": "vitest run",
58
+ "typecheck": "tsc --noEmit"
59
+ }
60
+ }