@pactor-app/core 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 +44 -0
- package/dist/index.cjs +127 -0
- package/dist/index.d.cts +217 -0
- package/dist/index.d.ts +217 -0
- package/dist/index.js +98 -0
- package/package.json +52 -0
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,44 @@
|
|
|
1
|
+
# @pactor-app/core
|
|
2
|
+
|
|
3
|
+
[Pactor](https://github.com/426-330/pactor) 的核心协议包:DSL 类型定义与通用 Registry。零运行时依赖,与 React 无关,可用于浏览器、Node 与构建工具等任意环境。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pactor-app/core
|
|
9
|
+
# 或
|
|
10
|
+
pnpm add @pactor-app/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 包含内容
|
|
14
|
+
|
|
15
|
+
- **DSL 节点类型**:`DslNode`、`ActionConfig`、`ConditionConfig`、`PermissionConfig`、`EventMap`
|
|
16
|
+
- **DSL 文档结构类型**:`PageDsl`、`FragmentDsl`、`ApplicationDsl`、`DataSourceConfig`、`LifecycleConfig`、`RequiresConfig`
|
|
17
|
+
- **`Registry<T>`**:通用注册表(register / get / has / unregister / list),Component / Action / Service / Capability / Expression Function 等 Registry 的共同基座
|
|
18
|
+
- **`RUNTIME_SCOPES`**:运行时作用域层级常量(global → app → page → overlay → form → component → row → event)
|
|
19
|
+
|
|
20
|
+
## 示例
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { Registry, type DslNode, type PageDsl } from '@pactor-app/core';
|
|
24
|
+
|
|
25
|
+
const node: DslNode = {
|
|
26
|
+
type: 'Button',
|
|
27
|
+
props: { text: '保存' },
|
|
28
|
+
events: { onClick: { action: 'submit' } },
|
|
29
|
+
when: "${state.status == 'ACTIVE'}",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const components = new Registry<unknown>('ComponentRegistry');
|
|
33
|
+
components.register('Button', ButtonRenderer);
|
|
34
|
+
components.get('Button'); // ButtonRenderer
|
|
35
|
+
components.get('Missing'); // undefined(不抛错)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 文档
|
|
39
|
+
|
|
40
|
+
完整文档见 <https://github.com/426-330/pactor/tree/main/docs>(`pnpm docs:dev` 本地启动文档站)。
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
[MIT](../../LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
RUNTIME_SCOPES: () => RUNTIME_SCOPES,
|
|
24
|
+
Registry: () => Registry,
|
|
25
|
+
createI18nContext: () => createI18nContext
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/scope.ts
|
|
30
|
+
var RUNTIME_SCOPES = [
|
|
31
|
+
"global",
|
|
32
|
+
"app",
|
|
33
|
+
"page",
|
|
34
|
+
"overlay",
|
|
35
|
+
"form",
|
|
36
|
+
"component",
|
|
37
|
+
"row",
|
|
38
|
+
"event"
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// src/registry.ts
|
|
42
|
+
var Registry = class {
|
|
43
|
+
/**
|
|
44
|
+
* @param label 用于错误信息中标识 Registry 用途,如 "ComponentRegistry"
|
|
45
|
+
*/
|
|
46
|
+
constructor(label = "Registry") {
|
|
47
|
+
this.label = label;
|
|
48
|
+
this.items = /* @__PURE__ */ new Map();
|
|
49
|
+
}
|
|
50
|
+
/** 注册条目;重复注册同名条目抛出错误 */
|
|
51
|
+
register(name, item) {
|
|
52
|
+
if (this.items.has(name)) {
|
|
53
|
+
throw new Error(`[${this.label}] duplicate registration: "${name}"`);
|
|
54
|
+
}
|
|
55
|
+
this.items.set(name, item);
|
|
56
|
+
}
|
|
57
|
+
/** 获取条目;未注册时返回 undefined,不抛错 */
|
|
58
|
+
get(name) {
|
|
59
|
+
return this.items.get(name);
|
|
60
|
+
}
|
|
61
|
+
has(name) {
|
|
62
|
+
return this.items.has(name);
|
|
63
|
+
}
|
|
64
|
+
/** 注销条目;返回是否存在并被移除 */
|
|
65
|
+
unregister(name) {
|
|
66
|
+
return this.items.delete(name);
|
|
67
|
+
}
|
|
68
|
+
/** 全部已注册名称 */
|
|
69
|
+
list() {
|
|
70
|
+
return [...this.items.keys()];
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/i18n.ts
|
|
75
|
+
function interpolate(template, params) {
|
|
76
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, name) => {
|
|
77
|
+
const value = params[name];
|
|
78
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function isStringMap(value) {
|
|
82
|
+
if (typeof value !== "object" || value === null) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
86
|
+
}
|
|
87
|
+
function createI18nContext(options = {}) {
|
|
88
|
+
const locale = options.locale ?? "zh-CN";
|
|
89
|
+
const defaultLocale = options.defaultLocale ?? "zh-CN";
|
|
90
|
+
const messages = options.messages ?? {};
|
|
91
|
+
const t = (key, params) => {
|
|
92
|
+
let template;
|
|
93
|
+
const currentLocaleMessages = messages[locale];
|
|
94
|
+
if (isStringMap(currentLocaleMessages)) {
|
|
95
|
+
const value = currentLocaleMessages[key];
|
|
96
|
+
if (typeof value === "string") {
|
|
97
|
+
template = value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (template === void 0) {
|
|
101
|
+
const flatValue = messages[key];
|
|
102
|
+
if (typeof flatValue === "string") {
|
|
103
|
+
template = flatValue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (template === void 0 && locale !== defaultLocale) {
|
|
107
|
+
const defaultLocaleMessages = messages[defaultLocale];
|
|
108
|
+
if (isStringMap(defaultLocaleMessages)) {
|
|
109
|
+
const value = defaultLocaleMessages[key];
|
|
110
|
+
if (typeof value === "string") {
|
|
111
|
+
template = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (template === void 0) {
|
|
116
|
+
return key;
|
|
117
|
+
}
|
|
118
|
+
return params ? interpolate(template, params) : template;
|
|
119
|
+
};
|
|
120
|
+
return { locale, defaultLocale, t };
|
|
121
|
+
}
|
|
122
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
123
|
+
0 && (module.exports = {
|
|
124
|
+
RUNTIME_SCOPES,
|
|
125
|
+
Registry,
|
|
126
|
+
createI18nContext
|
|
127
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL 核心节点(设计文档第 11 节)。
|
|
3
|
+
*/
|
|
4
|
+
/** 受控表达式字符串,如 "${state.count}" */
|
|
5
|
+
type ExpressionString = string;
|
|
6
|
+
/** 条件配置:表达式字符串,或显式对象形式 */
|
|
7
|
+
type ConditionConfig = ExpressionString | {
|
|
8
|
+
expression: ExpressionString;
|
|
9
|
+
};
|
|
10
|
+
/** 无权限时的组件处理策略(设计文档第 41 节) */
|
|
11
|
+
type PermissionDeniedStrategy = 'hidden' | 'disabled' | 'readonly';
|
|
12
|
+
/**
|
|
13
|
+
* 权限配置:权限码或权限码数组(任一命中即可,由权限运行时解释),
|
|
14
|
+
* 或对象形式(`code` 判定权限,`denied` 指定无权限时的组件处理策略,
|
|
15
|
+
* 缺省为 `'hidden'`)
|
|
16
|
+
*/
|
|
17
|
+
type PermissionConfig = string | string[] | {
|
|
18
|
+
code: string | string[];
|
|
19
|
+
denied?: PermissionDeniedStrategy;
|
|
20
|
+
};
|
|
21
|
+
/** 动作配置(设计文档第 17 节) */
|
|
22
|
+
interface ActionConfig {
|
|
23
|
+
/**
|
|
24
|
+
* 命名动作 id(设计文档第 48 节):声明后,动作执行结果写入
|
|
25
|
+
* 表达式作用域 `actions.<id>`(成功时为结果数据,失败时为
|
|
26
|
+
* `{ ok: false, error }`),供后续动作引用,如
|
|
27
|
+
* `${actions.editResult.success}`;缺省时行为不变
|
|
28
|
+
*/
|
|
29
|
+
id?: string;
|
|
30
|
+
/** 动作名,对应 Action Registry 中的注册名,如 "navigate"、"risk.calculate" */
|
|
31
|
+
action: string;
|
|
32
|
+
/** 动作参数,值可含表达式字符串 */
|
|
33
|
+
params?: Record<string, unknown>;
|
|
34
|
+
/** 条件执行 */
|
|
35
|
+
condition?: ConditionConfig;
|
|
36
|
+
/** 执行权限 */
|
|
37
|
+
permission?: PermissionConfig;
|
|
38
|
+
}
|
|
39
|
+
/** 事件映射:事件名 → 单个或多个动作 */
|
|
40
|
+
type EventMap = Record<string, ActionConfig | ActionConfig[]>;
|
|
41
|
+
/** UI DSL 树基础节点 */
|
|
42
|
+
interface DslNode {
|
|
43
|
+
id?: string;
|
|
44
|
+
/** 组件类型,对应 Component Registry 中的注册名 */
|
|
45
|
+
type: string;
|
|
46
|
+
props?: Record<string, unknown>;
|
|
47
|
+
children?: DslNode[];
|
|
48
|
+
events?: EventMap;
|
|
49
|
+
permission?: PermissionConfig;
|
|
50
|
+
/** 条件渲染 */
|
|
51
|
+
when?: ConditionConfig;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* DSL 文档结构(设计文档第 9、10、37 节)。
|
|
56
|
+
*/
|
|
57
|
+
/** DSL Specification Version 字符串,如 "1.0" */
|
|
58
|
+
type DslVersion = string;
|
|
59
|
+
/** 能力声明:DSL 对 Runtime / 组件版本的要求(第 37 节) */
|
|
60
|
+
interface RequiresConfig {
|
|
61
|
+
/** Runtime 版本范围(semver range),如 ">=1.0.0" */
|
|
62
|
+
runtime?: string;
|
|
63
|
+
/** 组件 API 版本要求:组件名 → semver range */
|
|
64
|
+
components?: Record<string, string>;
|
|
65
|
+
}
|
|
66
|
+
/** 页面元信息 */
|
|
67
|
+
interface PageMeta {
|
|
68
|
+
id: string;
|
|
69
|
+
title?: string;
|
|
70
|
+
/** 页面类型 / Page Pattern,如 "list"、"detail"、"form" */
|
|
71
|
+
type?: string;
|
|
72
|
+
permission?: PermissionConfig;
|
|
73
|
+
}
|
|
74
|
+
/** DataSource 类型(第 19 节) */
|
|
75
|
+
type DataSourceType = 'http' | 'service' | 'static' | 'computed' | 'resource';
|
|
76
|
+
/** DataSource 配置 */
|
|
77
|
+
interface DataSourceConfig {
|
|
78
|
+
type: DataSourceType;
|
|
79
|
+
/** type 为 service 时的服务名,如 "customer.list" */
|
|
80
|
+
service?: string;
|
|
81
|
+
autoLoad?: boolean;
|
|
82
|
+
/** 调用参数,值可含表达式字符串 */
|
|
83
|
+
params?: Record<string, unknown>;
|
|
84
|
+
/** type 为 http 时的请求描述 */
|
|
85
|
+
request?: {
|
|
86
|
+
method?: string;
|
|
87
|
+
url: string;
|
|
88
|
+
params?: Record<string, unknown>;
|
|
89
|
+
headers?: Record<string, string>;
|
|
90
|
+
body?: unknown;
|
|
91
|
+
};
|
|
92
|
+
/** 分页参数映射,值可含表达式字符串 */
|
|
93
|
+
pagination?: {
|
|
94
|
+
page?: unknown;
|
|
95
|
+
pageSize?: unknown;
|
|
96
|
+
};
|
|
97
|
+
/** type 为 static 时的静态数据 */
|
|
98
|
+
value?: unknown;
|
|
99
|
+
}
|
|
100
|
+
/** 业务语义生命周期事件名(第 20 节) */
|
|
101
|
+
type LifecycleEventName = 'onInit' | 'onLoad' | 'onReady' | 'onShow' | 'onHide' | 'onDestroy';
|
|
102
|
+
/** 生命周期配置:事件名 → 单个或多个动作 */
|
|
103
|
+
type LifecycleConfig = Partial<Record<LifecycleEventName, ActionConfig | ActionConfig[]>>;
|
|
104
|
+
/** Page DSL 根结构(第 10 节) */
|
|
105
|
+
interface PageDsl {
|
|
106
|
+
/** DSL Specification Version,必填 */
|
|
107
|
+
dsl: DslVersion;
|
|
108
|
+
requires?: RequiresConfig;
|
|
109
|
+
page: PageMeta;
|
|
110
|
+
/** 页面初始状态 */
|
|
111
|
+
state?: Record<string, unknown>;
|
|
112
|
+
dataSources?: Record<string, DataSourceConfig>;
|
|
113
|
+
lifecycle?: LifecycleConfig;
|
|
114
|
+
/** UI 树根 */
|
|
115
|
+
body?: DslNode;
|
|
116
|
+
}
|
|
117
|
+
/** Fragment DSL(第 50 节):可复用的 UI 片段 */
|
|
118
|
+
interface FragmentDsl {
|
|
119
|
+
dsl: DslVersion;
|
|
120
|
+
requires?: RequiresConfig;
|
|
121
|
+
fragment: {
|
|
122
|
+
id: string;
|
|
123
|
+
title?: string;
|
|
124
|
+
};
|
|
125
|
+
body: DslNode;
|
|
126
|
+
}
|
|
127
|
+
/** Application DSL(第 9.1 节) */
|
|
128
|
+
interface ApplicationDsl {
|
|
129
|
+
dsl: DslVersion;
|
|
130
|
+
requires?: RequiresConfig;
|
|
131
|
+
application: {
|
|
132
|
+
id: string;
|
|
133
|
+
layout?: {
|
|
134
|
+
type: string;
|
|
135
|
+
};
|
|
136
|
+
theme?: {
|
|
137
|
+
name: string;
|
|
138
|
+
};
|
|
139
|
+
navigation?: {
|
|
140
|
+
mode: string;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 运行时作用域层级(设计文档第 15 节),自外向内排列。
|
|
147
|
+
*/
|
|
148
|
+
declare const RUNTIME_SCOPES: readonly ["global", "app", "page", "overlay", "form", "component", "row", "event"];
|
|
149
|
+
type RuntimeScope = (typeof RUNTIME_SCOPES)[number];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 通用 Registry(设计文档第 3.3、12、17、18 节)。
|
|
153
|
+
*
|
|
154
|
+
* Component / Action / Service / Capability / Expression Function 等
|
|
155
|
+
* 具体 Registry 的共同基座:名称 → 条目。
|
|
156
|
+
*/
|
|
157
|
+
declare class Registry<T> {
|
|
158
|
+
private readonly label;
|
|
159
|
+
private readonly items;
|
|
160
|
+
/**
|
|
161
|
+
* @param label 用于错误信息中标识 Registry 用途,如 "ComponentRegistry"
|
|
162
|
+
*/
|
|
163
|
+
constructor(label?: string);
|
|
164
|
+
/** 注册条目;重复注册同名条目抛出错误 */
|
|
165
|
+
register(name: string, item: T): void;
|
|
166
|
+
/** 获取条目;未注册时返回 undefined,不抛错 */
|
|
167
|
+
get(name: string): T | undefined;
|
|
168
|
+
has(name: string): boolean;
|
|
169
|
+
/** 注销条目;返回是否存在并被移除 */
|
|
170
|
+
unregister(name: string): boolean;
|
|
171
|
+
/** 全部已注册名称 */
|
|
172
|
+
list(): string[];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 国际化(i18n)基础类型定义与翻译工具。
|
|
177
|
+
*
|
|
178
|
+
* 翻译模型为扁平键值对:key 支持点路径嵌套(如 `menu.home`),
|
|
179
|
+
* value 为对应语言的文案,可包含 `{{name}}` 占位符用于参数插值。
|
|
180
|
+
*/
|
|
181
|
+
/** 语言代码,如 'zh-CN'、'en-US' */
|
|
182
|
+
type Locale = string;
|
|
183
|
+
/** 翻译消息表:key → 文案,或 locale → key → 文案 */
|
|
184
|
+
type I18nMessages = Record<string, string | Record<string, string>>;
|
|
185
|
+
/** 参数插值值 */
|
|
186
|
+
type I18nParams = Record<string, unknown>;
|
|
187
|
+
/**
|
|
188
|
+
* i18n 上下文:暴露给 RuntimeContext 与表达式引擎。
|
|
189
|
+
* `t` 为翻译函数,支持参数插值与回退。
|
|
190
|
+
*/
|
|
191
|
+
interface I18nContext {
|
|
192
|
+
/** 当前生效的语言代码 */
|
|
193
|
+
locale: Locale;
|
|
194
|
+
/** 回退语言代码(缺省 'zh-CN') */
|
|
195
|
+
defaultLocale: Locale;
|
|
196
|
+
/** 翻译函数 */
|
|
197
|
+
t(key: string, params?: I18nParams): string;
|
|
198
|
+
}
|
|
199
|
+
/** 创建 i18n 上下文时的配置 */
|
|
200
|
+
interface CreateI18nOptions {
|
|
201
|
+
locale?: Locale;
|
|
202
|
+
defaultLocale?: Locale;
|
|
203
|
+
messages?: I18nMessages;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 创建翻译函数。
|
|
207
|
+
*
|
|
208
|
+
* messages 支持两种结构:
|
|
209
|
+
* - 扁平:`{ 'menu.home': '首页' }`(当前 locale 直接使用)
|
|
210
|
+
* - 按语言:`{ 'zh-CN': { 'menu.home': '首页' }, 'en-US': { 'menu.home': 'Home' } }`
|
|
211
|
+
*
|
|
212
|
+
* 回退顺序:当前 locale → defaultLocale → key 本身。
|
|
213
|
+
* 支持 `{{name}}` 参数插值。
|
|
214
|
+
*/
|
|
215
|
+
declare function createI18nContext(options?: CreateI18nOptions): I18nContext;
|
|
216
|
+
|
|
217
|
+
export { type ActionConfig, type ApplicationDsl, type ConditionConfig, type CreateI18nOptions, type DataSourceConfig, type DataSourceType, type DslNode, type DslVersion, type EventMap, type ExpressionString, type FragmentDsl, type I18nContext, type I18nMessages, type I18nParams, type LifecycleConfig, type LifecycleEventName, type Locale, type PageDsl, type PageMeta, type PermissionConfig, type PermissionDeniedStrategy, RUNTIME_SCOPES, Registry, type RequiresConfig, type RuntimeScope, createI18nContext };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL 核心节点(设计文档第 11 节)。
|
|
3
|
+
*/
|
|
4
|
+
/** 受控表达式字符串,如 "${state.count}" */
|
|
5
|
+
type ExpressionString = string;
|
|
6
|
+
/** 条件配置:表达式字符串,或显式对象形式 */
|
|
7
|
+
type ConditionConfig = ExpressionString | {
|
|
8
|
+
expression: ExpressionString;
|
|
9
|
+
};
|
|
10
|
+
/** 无权限时的组件处理策略(设计文档第 41 节) */
|
|
11
|
+
type PermissionDeniedStrategy = 'hidden' | 'disabled' | 'readonly';
|
|
12
|
+
/**
|
|
13
|
+
* 权限配置:权限码或权限码数组(任一命中即可,由权限运行时解释),
|
|
14
|
+
* 或对象形式(`code` 判定权限,`denied` 指定无权限时的组件处理策略,
|
|
15
|
+
* 缺省为 `'hidden'`)
|
|
16
|
+
*/
|
|
17
|
+
type PermissionConfig = string | string[] | {
|
|
18
|
+
code: string | string[];
|
|
19
|
+
denied?: PermissionDeniedStrategy;
|
|
20
|
+
};
|
|
21
|
+
/** 动作配置(设计文档第 17 节) */
|
|
22
|
+
interface ActionConfig {
|
|
23
|
+
/**
|
|
24
|
+
* 命名动作 id(设计文档第 48 节):声明后,动作执行结果写入
|
|
25
|
+
* 表达式作用域 `actions.<id>`(成功时为结果数据,失败时为
|
|
26
|
+
* `{ ok: false, error }`),供后续动作引用,如
|
|
27
|
+
* `${actions.editResult.success}`;缺省时行为不变
|
|
28
|
+
*/
|
|
29
|
+
id?: string;
|
|
30
|
+
/** 动作名,对应 Action Registry 中的注册名,如 "navigate"、"risk.calculate" */
|
|
31
|
+
action: string;
|
|
32
|
+
/** 动作参数,值可含表达式字符串 */
|
|
33
|
+
params?: Record<string, unknown>;
|
|
34
|
+
/** 条件执行 */
|
|
35
|
+
condition?: ConditionConfig;
|
|
36
|
+
/** 执行权限 */
|
|
37
|
+
permission?: PermissionConfig;
|
|
38
|
+
}
|
|
39
|
+
/** 事件映射:事件名 → 单个或多个动作 */
|
|
40
|
+
type EventMap = Record<string, ActionConfig | ActionConfig[]>;
|
|
41
|
+
/** UI DSL 树基础节点 */
|
|
42
|
+
interface DslNode {
|
|
43
|
+
id?: string;
|
|
44
|
+
/** 组件类型,对应 Component Registry 中的注册名 */
|
|
45
|
+
type: string;
|
|
46
|
+
props?: Record<string, unknown>;
|
|
47
|
+
children?: DslNode[];
|
|
48
|
+
events?: EventMap;
|
|
49
|
+
permission?: PermissionConfig;
|
|
50
|
+
/** 条件渲染 */
|
|
51
|
+
when?: ConditionConfig;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* DSL 文档结构(设计文档第 9、10、37 节)。
|
|
56
|
+
*/
|
|
57
|
+
/** DSL Specification Version 字符串,如 "1.0" */
|
|
58
|
+
type DslVersion = string;
|
|
59
|
+
/** 能力声明:DSL 对 Runtime / 组件版本的要求(第 37 节) */
|
|
60
|
+
interface RequiresConfig {
|
|
61
|
+
/** Runtime 版本范围(semver range),如 ">=1.0.0" */
|
|
62
|
+
runtime?: string;
|
|
63
|
+
/** 组件 API 版本要求:组件名 → semver range */
|
|
64
|
+
components?: Record<string, string>;
|
|
65
|
+
}
|
|
66
|
+
/** 页面元信息 */
|
|
67
|
+
interface PageMeta {
|
|
68
|
+
id: string;
|
|
69
|
+
title?: string;
|
|
70
|
+
/** 页面类型 / Page Pattern,如 "list"、"detail"、"form" */
|
|
71
|
+
type?: string;
|
|
72
|
+
permission?: PermissionConfig;
|
|
73
|
+
}
|
|
74
|
+
/** DataSource 类型(第 19 节) */
|
|
75
|
+
type DataSourceType = 'http' | 'service' | 'static' | 'computed' | 'resource';
|
|
76
|
+
/** DataSource 配置 */
|
|
77
|
+
interface DataSourceConfig {
|
|
78
|
+
type: DataSourceType;
|
|
79
|
+
/** type 为 service 时的服务名,如 "customer.list" */
|
|
80
|
+
service?: string;
|
|
81
|
+
autoLoad?: boolean;
|
|
82
|
+
/** 调用参数,值可含表达式字符串 */
|
|
83
|
+
params?: Record<string, unknown>;
|
|
84
|
+
/** type 为 http 时的请求描述 */
|
|
85
|
+
request?: {
|
|
86
|
+
method?: string;
|
|
87
|
+
url: string;
|
|
88
|
+
params?: Record<string, unknown>;
|
|
89
|
+
headers?: Record<string, string>;
|
|
90
|
+
body?: unknown;
|
|
91
|
+
};
|
|
92
|
+
/** 分页参数映射,值可含表达式字符串 */
|
|
93
|
+
pagination?: {
|
|
94
|
+
page?: unknown;
|
|
95
|
+
pageSize?: unknown;
|
|
96
|
+
};
|
|
97
|
+
/** type 为 static 时的静态数据 */
|
|
98
|
+
value?: unknown;
|
|
99
|
+
}
|
|
100
|
+
/** 业务语义生命周期事件名(第 20 节) */
|
|
101
|
+
type LifecycleEventName = 'onInit' | 'onLoad' | 'onReady' | 'onShow' | 'onHide' | 'onDestroy';
|
|
102
|
+
/** 生命周期配置:事件名 → 单个或多个动作 */
|
|
103
|
+
type LifecycleConfig = Partial<Record<LifecycleEventName, ActionConfig | ActionConfig[]>>;
|
|
104
|
+
/** Page DSL 根结构(第 10 节) */
|
|
105
|
+
interface PageDsl {
|
|
106
|
+
/** DSL Specification Version,必填 */
|
|
107
|
+
dsl: DslVersion;
|
|
108
|
+
requires?: RequiresConfig;
|
|
109
|
+
page: PageMeta;
|
|
110
|
+
/** 页面初始状态 */
|
|
111
|
+
state?: Record<string, unknown>;
|
|
112
|
+
dataSources?: Record<string, DataSourceConfig>;
|
|
113
|
+
lifecycle?: LifecycleConfig;
|
|
114
|
+
/** UI 树根 */
|
|
115
|
+
body?: DslNode;
|
|
116
|
+
}
|
|
117
|
+
/** Fragment DSL(第 50 节):可复用的 UI 片段 */
|
|
118
|
+
interface FragmentDsl {
|
|
119
|
+
dsl: DslVersion;
|
|
120
|
+
requires?: RequiresConfig;
|
|
121
|
+
fragment: {
|
|
122
|
+
id: string;
|
|
123
|
+
title?: string;
|
|
124
|
+
};
|
|
125
|
+
body: DslNode;
|
|
126
|
+
}
|
|
127
|
+
/** Application DSL(第 9.1 节) */
|
|
128
|
+
interface ApplicationDsl {
|
|
129
|
+
dsl: DslVersion;
|
|
130
|
+
requires?: RequiresConfig;
|
|
131
|
+
application: {
|
|
132
|
+
id: string;
|
|
133
|
+
layout?: {
|
|
134
|
+
type: string;
|
|
135
|
+
};
|
|
136
|
+
theme?: {
|
|
137
|
+
name: string;
|
|
138
|
+
};
|
|
139
|
+
navigation?: {
|
|
140
|
+
mode: string;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 运行时作用域层级(设计文档第 15 节),自外向内排列。
|
|
147
|
+
*/
|
|
148
|
+
declare const RUNTIME_SCOPES: readonly ["global", "app", "page", "overlay", "form", "component", "row", "event"];
|
|
149
|
+
type RuntimeScope = (typeof RUNTIME_SCOPES)[number];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 通用 Registry(设计文档第 3.3、12、17、18 节)。
|
|
153
|
+
*
|
|
154
|
+
* Component / Action / Service / Capability / Expression Function 等
|
|
155
|
+
* 具体 Registry 的共同基座:名称 → 条目。
|
|
156
|
+
*/
|
|
157
|
+
declare class Registry<T> {
|
|
158
|
+
private readonly label;
|
|
159
|
+
private readonly items;
|
|
160
|
+
/**
|
|
161
|
+
* @param label 用于错误信息中标识 Registry 用途,如 "ComponentRegistry"
|
|
162
|
+
*/
|
|
163
|
+
constructor(label?: string);
|
|
164
|
+
/** 注册条目;重复注册同名条目抛出错误 */
|
|
165
|
+
register(name: string, item: T): void;
|
|
166
|
+
/** 获取条目;未注册时返回 undefined,不抛错 */
|
|
167
|
+
get(name: string): T | undefined;
|
|
168
|
+
has(name: string): boolean;
|
|
169
|
+
/** 注销条目;返回是否存在并被移除 */
|
|
170
|
+
unregister(name: string): boolean;
|
|
171
|
+
/** 全部已注册名称 */
|
|
172
|
+
list(): string[];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 国际化(i18n)基础类型定义与翻译工具。
|
|
177
|
+
*
|
|
178
|
+
* 翻译模型为扁平键值对:key 支持点路径嵌套(如 `menu.home`),
|
|
179
|
+
* value 为对应语言的文案,可包含 `{{name}}` 占位符用于参数插值。
|
|
180
|
+
*/
|
|
181
|
+
/** 语言代码,如 'zh-CN'、'en-US' */
|
|
182
|
+
type Locale = string;
|
|
183
|
+
/** 翻译消息表:key → 文案,或 locale → key → 文案 */
|
|
184
|
+
type I18nMessages = Record<string, string | Record<string, string>>;
|
|
185
|
+
/** 参数插值值 */
|
|
186
|
+
type I18nParams = Record<string, unknown>;
|
|
187
|
+
/**
|
|
188
|
+
* i18n 上下文:暴露给 RuntimeContext 与表达式引擎。
|
|
189
|
+
* `t` 为翻译函数,支持参数插值与回退。
|
|
190
|
+
*/
|
|
191
|
+
interface I18nContext {
|
|
192
|
+
/** 当前生效的语言代码 */
|
|
193
|
+
locale: Locale;
|
|
194
|
+
/** 回退语言代码(缺省 'zh-CN') */
|
|
195
|
+
defaultLocale: Locale;
|
|
196
|
+
/** 翻译函数 */
|
|
197
|
+
t(key: string, params?: I18nParams): string;
|
|
198
|
+
}
|
|
199
|
+
/** 创建 i18n 上下文时的配置 */
|
|
200
|
+
interface CreateI18nOptions {
|
|
201
|
+
locale?: Locale;
|
|
202
|
+
defaultLocale?: Locale;
|
|
203
|
+
messages?: I18nMessages;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 创建翻译函数。
|
|
207
|
+
*
|
|
208
|
+
* messages 支持两种结构:
|
|
209
|
+
* - 扁平:`{ 'menu.home': '首页' }`(当前 locale 直接使用)
|
|
210
|
+
* - 按语言:`{ 'zh-CN': { 'menu.home': '首页' }, 'en-US': { 'menu.home': 'Home' } }`
|
|
211
|
+
*
|
|
212
|
+
* 回退顺序:当前 locale → defaultLocale → key 本身。
|
|
213
|
+
* 支持 `{{name}}` 参数插值。
|
|
214
|
+
*/
|
|
215
|
+
declare function createI18nContext(options?: CreateI18nOptions): I18nContext;
|
|
216
|
+
|
|
217
|
+
export { type ActionConfig, type ApplicationDsl, type ConditionConfig, type CreateI18nOptions, type DataSourceConfig, type DataSourceType, type DslNode, type DslVersion, type EventMap, type ExpressionString, type FragmentDsl, type I18nContext, type I18nMessages, type I18nParams, type LifecycleConfig, type LifecycleEventName, type Locale, type PageDsl, type PageMeta, type PermissionConfig, type PermissionDeniedStrategy, RUNTIME_SCOPES, Registry, type RequiresConfig, type RuntimeScope, createI18nContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/scope.ts
|
|
2
|
+
var RUNTIME_SCOPES = [
|
|
3
|
+
"global",
|
|
4
|
+
"app",
|
|
5
|
+
"page",
|
|
6
|
+
"overlay",
|
|
7
|
+
"form",
|
|
8
|
+
"component",
|
|
9
|
+
"row",
|
|
10
|
+
"event"
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
// src/registry.ts
|
|
14
|
+
var Registry = class {
|
|
15
|
+
/**
|
|
16
|
+
* @param label 用于错误信息中标识 Registry 用途,如 "ComponentRegistry"
|
|
17
|
+
*/
|
|
18
|
+
constructor(label = "Registry") {
|
|
19
|
+
this.label = label;
|
|
20
|
+
this.items = /* @__PURE__ */ new Map();
|
|
21
|
+
}
|
|
22
|
+
/** 注册条目;重复注册同名条目抛出错误 */
|
|
23
|
+
register(name, item) {
|
|
24
|
+
if (this.items.has(name)) {
|
|
25
|
+
throw new Error(`[${this.label}] duplicate registration: "${name}"`);
|
|
26
|
+
}
|
|
27
|
+
this.items.set(name, item);
|
|
28
|
+
}
|
|
29
|
+
/** 获取条目;未注册时返回 undefined,不抛错 */
|
|
30
|
+
get(name) {
|
|
31
|
+
return this.items.get(name);
|
|
32
|
+
}
|
|
33
|
+
has(name) {
|
|
34
|
+
return this.items.has(name);
|
|
35
|
+
}
|
|
36
|
+
/** 注销条目;返回是否存在并被移除 */
|
|
37
|
+
unregister(name) {
|
|
38
|
+
return this.items.delete(name);
|
|
39
|
+
}
|
|
40
|
+
/** 全部已注册名称 */
|
|
41
|
+
list() {
|
|
42
|
+
return [...this.items.keys()];
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/i18n.ts
|
|
47
|
+
function interpolate(template, params) {
|
|
48
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, name) => {
|
|
49
|
+
const value = params[name];
|
|
50
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function isStringMap(value) {
|
|
54
|
+
if (typeof value !== "object" || value === null) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
58
|
+
}
|
|
59
|
+
function createI18nContext(options = {}) {
|
|
60
|
+
const locale = options.locale ?? "zh-CN";
|
|
61
|
+
const defaultLocale = options.defaultLocale ?? "zh-CN";
|
|
62
|
+
const messages = options.messages ?? {};
|
|
63
|
+
const t = (key, params) => {
|
|
64
|
+
let template;
|
|
65
|
+
const currentLocaleMessages = messages[locale];
|
|
66
|
+
if (isStringMap(currentLocaleMessages)) {
|
|
67
|
+
const value = currentLocaleMessages[key];
|
|
68
|
+
if (typeof value === "string") {
|
|
69
|
+
template = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (template === void 0) {
|
|
73
|
+
const flatValue = messages[key];
|
|
74
|
+
if (typeof flatValue === "string") {
|
|
75
|
+
template = flatValue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (template === void 0 && locale !== defaultLocale) {
|
|
79
|
+
const defaultLocaleMessages = messages[defaultLocale];
|
|
80
|
+
if (isStringMap(defaultLocaleMessages)) {
|
|
81
|
+
const value = defaultLocaleMessages[key];
|
|
82
|
+
if (typeof value === "string") {
|
|
83
|
+
template = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (template === void 0) {
|
|
88
|
+
return key;
|
|
89
|
+
}
|
|
90
|
+
return params ? interpolate(template, params) : template;
|
|
91
|
+
};
|
|
92
|
+
return { locale, defaultLocale, t };
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
RUNTIME_SCOPES,
|
|
96
|
+
Registry,
|
|
97
|
+
createI18nContext
|
|
98
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pactor-app/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pactor core: DSL protocol types and generic Registry (React-free, zero dependencies)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pactor",
|
|
7
|
+
"dsl",
|
|
8
|
+
"server-driven-ui",
|
|
9
|
+
"dynamic-ui",
|
|
10
|
+
"protocol",
|
|
11
|
+
"registry"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/426-330/pactor.git",
|
|
17
|
+
"directory": "packages/core"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/426-330/pactor/tree/main/packages/core",
|
|
20
|
+
"bugs": "https://github.com/426-330/pactor/issues",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"default": "./dist/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"typecheck": "tsc --noEmit"
|
|
51
|
+
}
|
|
52
|
+
}
|