@lokvis/runtime 0.2.2 → 0.4.1
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/asset-store.d.ts +13 -0
- package/dist/asset-store.d.ts.map +1 -1
- package/dist/asset-store.js +15 -2
- package/dist/asset-store.js.map +1 -1
- package/dist/batch-processor.d.ts +32 -161
- package/dist/batch-processor.d.ts.map +1 -1
- package/dist/batch-processor.js +107 -333
- package/dist/batch-processor.js.map +1 -1
- package/dist/batch-progress.d.ts +94 -0
- package/dist/batch-progress.d.ts.map +1 -0
- package/dist/batch-progress.js +128 -0
- package/dist/batch-progress.js.map +1 -0
- package/dist/batch-scheduler.d.ts +70 -0
- package/dist/batch-scheduler.d.ts.map +1 -0
- package/dist/batch-scheduler.js +147 -0
- package/dist/batch-scheduler.js.map +1 -0
- package/dist/browser-detect.d.ts +115 -0
- package/dist/browser-detect.d.ts.map +1 -0
- package/dist/browser-detect.js +160 -0
- package/dist/browser-detect.js.map +1 -0
- package/dist/concurrency-controller.d.ts +31 -0
- package/dist/concurrency-controller.d.ts.map +1 -0
- package/dist/concurrency-controller.js +33 -0
- package/dist/concurrency-controller.js.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +74 -0
- package/dist/errors.js.map +1 -0
- package/dist/executor.d.ts +9 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +87 -48
- package/dist/executor.js.map +1 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +3 -2
- package/dist/history.js.map +1 -1
- package/dist/idb-asset-store.d.ts.map +1 -1
- package/dist/idb-asset-store.js +6 -1
- package/dist/idb-asset-store.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -15
- package/dist/index.js.map +1 -1
- package/dist/managers/asset-manager.d.ts +114 -0
- package/dist/managers/asset-manager.d.ts.map +1 -0
- package/dist/managers/asset-manager.js +238 -0
- package/dist/managers/asset-manager.js.map +1 -0
- package/dist/managers/history-manager.d.ts +100 -0
- package/dist/managers/history-manager.d.ts.map +1 -0
- package/dist/managers/history-manager.js +296 -0
- package/dist/managers/history-manager.js.map +1 -0
- package/dist/managers/mcp-manifest-builder.d.ts +18 -0
- package/dist/managers/mcp-manifest-builder.d.ts.map +1 -0
- package/dist/managers/mcp-manifest-builder.js +99 -0
- package/dist/managers/mcp-manifest-builder.js.map +1 -0
- package/dist/managers/quota-manager.d.ts +42 -0
- package/dist/managers/quota-manager.d.ts.map +1 -0
- package/dist/managers/quota-manager.js +126 -0
- package/dist/managers/quota-manager.js.map +1 -0
- package/dist/managers/workflow-coordinator.d.ts +62 -0
- package/dist/managers/workflow-coordinator.d.ts.map +1 -0
- package/dist/managers/workflow-coordinator.js +125 -0
- package/dist/managers/workflow-coordinator.js.map +1 -0
- package/dist/opfs-asset-store.d.ts.map +1 -1
- package/dist/opfs-asset-store.js +12 -2
- package/dist/opfs-asset-store.js.map +1 -1
- package/dist/plugin-context.d.ts +35 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugin-context.js +62 -0
- package/dist/plugin-context.js.map +1 -0
- package/dist/plugin-permissions.d.ts +99 -0
- package/dist/plugin-permissions.d.ts.map +1 -0
- package/dist/plugin-permissions.js +205 -0
- package/dist/plugin-permissions.js.map +1 -0
- package/dist/runtime-impl.d.ts +116 -0
- package/dist/runtime-impl.d.ts.map +1 -0
- package/dist/runtime-impl.js +270 -0
- package/dist/runtime-impl.js.map +1 -0
- package/dist/runtime.d.ts +10 -233
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +12 -978
- package/dist/runtime.js.map +1 -1
- package/dist/test-utils/fakes.d.ts +35 -0
- package/dist/test-utils/fakes.d.ts.map +1 -0
- package/dist/test-utils/fakes.js +60 -0
- package/dist/test-utils/fakes.js.map +1 -0
- package/dist/types.d.ts +113 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/worker-host.d.ts.map +1 -1
- package/dist/worker-host.js +32 -9
- package/dist/worker-host.js.map +1 -1
- package/dist/worker-protocol.d.ts +30 -0
- package/dist/worker-protocol.d.ts.map +1 -1
- package/dist/worker-protocol.js +24 -0
- package/dist/worker-protocol.js.map +1 -1
- package/package.json +2 -2
- package/src/asset-store.ts +32 -2
- package/src/batch-processor.ts +133 -510
- package/src/batch-progress.ts +228 -0
- package/src/batch-scheduler.ts +209 -0
- package/src/browser-detect.ts +221 -0
- package/src/concurrency-controller.ts +52 -0
- package/src/errors.ts +82 -0
- package/src/executor.ts +106 -55
- package/src/history.ts +3 -2
- package/src/idb-asset-store.ts +7 -1
- package/src/index.ts +50 -15
- package/src/managers/asset-manager.ts +259 -0
- package/src/managers/history-manager.ts +327 -0
- package/src/managers/mcp-manifest-builder.ts +108 -0
- package/src/managers/quota-manager.ts +149 -0
- package/src/managers/workflow-coordinator.ts +158 -0
- package/src/opfs-asset-store.ts +16 -2
- package/src/plugin-context.ts +87 -0
- package/src/plugin-permissions.ts +228 -0
- package/src/runtime-impl.ts +298 -0
- package/src/runtime.ts +16 -1116
- package/src/test-utils/fakes.ts +72 -0
- package/src/types.ts +116 -0
- package/src/worker-host.ts +35 -7
- package/src/worker-protocol.ts +47 -0
- package/dist/workflow-builder.d.ts +0 -124
- package/dist/workflow-builder.d.ts.map +0 -1
- package/dist/workflow-builder.js +0 -240
- package/dist/workflow-builder.js.map +0 -1
- package/src/workflow-builder.ts +0 -303
package/dist/workflow-builder.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorkflowBuilder - 线性工作流链式构造器(W10.1)
|
|
3
|
-
*
|
|
4
|
-
* 提供链式 API 构造线性 Workflow(第一年不支持 Branch/Loop/Condition/Parallel)。
|
|
5
|
-
*
|
|
6
|
-
* 限制:
|
|
7
|
-
* - 最多 5 步(M1 MVP 约束,免费用户定位,避免复杂度过高)
|
|
8
|
-
* - 节点必须为 transform 类型(load/export 由执行器隐式处理)
|
|
9
|
-
* - 自动生成线性 edges:node[i] → node[i+1]
|
|
10
|
-
*
|
|
11
|
-
* 使用示例:
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const workflow = new WorkflowBuilder('my-workflow', 'Web 优化')
|
|
14
|
-
* .setInput({ type: 'image', multiple: true })
|
|
15
|
-
* .setOutput({ type: 'image', format: 'webp' })
|
|
16
|
-
* .add('image.resize', { width: 1920, fit: 'inside' })
|
|
17
|
-
* .add('image.compress', { format: 'webp', quality: 80 })
|
|
18
|
-
* .add('image.watermark', { text: '@lokvis' })
|
|
19
|
-
* .build();
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* 与 workflow-slice 的 buildLinearWorkflow 区别:
|
|
23
|
-
* - buildLinearWorkflow 是内部工具函数,从 WorkspaceNode[] 构造无校验
|
|
24
|
-
* - WorkflowBuilder 是公开 API,链式调用 + 5 步上限校验 + 元数据完善
|
|
25
|
-
*
|
|
26
|
-
* @module workflow-builder
|
|
27
|
-
*/
|
|
28
|
-
/** 工作流最大节点数(M1 MVP 约束) */
|
|
29
|
-
export const MAX_WORKFLOW_STEPS = 5;
|
|
30
|
-
/**
|
|
31
|
-
* WorkflowBuilder - 链式构造线性 Workflow。
|
|
32
|
-
*
|
|
33
|
-
* 抛错时机:
|
|
34
|
-
* - add() 超过 maxSteps 时立即抛错(避免构造完才发现步数超限)
|
|
35
|
-
* - build() 在节点为空 / 输入输出未设置时抛错
|
|
36
|
-
* - remove()/move() 在节点 id 不存在时静默(链式 API 容错)
|
|
37
|
-
*/
|
|
38
|
-
export class WorkflowBuilder {
|
|
39
|
-
options;
|
|
40
|
-
maxSteps;
|
|
41
|
-
nodes = [];
|
|
42
|
-
input = null;
|
|
43
|
-
output = null;
|
|
44
|
-
nodeCounter = 0;
|
|
45
|
-
constructor(options) {
|
|
46
|
-
if (!options.id)
|
|
47
|
-
throw new Error('WorkflowBuilder: id is required');
|
|
48
|
-
if (!options.name)
|
|
49
|
-
throw new Error('WorkflowBuilder: name is required');
|
|
50
|
-
this.options = options;
|
|
51
|
-
this.maxSteps = options.maxSteps ?? MAX_WORKFLOW_STEPS;
|
|
52
|
-
}
|
|
53
|
-
/** 设置工作流输入定义 */
|
|
54
|
-
setInput(input) {
|
|
55
|
-
this.input = input;
|
|
56
|
-
return this;
|
|
57
|
-
}
|
|
58
|
-
/** 设置工作流输出定义 */
|
|
59
|
-
setOutput(output) {
|
|
60
|
-
this.output = output;
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* 添加一个节点到链尾。
|
|
65
|
-
*
|
|
66
|
-
* @param capability 能力名,如 `image.resize`
|
|
67
|
-
* @param params 能力参数(可选,默认空对象)
|
|
68
|
-
* @param label 节点标签(可选,UI 显示用)
|
|
69
|
-
* @throws Error 当节点数达到 maxSteps 时
|
|
70
|
-
*/
|
|
71
|
-
add(capability, params = {}, label) {
|
|
72
|
-
if (!capability)
|
|
73
|
-
throw new Error('WorkflowBuilder.add: capability is required');
|
|
74
|
-
if (this.nodes.length >= this.maxSteps) {
|
|
75
|
-
throw new Error(`WorkflowBuilder.add: max steps (${this.maxSteps}) reached; ` +
|
|
76
|
-
`cannot add more nodes. Adjust maxSteps option if needed.`);
|
|
77
|
-
}
|
|
78
|
-
this.nodeCounter += 1;
|
|
79
|
-
this.nodes.push({
|
|
80
|
-
id: `${this.options.id}-node-${this.nodeCounter}`,
|
|
81
|
-
capability,
|
|
82
|
-
params,
|
|
83
|
-
label,
|
|
84
|
-
});
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* 按 capability 或节点 id 移除节点。
|
|
89
|
-
* 若有多个同 capability 节点,只移除第一个。
|
|
90
|
-
* 若不存在则静默(链式 API 容错)。
|
|
91
|
-
*
|
|
92
|
-
* @param capabilityOrId 能力名或节点 id
|
|
93
|
-
*/
|
|
94
|
-
remove(capabilityOrId) {
|
|
95
|
-
const idx = this.nodes.findIndex((n) => n.id === capabilityOrId || n.capability === capabilityOrId);
|
|
96
|
-
if (idx >= 0) {
|
|
97
|
-
this.nodes.splice(idx, 1);
|
|
98
|
-
}
|
|
99
|
-
return this;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* 移动节点到新位置(线性链中重排)。
|
|
103
|
-
*
|
|
104
|
-
* @param from 源位置(0-based 索引)
|
|
105
|
-
* @param to 目标位置(0-based 索引,移动后该节点的新位置)
|
|
106
|
-
* @throws Error 当索引越界
|
|
107
|
-
*/
|
|
108
|
-
move(from, to) {
|
|
109
|
-
if (from < 0 || from >= this.nodes.length) {
|
|
110
|
-
throw new Error(`WorkflowBuilder.move: from index ${from} out of range`);
|
|
111
|
-
}
|
|
112
|
-
if (to < 0 || to >= this.nodes.length) {
|
|
113
|
-
throw new Error(`WorkflowBuilder.move: to index ${to} out of range`);
|
|
114
|
-
}
|
|
115
|
-
if (from === to)
|
|
116
|
-
return this;
|
|
117
|
-
const [node] = this.nodes.splice(from, 1);
|
|
118
|
-
// splice(from, 1) 在已校验的 in-range 索引上必返回 1 元素;
|
|
119
|
-
// 此处显式检查以满足 noUncheckedIndexedAccess,并在不变量被打破时报错
|
|
120
|
-
if (!node) {
|
|
121
|
-
throw new Error(`WorkflowBuilder.move: source node at index ${from} missing`);
|
|
122
|
-
}
|
|
123
|
-
this.nodes.splice(to, 0, node);
|
|
124
|
-
return this;
|
|
125
|
-
}
|
|
126
|
-
/** 交换两个节点位置 */
|
|
127
|
-
swap(i, j) {
|
|
128
|
-
if (i < 0 || i >= this.nodes.length || j < 0 || j >= this.nodes.length) {
|
|
129
|
-
throw new Error('WorkflowBuilder.swap: index out of range');
|
|
130
|
-
}
|
|
131
|
-
if (i === j)
|
|
132
|
-
return this;
|
|
133
|
-
const a = this.nodes[i];
|
|
134
|
-
const b = this.nodes[j];
|
|
135
|
-
// 上方范围校验保证 i / j 在界内;显式检查以满足 noUncheckedIndexedAccess
|
|
136
|
-
if (!a || !b) {
|
|
137
|
-
throw new Error('WorkflowBuilder.swap: node missing (invariant violated)');
|
|
138
|
-
}
|
|
139
|
-
this.nodes[i] = b;
|
|
140
|
-
this.nodes[j] = a;
|
|
141
|
-
return this;
|
|
142
|
-
}
|
|
143
|
-
/** 更新指定节点的参数 */
|
|
144
|
-
updateParams(nodeId, params) {
|
|
145
|
-
const node = this.nodes.find((n) => n.id === nodeId);
|
|
146
|
-
if (node) {
|
|
147
|
-
node.params = { ...node.params, ...params };
|
|
148
|
-
}
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
/** 当前节点数 */
|
|
152
|
-
get size() {
|
|
153
|
-
return this.nodes.length;
|
|
154
|
-
}
|
|
155
|
-
/** 是否已达最大节点数 */
|
|
156
|
-
get isFull() {
|
|
157
|
-
return this.nodes.length >= this.maxSteps;
|
|
158
|
-
}
|
|
159
|
-
/** 获取节点的只读副本(用于 UI 预览) */
|
|
160
|
-
getNodes() {
|
|
161
|
-
return this.nodes.map((n) => ({ ...n }));
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* 构造 Workflow 实例。
|
|
165
|
-
*
|
|
166
|
-
* @throws Error 当节点为空 / 输入未设置 / 输出未设置
|
|
167
|
-
*/
|
|
168
|
-
build() {
|
|
169
|
-
if (this.nodes.length === 0) {
|
|
170
|
-
throw new Error('WorkflowBuilder.build: no nodes added');
|
|
171
|
-
}
|
|
172
|
-
if (!this.input) {
|
|
173
|
-
throw new Error('WorkflowBuilder.build: input not set (call setInput first)');
|
|
174
|
-
}
|
|
175
|
-
if (!this.output) {
|
|
176
|
-
throw new Error('WorkflowBuilder.build: output not set (call setOutput first)');
|
|
177
|
-
}
|
|
178
|
-
const workflowNodes = this.nodes.map((n) => ({
|
|
179
|
-
id: n.id,
|
|
180
|
-
type: 'transform',
|
|
181
|
-
capability: n.capability,
|
|
182
|
-
params: n.params,
|
|
183
|
-
label: n.label,
|
|
184
|
-
}));
|
|
185
|
-
const edges = [];
|
|
186
|
-
for (let i = 0; i < workflowNodes.length - 1; i++) {
|
|
187
|
-
const fromNode = workflowNodes[i];
|
|
188
|
-
const toNode = workflowNodes[i + 1];
|
|
189
|
-
// 循环边界 i < length - 1 保证 i 与 i+1 均在界内;显式检查以满足 noUncheckedIndexedAccess
|
|
190
|
-
if (!fromNode || !toNode) {
|
|
191
|
-
throw new Error('WorkflowBuilder.build: edge node missing (invariant violated)');
|
|
192
|
-
}
|
|
193
|
-
edges.push({
|
|
194
|
-
from: fromNode.id,
|
|
195
|
-
to: toNode.id,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
const now = Date.now();
|
|
199
|
-
return {
|
|
200
|
-
$schema: `https://lokvis.dev/schemas/workflow.json`,
|
|
201
|
-
id: this.options.id,
|
|
202
|
-
version: '1.0.0',
|
|
203
|
-
name: this.options.name,
|
|
204
|
-
description: this.options.description ?? '',
|
|
205
|
-
author: this.options.author ?? { id: 'local', name: 'Local User' },
|
|
206
|
-
category: this.options.category ?? 'image',
|
|
207
|
-
tags: this.options.tags ?? [],
|
|
208
|
-
nodes: workflowNodes,
|
|
209
|
-
edges,
|
|
210
|
-
inputs: this.input,
|
|
211
|
-
outputs: this.output,
|
|
212
|
-
official: this.options.official ?? false,
|
|
213
|
-
createdAt: now,
|
|
214
|
-
updatedAt: now,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/** 从 Workflow 反向构造 Builder(用于编辑已有工作流) */
|
|
219
|
-
export function workflowToBuilder(workflow, maxSteps) {
|
|
220
|
-
const builder = new WorkflowBuilder({
|
|
221
|
-
id: workflow.id,
|
|
222
|
-
name: workflow.name,
|
|
223
|
-
description: workflow.description,
|
|
224
|
-
author: workflow.author,
|
|
225
|
-
category: workflow.category,
|
|
226
|
-
tags: workflow.tags,
|
|
227
|
-
official: workflow.official,
|
|
228
|
-
maxSteps,
|
|
229
|
-
});
|
|
230
|
-
builder.setInput(workflow.inputs);
|
|
231
|
-
builder.setOutput(workflow.outputs);
|
|
232
|
-
// 按拓扑顺序添加节点(线性链 nodes 数组即顺序)
|
|
233
|
-
for (const node of workflow.nodes) {
|
|
234
|
-
if (node.type === 'transform' && node.capability) {
|
|
235
|
-
builder.add(node.capability, node.params ?? {}, node.label);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return builder;
|
|
239
|
-
}
|
|
240
|
-
//# sourceMappingURL=workflow-builder.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-builder.js","sourceRoot":"","sources":["../src/workflow-builder.ts"],"names":[],"mappings":"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"}
|
package/src/workflow-builder.ts
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorkflowBuilder - 线性工作流链式构造器(W10.1)
|
|
3
|
-
*
|
|
4
|
-
* 提供链式 API 构造线性 Workflow(第一年不支持 Branch/Loop/Condition/Parallel)。
|
|
5
|
-
*
|
|
6
|
-
* 限制:
|
|
7
|
-
* - 最多 5 步(M1 MVP 约束,免费用户定位,避免复杂度过高)
|
|
8
|
-
* - 节点必须为 transform 类型(load/export 由执行器隐式处理)
|
|
9
|
-
* - 自动生成线性 edges:node[i] → node[i+1]
|
|
10
|
-
*
|
|
11
|
-
* 使用示例:
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const workflow = new WorkflowBuilder('my-workflow', 'Web 优化')
|
|
14
|
-
* .setInput({ type: 'image', multiple: true })
|
|
15
|
-
* .setOutput({ type: 'image', format: 'webp' })
|
|
16
|
-
* .add('image.resize', { width: 1920, fit: 'inside' })
|
|
17
|
-
* .add('image.compress', { format: 'webp', quality: 80 })
|
|
18
|
-
* .add('image.watermark', { text: '@lokvis' })
|
|
19
|
-
* .build();
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* 与 workflow-slice 的 buildLinearWorkflow 区别:
|
|
23
|
-
* - buildLinearWorkflow 是内部工具函数,从 WorkspaceNode[] 构造无校验
|
|
24
|
-
* - WorkflowBuilder 是公开 API,链式调用 + 5 步上限校验 + 元数据完善
|
|
25
|
-
*
|
|
26
|
-
* @module workflow-builder
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import type {
|
|
30
|
-
AssetType,
|
|
31
|
-
Workflow,
|
|
32
|
-
WorkflowAuthor,
|
|
33
|
-
WorkflowCategory,
|
|
34
|
-
WorkflowEdge,
|
|
35
|
-
WorkflowInput,
|
|
36
|
-
WorkflowNode,
|
|
37
|
-
WorkflowOutput,
|
|
38
|
-
} from '@lokvis/schema';
|
|
39
|
-
|
|
40
|
-
/** 工作流最大节点数(M1 MVP 约束) */
|
|
41
|
-
export const MAX_WORKFLOW_STEPS = 5;
|
|
42
|
-
|
|
43
|
-
/** Builder 内部节点结构(构造中,与 WorkflowNode 略有差异:label 可独立设置) */
|
|
44
|
-
interface BuilderNode {
|
|
45
|
-
/** 内部生成的节点 id(builder 内部唯一) */
|
|
46
|
-
id: string;
|
|
47
|
-
capability: string;
|
|
48
|
-
params: Record<string, unknown>;
|
|
49
|
-
label?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** WorkflowBuilder 构造选项 */
|
|
53
|
-
export interface WorkflowBuilderOptions {
|
|
54
|
-
/** 工作流 ID(必填,需全局唯一) */
|
|
55
|
-
id: string;
|
|
56
|
-
/** 工作流名称 */
|
|
57
|
-
name: string;
|
|
58
|
-
/** 工作流描述(可选,默认为空字符串) */
|
|
59
|
-
description?: string;
|
|
60
|
-
/** 作者信息(可选,默认本地用户) */
|
|
61
|
-
author?: WorkflowAuthor;
|
|
62
|
-
/** 分类(可选,默认 'image') */
|
|
63
|
-
category?: WorkflowCategory;
|
|
64
|
-
/** 标签(可选) */
|
|
65
|
-
tags?: string[];
|
|
66
|
-
/** 是否为官方工作流(可选,默认 false) */
|
|
67
|
-
official?: boolean;
|
|
68
|
-
/** 最大节点数(可选,默认 MAX_WORKFLOW_STEPS;测试或特殊场景可放宽) */
|
|
69
|
-
maxSteps?: number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* WorkflowBuilder - 链式构造线性 Workflow。
|
|
74
|
-
*
|
|
75
|
-
* 抛错时机:
|
|
76
|
-
* - add() 超过 maxSteps 时立即抛错(避免构造完才发现步数超限)
|
|
77
|
-
* - build() 在节点为空 / 输入输出未设置时抛错
|
|
78
|
-
* - remove()/move() 在节点 id 不存在时静默(链式 API 容错)
|
|
79
|
-
*/
|
|
80
|
-
export class WorkflowBuilder {
|
|
81
|
-
private readonly options: WorkflowBuilderOptions;
|
|
82
|
-
private readonly maxSteps: number;
|
|
83
|
-
private nodes: BuilderNode[] = [];
|
|
84
|
-
private input: WorkflowInput | null = null;
|
|
85
|
-
private output: WorkflowOutput | null = null;
|
|
86
|
-
private nodeCounter = 0;
|
|
87
|
-
|
|
88
|
-
constructor(options: WorkflowBuilderOptions) {
|
|
89
|
-
if (!options.id) throw new Error('WorkflowBuilder: id is required');
|
|
90
|
-
if (!options.name) throw new Error('WorkflowBuilder: name is required');
|
|
91
|
-
this.options = options;
|
|
92
|
-
this.maxSteps = options.maxSteps ?? MAX_WORKFLOW_STEPS;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** 设置工作流输入定义 */
|
|
96
|
-
setInput(input: WorkflowInput): this {
|
|
97
|
-
this.input = input;
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** 设置工作流输出定义 */
|
|
102
|
-
setOutput(output: WorkflowOutput): this {
|
|
103
|
-
this.output = output;
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 添加一个节点到链尾。
|
|
109
|
-
*
|
|
110
|
-
* @param capability 能力名,如 `image.resize`
|
|
111
|
-
* @param params 能力参数(可选,默认空对象)
|
|
112
|
-
* @param label 节点标签(可选,UI 显示用)
|
|
113
|
-
* @throws Error 当节点数达到 maxSteps 时
|
|
114
|
-
*/
|
|
115
|
-
add(
|
|
116
|
-
capability: string,
|
|
117
|
-
params: Record<string, unknown> = {},
|
|
118
|
-
label?: string
|
|
119
|
-
): this {
|
|
120
|
-
if (!capability) throw new Error('WorkflowBuilder.add: capability is required');
|
|
121
|
-
if (this.nodes.length >= this.maxSteps) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
`WorkflowBuilder.add: max steps (${this.maxSteps}) reached; ` +
|
|
124
|
-
`cannot add more nodes. Adjust maxSteps option if needed.`
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
this.nodeCounter += 1;
|
|
128
|
-
this.nodes.push({
|
|
129
|
-
id: `${this.options.id}-node-${this.nodeCounter}`,
|
|
130
|
-
capability,
|
|
131
|
-
params,
|
|
132
|
-
label,
|
|
133
|
-
});
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 按 capability 或节点 id 移除节点。
|
|
139
|
-
* 若有多个同 capability 节点,只移除第一个。
|
|
140
|
-
* 若不存在则静默(链式 API 容错)。
|
|
141
|
-
*
|
|
142
|
-
* @param capabilityOrId 能力名或节点 id
|
|
143
|
-
*/
|
|
144
|
-
remove(capabilityOrId: string): this {
|
|
145
|
-
const idx = this.nodes.findIndex(
|
|
146
|
-
(n) => n.id === capabilityOrId || n.capability === capabilityOrId
|
|
147
|
-
);
|
|
148
|
-
if (idx >= 0) {
|
|
149
|
-
this.nodes.splice(idx, 1);
|
|
150
|
-
}
|
|
151
|
-
return this;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 移动节点到新位置(线性链中重排)。
|
|
156
|
-
*
|
|
157
|
-
* @param from 源位置(0-based 索引)
|
|
158
|
-
* @param to 目标位置(0-based 索引,移动后该节点的新位置)
|
|
159
|
-
* @throws Error 当索引越界
|
|
160
|
-
*/
|
|
161
|
-
move(from: number, to: number): this {
|
|
162
|
-
if (from < 0 || from >= this.nodes.length) {
|
|
163
|
-
throw new Error(`WorkflowBuilder.move: from index ${from} out of range`);
|
|
164
|
-
}
|
|
165
|
-
if (to < 0 || to >= this.nodes.length) {
|
|
166
|
-
throw new Error(`WorkflowBuilder.move: to index ${to} out of range`);
|
|
167
|
-
}
|
|
168
|
-
if (from === to) return this;
|
|
169
|
-
const [node] = this.nodes.splice(from, 1);
|
|
170
|
-
// splice(from, 1) 在已校验的 in-range 索引上必返回 1 元素;
|
|
171
|
-
// 此处显式检查以满足 noUncheckedIndexedAccess,并在不变量被打破时报错
|
|
172
|
-
if (!node) {
|
|
173
|
-
throw new Error(`WorkflowBuilder.move: source node at index ${from} missing`);
|
|
174
|
-
}
|
|
175
|
-
this.nodes.splice(to, 0, node);
|
|
176
|
-
return this;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** 交换两个节点位置 */
|
|
180
|
-
swap(i: number, j: number): this {
|
|
181
|
-
if (i < 0 || i >= this.nodes.length || j < 0 || j >= this.nodes.length) {
|
|
182
|
-
throw new Error('WorkflowBuilder.swap: index out of range');
|
|
183
|
-
}
|
|
184
|
-
if (i === j) return this;
|
|
185
|
-
const a = this.nodes[i];
|
|
186
|
-
const b = this.nodes[j];
|
|
187
|
-
// 上方范围校验保证 i / j 在界内;显式检查以满足 noUncheckedIndexedAccess
|
|
188
|
-
if (!a || !b) {
|
|
189
|
-
throw new Error('WorkflowBuilder.swap: node missing (invariant violated)');
|
|
190
|
-
}
|
|
191
|
-
this.nodes[i] = b;
|
|
192
|
-
this.nodes[j] = a;
|
|
193
|
-
return this;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** 更新指定节点的参数 */
|
|
197
|
-
updateParams(nodeId: string, params: Record<string, unknown>): this {
|
|
198
|
-
const node = this.nodes.find((n) => n.id === nodeId);
|
|
199
|
-
if (node) {
|
|
200
|
-
node.params = { ...node.params, ...params };
|
|
201
|
-
}
|
|
202
|
-
return this;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** 当前节点数 */
|
|
206
|
-
get size(): number {
|
|
207
|
-
return this.nodes.length;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** 是否已达最大节点数 */
|
|
211
|
-
get isFull(): boolean {
|
|
212
|
-
return this.nodes.length >= this.maxSteps;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** 获取节点的只读副本(用于 UI 预览) */
|
|
216
|
-
getNodes(): ReadonlyArray<Readonly<BuilderNode>> {
|
|
217
|
-
return this.nodes.map((n) => ({ ...n }));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 构造 Workflow 实例。
|
|
222
|
-
*
|
|
223
|
-
* @throws Error 当节点为空 / 输入未设置 / 输出未设置
|
|
224
|
-
*/
|
|
225
|
-
build(): Workflow {
|
|
226
|
-
if (this.nodes.length === 0) {
|
|
227
|
-
throw new Error('WorkflowBuilder.build: no nodes added');
|
|
228
|
-
}
|
|
229
|
-
if (!this.input) {
|
|
230
|
-
throw new Error('WorkflowBuilder.build: input not set (call setInput first)');
|
|
231
|
-
}
|
|
232
|
-
if (!this.output) {
|
|
233
|
-
throw new Error('WorkflowBuilder.build: output not set (call setOutput first)');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const workflowNodes: WorkflowNode[] = this.nodes.map((n) => ({
|
|
237
|
-
id: n.id,
|
|
238
|
-
type: 'transform' as const,
|
|
239
|
-
capability: n.capability,
|
|
240
|
-
params: n.params,
|
|
241
|
-
label: n.label,
|
|
242
|
-
}));
|
|
243
|
-
|
|
244
|
-
const edges: WorkflowEdge[] = [];
|
|
245
|
-
for (let i = 0; i < workflowNodes.length - 1; i++) {
|
|
246
|
-
const fromNode = workflowNodes[i];
|
|
247
|
-
const toNode = workflowNodes[i + 1];
|
|
248
|
-
// 循环边界 i < length - 1 保证 i 与 i+1 均在界内;显式检查以满足 noUncheckedIndexedAccess
|
|
249
|
-
if (!fromNode || !toNode) {
|
|
250
|
-
throw new Error('WorkflowBuilder.build: edge node missing (invariant violated)');
|
|
251
|
-
}
|
|
252
|
-
edges.push({
|
|
253
|
-
from: fromNode.id,
|
|
254
|
-
to: toNode.id,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const now = Date.now();
|
|
259
|
-
return {
|
|
260
|
-
$schema: `https://lokvis.dev/schemas/workflow.json`,
|
|
261
|
-
id: this.options.id,
|
|
262
|
-
version: '1.0.0',
|
|
263
|
-
name: this.options.name,
|
|
264
|
-
description: this.options.description ?? '',
|
|
265
|
-
author: this.options.author ?? { id: 'local', name: 'Local User' },
|
|
266
|
-
category: this.options.category ?? 'image',
|
|
267
|
-
tags: this.options.tags ?? [],
|
|
268
|
-
nodes: workflowNodes,
|
|
269
|
-
edges,
|
|
270
|
-
inputs: this.input,
|
|
271
|
-
outputs: this.output,
|
|
272
|
-
official: this.options.official ?? false,
|
|
273
|
-
createdAt: now,
|
|
274
|
-
updatedAt: now,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** 从 Workflow 反向构造 Builder(用于编辑已有工作流) */
|
|
280
|
-
export function workflowToBuilder(workflow: Workflow, maxSteps?: number): WorkflowBuilder {
|
|
281
|
-
const builder = new WorkflowBuilder({
|
|
282
|
-
id: workflow.id,
|
|
283
|
-
name: workflow.name,
|
|
284
|
-
description: workflow.description,
|
|
285
|
-
author: workflow.author,
|
|
286
|
-
category: workflow.category,
|
|
287
|
-
tags: workflow.tags,
|
|
288
|
-
official: workflow.official,
|
|
289
|
-
maxSteps,
|
|
290
|
-
});
|
|
291
|
-
builder.setInput(workflow.inputs);
|
|
292
|
-
builder.setOutput(workflow.outputs);
|
|
293
|
-
// 按拓扑顺序添加节点(线性链 nodes 数组即顺序)
|
|
294
|
-
for (const node of workflow.nodes) {
|
|
295
|
-
if (node.type === 'transform' && node.capability) {
|
|
296
|
-
builder.add(node.capability, node.params ?? {}, node.label);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return builder;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/** AssetType 用于类型导出(便于消费方) */
|
|
303
|
-
export type { AssetType };
|