@tmagic/editor 1.8.0-beta.22 → 1.8.0-beta.23
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/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/initService.js +2 -1
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/services/codeBlock.js +4 -2
- package/dist/es/utils/dep/collect-worker-client.js +67 -27
- package/dist/es/utils/dep/worker.js +1 -1
- package/dist/es/utils/type-match-rules.js +7 -27
- package/dist/tmagic-editor.umd.cjs +294 -265
- package/package.json +12 -12
- package/src/utils/dep/collect-worker-client.ts +97 -31
- package/src/utils/type-match-rules.ts +4 -30
- package/types/index.d.ts +86 -153
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.23",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -54,29 +54,29 @@
|
|
|
54
54
|
"events": "^3.3.0",
|
|
55
55
|
"gesto": "^1.19.4",
|
|
56
56
|
"keycon": "^1.4.0",
|
|
57
|
-
"lodash-es": "^4.
|
|
57
|
+
"lodash-es": "^4.18.1",
|
|
58
58
|
"moveable": "^0.53.0",
|
|
59
|
-
"serialize-javascript": "^7.0.
|
|
60
|
-
"sortablejs": "^1.15.
|
|
61
|
-
"@tmagic/design": "1.8.0-beta.
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/utils": "1.8.0-beta.
|
|
64
|
-
"@tmagic/table": "1.8.0-beta.
|
|
65
|
-
"@tmagic/
|
|
59
|
+
"serialize-javascript": "^7.0.7",
|
|
60
|
+
"sortablejs": "^1.15.7",
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.23",
|
|
62
|
+
"@tmagic/stage": "1.8.0-beta.23",
|
|
63
|
+
"@tmagic/utils": "1.8.0-beta.23",
|
|
64
|
+
"@tmagic/table": "1.8.0-beta.23",
|
|
65
|
+
"@tmagic/form": "1.8.0-beta.23"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/events": "^3.0.3",
|
|
69
|
-
"@types/lodash-es": "^4.17.
|
|
69
|
+
"@types/lodash-es": "^4.17.12",
|
|
70
70
|
"@types/serialize-javascript": "^5.0.4",
|
|
71
71
|
"@types/sortablejs": "^1.15.9",
|
|
72
|
-
"@vue/test-utils": "^2.4.
|
|
72
|
+
"@vue/test-utils": "^2.4.11"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"monaco-editor": "^0.55.1 ",
|
|
76
76
|
"type-fest": "^5.2.0",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
78
|
"vue": "^3.5.40",
|
|
79
|
-
"@tmagic/core": "1.8.0-beta.
|
|
79
|
+
"@tmagic/core": "1.8.0-beta.23"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
|
@@ -12,6 +12,14 @@ export interface CollectWorkerResult {
|
|
|
12
12
|
nodeIds: Id[];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface CollectTask {
|
|
16
|
+
id: number;
|
|
17
|
+
createRequest: (id: number) => CollectWorkerRequest;
|
|
18
|
+
resolve: (response: CollectWorkerResponse | null) => void;
|
|
19
|
+
/** 已被 abort 丢弃:调用方已按 null 结算,结果回来后不再回传 */
|
|
20
|
+
discarded: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* 依赖收集 worker 的主线程客户端
|
|
17
25
|
*
|
|
@@ -20,13 +28,20 @@ export interface CollectWorkerResult {
|
|
|
20
28
|
*
|
|
21
29
|
* 全量收集与增量收集共用一个常驻 worker:增量收集调用频繁(每次节点更新都会触发),
|
|
22
30
|
* 按次创建 worker 的启动开销无法接受,也容易漏掉销毁导致线程泄漏。
|
|
31
|
+
*
|
|
32
|
+
* 请求在主线程侧排队后逐个投递(worker 本身也是串行处理),这样 abort 时能直接丢掉尚未投递的请求,
|
|
33
|
+
* 无需销毁线程:worker 只在真正异常或销毁时才重建。
|
|
23
34
|
*/
|
|
24
35
|
export class CollectWorkerClient {
|
|
25
36
|
private worker: Worker | null = null;
|
|
26
37
|
|
|
27
38
|
private seed = 0;
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
/** 等待投递给 worker 的请求 */
|
|
41
|
+
private queue: CollectTask[] = [];
|
|
42
|
+
|
|
43
|
+
/** 已投递、等待 worker 响应的请求,worker 串行处理,同一时刻最多一个 */
|
|
44
|
+
private inflight: CollectTask | null = null;
|
|
30
45
|
|
|
31
46
|
public get isSupported() {
|
|
32
47
|
return typeof Worker !== 'undefined';
|
|
@@ -53,24 +68,37 @@ export class CollectWorkerClient {
|
|
|
53
68
|
public terminate() {
|
|
54
69
|
this.worker?.terminate();
|
|
55
70
|
this.worker = null;
|
|
56
|
-
this.
|
|
71
|
+
this.settleAll();
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
75
|
+
* 丢弃在途请求,但保留常驻 worker
|
|
76
|
+
*
|
|
77
|
+
* clearIdleTasks / reset 中断收集时调用。这里不能销毁 worker:worker 重建要重新加载并启动整份收集逻辑,
|
|
78
|
+
* 而 clearIdleTasks / reset 触发频繁(root 更新、数据源变更、历史回滚等),一旦下一次 abort 落在
|
|
79
|
+
* 上一个 worker 还没启动完的窗口内,就会陷入「加载中被销毁 → 重建 → 又被销毁」的循环,
|
|
80
|
+
* 表现为 worker 反复加载且依赖始终收集不完。
|
|
81
|
+
*
|
|
82
|
+
* 因此 abort 只做「不要这些结果」:尚未投递的请求直接丢掉,已投递的那一个标记为丢弃,
|
|
83
|
+
* 结果回来后忽略(不写回主线程),worker 继续复用。
|
|
62
84
|
*/
|
|
63
85
|
public abort() {
|
|
64
|
-
this
|
|
86
|
+
const { queue } = this;
|
|
87
|
+
this.queue = [];
|
|
88
|
+
|
|
89
|
+
for (const task of queue) {
|
|
90
|
+
task.resolve(null);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 已投递的请求无法从外部打断(worker 忙时收不到取消消息),只能丢弃其结果
|
|
94
|
+
if (this.inflight && !this.inflight.discarded) {
|
|
95
|
+
this.inflight.discarded = true;
|
|
96
|
+
this.inflight.resolve(null);
|
|
97
|
+
}
|
|
65
98
|
}
|
|
66
99
|
|
|
67
|
-
/**
|
|
68
|
-
* worker 按收到的顺序逐个处理请求,因此请求间不会互相打断,用 id 把结果分发回各自的调用方
|
|
69
|
-
*/
|
|
70
100
|
private request(createRequest: (id: number) => CollectWorkerRequest): Promise<CollectWorkerResponse | null> {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!worker) {
|
|
101
|
+
if (!this.isSupported) {
|
|
74
102
|
return Promise.resolve(null);
|
|
75
103
|
}
|
|
76
104
|
|
|
@@ -78,19 +106,44 @@ export class CollectWorkerClient {
|
|
|
78
106
|
const id = this.seed;
|
|
79
107
|
|
|
80
108
|
return new Promise<CollectWorkerResponse | null>((resolve) => {
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
// 节点配置中可能存在函数等无法结构化克隆的值,需要先序列化再传递
|
|
85
|
-
worker.postMessage(createRequest(id));
|
|
86
|
-
} catch (e) {
|
|
87
|
-
error('magic editor: 依赖收集 worker 通信失败', e);
|
|
88
|
-
this.pending.delete(id);
|
|
89
|
-
resolve(null);
|
|
90
|
-
}
|
|
109
|
+
this.queue.push({ id, createRequest, resolve, discarded: false });
|
|
110
|
+
this.flush();
|
|
91
111
|
});
|
|
92
112
|
}
|
|
93
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 投递队首请求,worker 串行处理,因此同一时刻只投递一个,用 id 把结果分发回各自的调用方
|
|
116
|
+
*/
|
|
117
|
+
private flush() {
|
|
118
|
+
if (this.inflight || !this.queue.length) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const worker = this.getWorker();
|
|
123
|
+
|
|
124
|
+
if (!worker) {
|
|
125
|
+
this.settleAll();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const task = this.queue.shift()!;
|
|
130
|
+
this.inflight = task;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// 节点配置中可能存在函数等无法结构化克隆的值,需要先序列化再传递
|
|
134
|
+
worker.postMessage(task.createRequest(task.id));
|
|
135
|
+
} catch (e) {
|
|
136
|
+
error('magic editor: 依赖收集 worker 通信失败', e);
|
|
137
|
+
this.inflight = null;
|
|
138
|
+
|
|
139
|
+
if (!task.discarded) {
|
|
140
|
+
task.resolve(null);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.flush();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
94
147
|
private getWorker() {
|
|
95
148
|
if (!this.isSupported) {
|
|
96
149
|
return null;
|
|
@@ -122,28 +175,41 @@ export class CollectWorkerClient {
|
|
|
122
175
|
}
|
|
123
176
|
|
|
124
177
|
private handleResponse(response: CollectWorkerResponse) {
|
|
125
|
-
const
|
|
178
|
+
const task = this.inflight;
|
|
126
179
|
|
|
127
|
-
if (
|
|
180
|
+
if (task?.id !== response?.id) {
|
|
128
181
|
return;
|
|
129
182
|
}
|
|
130
183
|
|
|
131
|
-
this.
|
|
132
|
-
|
|
184
|
+
this.inflight = null;
|
|
185
|
+
|
|
186
|
+
// 被 abort 丢弃的请求已按 null 结算,结果不能再写回主线程
|
|
187
|
+
if (!task.discarded) {
|
|
188
|
+
task.resolve(response.failed ? null : response);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.flush();
|
|
133
192
|
}
|
|
134
193
|
|
|
135
194
|
private handleFatalError() {
|
|
136
195
|
this.worker?.terminate();
|
|
137
196
|
this.worker = null;
|
|
138
|
-
this.
|
|
197
|
+
this.settleAll();
|
|
139
198
|
}
|
|
140
199
|
|
|
141
|
-
private
|
|
142
|
-
const
|
|
143
|
-
this.
|
|
200
|
+
private settleAll() {
|
|
201
|
+
const tasks = this.queue;
|
|
202
|
+
this.queue = [];
|
|
144
203
|
|
|
145
|
-
|
|
146
|
-
|
|
204
|
+
if (this.inflight) {
|
|
205
|
+
tasks.push(this.inflight);
|
|
206
|
+
this.inflight = null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const task of tasks) {
|
|
210
|
+
if (!task.discarded) {
|
|
211
|
+
task.resolve(null);
|
|
212
|
+
}
|
|
147
213
|
}
|
|
148
214
|
}
|
|
149
215
|
}
|
|
@@ -20,7 +20,7 @@ import type { DataSourceFieldType, DataSourceSchema, Id } from '@tmagic/core';
|
|
|
20
20
|
import { NodeType } from '@tmagic/core';
|
|
21
21
|
import { appendValidateSuggestion } from '@tmagic/design';
|
|
22
22
|
import type { TypeMatchValidateContext, TypeMatchValidator } from '@tmagic/form';
|
|
23
|
-
import { validateTypeMatch } from '@tmagic/form';
|
|
23
|
+
import { MAX_SUGGESTION_OPTIONS, optionSuggestion, stringifyExampleValue, validateTypeMatch } from '@tmagic/form';
|
|
24
24
|
import {
|
|
25
25
|
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
|
|
26
26
|
DATA_SOURCE_SET_DATA_METHOD_NAME,
|
|
@@ -41,35 +41,9 @@ import {
|
|
|
41
41
|
numberOptions,
|
|
42
42
|
} from '@editor/utils/props';
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const stringifyExampleValue = (value: any): string => {
|
|
48
|
-
if (typeof value === 'string') return `"${value}"`;
|
|
49
|
-
if (value === null || value === undefined) return String(value);
|
|
50
|
-
if (typeof value === 'object') {
|
|
51
|
-
try {
|
|
52
|
-
return JSON.stringify(value);
|
|
53
|
-
} catch {
|
|
54
|
-
return String(value);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return String(value);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// 参考建议中最多展示的可选值个数,超出以「等」省略。
|
|
61
|
-
const MAX_SUGGESTION_OPTIONS = 20;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 生成「请使用以下某一个值:xxx;xxx」形式的参考建议;无可选值时返回空字符串(不追加建议)。
|
|
65
|
-
*/
|
|
66
|
-
const listSuggestion = (values: any[]): string => {
|
|
67
|
-
const list = values.filter((item) => typeof item !== 'undefined' && item !== null && item !== '');
|
|
68
|
-
if (!list.length) return '';
|
|
69
|
-
const shown = list.slice(0, MAX_SUGGESTION_OPTIONS).map(stringifyExampleValue);
|
|
70
|
-
const suffix = list.length > MAX_SUGGESTION_OPTIONS ? ' 等' : '';
|
|
71
|
-
return `请使用以下某一个值:${shown.join(';')}${suffix}`;
|
|
72
|
-
};
|
|
44
|
+
/** 复用 form 的可选项建议文案;额外过滤 null / 空串,避免业务侧无效枚举污染提示。 */
|
|
45
|
+
const listSuggestion = (values: any[]): string =>
|
|
46
|
+
optionSuggestion(values.filter((item) => item !== null && item !== ''));
|
|
73
47
|
|
|
74
48
|
/**
|
|
75
49
|
* 列出当前可用数据源 id 的参考建议。
|