@ldsg/workflow 0.3.38 → 0.3.41

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.
@@ -119,6 +119,7 @@ describe("workflow BFS level scheduling with multiple predecessors", () => {
119
119
  kind: "workflow",
120
120
  parentId: "root",
121
121
  settings: {
122
+ slug: "test",
122
123
  title: "",
123
124
  description: "",
124
125
  startWorkflowNodeResourceId: "start",
@@ -205,6 +206,7 @@ describe("workflow BFS level scheduling with multiple predecessors", () => {
205
206
  kind: "workflow",
206
207
  parentId: "root",
207
208
  settings: {
209
+ slug: "test",
208
210
  title: "",
209
211
  description: "",
210
212
  startWorkflowNodeResourceId: "start",
@@ -312,6 +314,7 @@ describe("workflow BFS level scheduling with multiple predecessors", () => {
312
314
  kind: "workflow",
313
315
  parentId: "root",
314
316
  settings: {
317
+ slug: "test",
315
318
  title: "",
316
319
  description: "",
317
320
  startWorkflowNodeResourceId: "start",
@@ -428,6 +431,7 @@ describe("workflow BFS level scheduling with multiple predecessors", () => {
428
431
  kind: "workflow",
429
432
  parentId: "root",
430
433
  settings: {
434
+ slug: "test",
431
435
  title: "",
432
436
  description: "",
433
437
  startWorkflowNodeResourceId: "start",
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const resource_1 = require("../../resource");
3
+ const resource_1 = require("@ldsg/resource");
4
+ const resource_2 = require("../../resource");
4
5
  const numberIdList = ["1", "2", "3", "4", "5"];
5
6
  const nodeIdList = numberIdList.map((id) => `node-${id}`);
6
7
  const nodes = nodeIdList.map((nodeId) => {
@@ -116,7 +117,7 @@ const workflowData = {
116
117
  const getWorkflowDataRes = {
117
118
  workflowData,
118
119
  };
119
- class TestWorkflowResource extends resource_1.WorkflowResource {
120
+ class TestWorkflowResource extends resource_2.WorkflowResource {
120
121
  constructor() {
121
122
  super(...arguments);
122
123
  this.getWorkflowData = () => {
@@ -124,12 +125,16 @@ class TestWorkflowResource extends resource_1.WorkflowResource {
124
125
  };
125
126
  }
126
127
  }
128
+ beforeEach(() => {
129
+ (0, resource_1.clearResourceRegistry)();
130
+ });
127
131
  test("WorkflowResource execute", async () => {
128
132
  const testWorkflowResource = new TestWorkflowResource({
129
133
  id: "test",
130
134
  kind: "workflow",
131
135
  parentId: "root",
132
136
  settings: {
137
+ slug: "test",
133
138
  title: "",
134
139
  description: "",
135
140
  startWorkflowNodeResourceId: "node-1",
@@ -148,3 +153,201 @@ test("WorkflowResource execute", async () => {
148
153
  });
149
154
  expect(variables).toMatchObject(workflowOutputVariables);
150
155
  });
156
+ test("WorkflowResource execute with wrapper workflow", async () => {
157
+ var _a, _b, _c, _d;
158
+ let wrapperInputVariables;
159
+ class RecordingWrapperWorkflowResource extends resource_2.WorkflowResource {
160
+ constructor() {
161
+ super(...arguments);
162
+ this.execute = async (params) => {
163
+ wrapperInputVariables = params.variables;
164
+ const { wrapperWorkflowsInfo } = params.variables;
165
+ const innerWorkflowInfo = wrapperWorkflowsInfo.wrapperWorkflowResourceIdToInnerWorkflowInfoMap["wrapper-workflow"];
166
+ const { variables: innerWorkflowVariables } = await innerWorkflowInfo.execute({
167
+ context: params.context,
168
+ variables: { hello: "inner" },
169
+ });
170
+ const res = {
171
+ variables: {
172
+ ...innerWorkflowVariables,
173
+ wrapped: true,
174
+ },
175
+ };
176
+ return res;
177
+ };
178
+ }
179
+ }
180
+ const wrapperWorkflowResource = new RecordingWrapperWorkflowResource({
181
+ id: "wrapper-workflow",
182
+ kind: "workflow",
183
+ parentId: "root",
184
+ settings: {
185
+ slug: "wrapper-workflow",
186
+ title: "",
187
+ description: "",
188
+ startWorkflowNodeResourceId: "",
189
+ endWorkflowNodeResourceId: "",
190
+ },
191
+ });
192
+ const testWorkflowResource = new TestWorkflowResource({
193
+ id: "wrapped-workflow",
194
+ kind: "workflow",
195
+ parentId: "root",
196
+ settings: {
197
+ slug: "wrapped-workflow",
198
+ title: "",
199
+ description: "",
200
+ startWorkflowNodeResourceId: "node-1",
201
+ endWorkflowNodeResourceId: "node-5",
202
+ wrapperWorkflowResourceId: wrapperWorkflowResource.id,
203
+ },
204
+ });
205
+ const { variables } = await testWorkflowResource.execute({
206
+ context: {},
207
+ variables: { hello: "world" },
208
+ });
209
+ /**
210
+ * 包装工作流接收到了内层工作流执行方法及原变量
211
+ */
212
+ expect((_c = (_b = (_a = wrapperInputVariables === null || wrapperInputVariables === void 0 ? void 0 : wrapperInputVariables.wrapperWorkflowsInfo) === null || _a === void 0 ? void 0 : _a.wrapperWorkflowResourceIdToInnerWorkflowInfoMap) === null || _b === void 0 ? void 0 : _b["wrapper-workflow"]) === null || _c === void 0 ? void 0 : _c.execute).toBeDefined();
213
+ expect((_d = wrapperInputVariables === null || wrapperInputVariables === void 0 ? void 0 : wrapperInputVariables.wrapperWorkflowsInfo) === null || _d === void 0 ? void 0 : _d.pendingWrapperWorkflowResourceIdsFromInnerToOuter).toEqual(["wrapper-workflow"]);
214
+ expect(wrapperInputVariables === null || wrapperInputVariables === void 0 ? void 0 : wrapperInputVariables.hello).toBe("world");
215
+ /**
216
+ * 执行结果由包装工作流决定
217
+ */
218
+ expect(variables).toMatchObject({ hello: "inner", wrapped: true });
219
+ });
220
+ test("WorkflowResource execute with two layer wrapper workflows", async () => {
221
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
222
+ let topWorkflowInputVariables;
223
+ class TopWorkflowResource extends resource_2.WorkflowResource {
224
+ constructor() {
225
+ super(...arguments);
226
+ this.execute = async (params) => {
227
+ topWorkflowInputVariables = params.variables;
228
+ const { wrapperWorkflowsInfo } = params.variables;
229
+ /**
230
+ * 顶层包装工作流执行自己的内嵌工作流,即中间层包装工作流
231
+ */
232
+ const middleWorkflowInfo = wrapperWorkflowsInfo.wrapperWorkflowResourceIdToInnerWorkflowInfoMap["workflow-top"];
233
+ const { variables: middleWorkflowVariables } = await middleWorkflowInfo.execute({
234
+ context: params.context,
235
+ variables: { ...params.variables, hello: "inner" },
236
+ });
237
+ const res = {
238
+ variables: middleWorkflowVariables,
239
+ };
240
+ return res;
241
+ };
242
+ }
243
+ }
244
+ const topWorkflowResource = new TopWorkflowResource({
245
+ id: "workflow-top",
246
+ kind: "workflow",
247
+ parentId: "root",
248
+ settings: {
249
+ slug: "workflow-top",
250
+ title: "",
251
+ description: "",
252
+ startWorkflowNodeResourceId: "node-1",
253
+ endWorkflowNodeResourceId: "node-5",
254
+ },
255
+ });
256
+ const middleWorkflowResource = new TestWorkflowResource({
257
+ id: "workflow-middle",
258
+ kind: "workflow",
259
+ parentId: "root",
260
+ settings: {
261
+ slug: "workflow-middle",
262
+ title: "",
263
+ description: "",
264
+ startWorkflowNodeResourceId: "node-1",
265
+ endWorkflowNodeResourceId: "node-5",
266
+ wrapperWorkflowResourceId: topWorkflowResource.id,
267
+ },
268
+ });
269
+ const wrappedWorkflowResource = new TestWorkflowResource({
270
+ id: "workflow-wrapped",
271
+ kind: "workflow",
272
+ parentId: "root",
273
+ settings: {
274
+ slug: "workflow-wrapped",
275
+ title: "",
276
+ description: "",
277
+ startWorkflowNodeResourceId: "node-1",
278
+ endWorkflowNodeResourceId: "node-5",
279
+ wrapperWorkflowResourceId: middleWorkflowResource.id,
280
+ },
281
+ });
282
+ const { variables } = await wrappedWorkflowResource.execute({
283
+ context: {},
284
+ variables: { hello: "world" },
285
+ });
286
+ /**
287
+ * 顶层包装工作流收到的内嵌工作流信息表中同时保留了中间层与最内层
288
+ */
289
+ expect(Object.keys((_b = (_a = topWorkflowInputVariables === null || topWorkflowInputVariables === void 0 ? void 0 : topWorkflowInputVariables.wrapperWorkflowsInfo) === null || _a === void 0 ? void 0 : _a.wrapperWorkflowResourceIdToInnerWorkflowInfoMap) !== null && _b !== void 0 ? _b : {}).sort()).toEqual(["workflow-middle", "workflow-top"]);
290
+ /**
291
+ * 待消费栈按包装链由内到外登记,末位为顶层包装工作流自身
292
+ */
293
+ expect((_c = topWorkflowInputVariables === null || topWorkflowInputVariables === void 0 ? void 0 : topWorkflowInputVariables.wrapperWorkflowsInfo) === null || _c === void 0 ? void 0 : _c.pendingWrapperWorkflowResourceIdsFromInnerToOuter).toEqual(["workflow-middle", "workflow-top"]);
294
+ expect((_f = (_e = (_d = topWorkflowInputVariables === null || topWorkflowInputVariables === void 0 ? void 0 : topWorkflowInputVariables.wrapperWorkflowsInfo) === null || _d === void 0 ? void 0 : _d.wrapperWorkflowResourceIdToInnerWorkflowInfoMap) === null || _e === void 0 ? void 0 : _e["workflow-top"]) === null || _f === void 0 ? void 0 : _f.execute).toBeDefined();
295
+ expect((_j = (_h = (_g = topWorkflowInputVariables === null || topWorkflowInputVariables === void 0 ? void 0 : topWorkflowInputVariables.wrapperWorkflowsInfo) === null || _g === void 0 ? void 0 : _g.wrapperWorkflowResourceIdToInnerWorkflowInfoMap) === null || _h === void 0 ? void 0 : _h["workflow-middle"]) === null || _j === void 0 ? void 0 : _j.execute).toBeDefined();
296
+ expect(topWorkflowInputVariables === null || topWorkflowInputVariables === void 0 ? void 0 : topWorkflowInputVariables.hello).toBe("world");
297
+ expect(variables).toMatchObject({ hello: "inner" });
298
+ });
299
+ test("WorkflowResource execute detects wrapper cycle", async () => {
300
+ const workflowA = new TestWorkflowResource({
301
+ id: "workflow-a",
302
+ kind: "workflow",
303
+ parentId: "root",
304
+ settings: {
305
+ slug: "workflow-a",
306
+ title: "",
307
+ description: "",
308
+ startWorkflowNodeResourceId: "node-1",
309
+ endWorkflowNodeResourceId: "node-5",
310
+ wrapperWorkflowResourceId: "workflow-b",
311
+ },
312
+ });
313
+ const workflowB = new TestWorkflowResource({
314
+ id: "workflow-b",
315
+ kind: "workflow",
316
+ parentId: "root",
317
+ settings: {
318
+ slug: "workflow-b",
319
+ title: "",
320
+ description: "",
321
+ startWorkflowNodeResourceId: "node-1",
322
+ endWorkflowNodeResourceId: "node-5",
323
+ wrapperWorkflowResourceId: "workflow-a",
324
+ },
325
+ });
326
+ await expect(workflowA.execute({
327
+ context: {},
328
+ variables: { hello: "world" },
329
+ })).rejects.toThrow("workflow wrapper cycle detected: workflow-b");
330
+ await expect(workflowB.execute({
331
+ context: {},
332
+ variables: { hello: "world" },
333
+ })).rejects.toThrow("workflow wrapper cycle detected: workflow-a");
334
+ });
335
+ test("WorkflowResource execute with invalid wrapper workflow resource", async () => {
336
+ const testWorkflowResource = new TestWorkflowResource({
337
+ id: "workflow-with-invalid-wrapper",
338
+ kind: "workflow",
339
+ parentId: "root",
340
+ settings: {
341
+ slug: "workflow-with-invalid-wrapper",
342
+ title: "",
343
+ description: "",
344
+ startWorkflowNodeResourceId: "node-1",
345
+ endWorkflowNodeResourceId: "node-5",
346
+ wrapperWorkflowResourceId: "not-exist",
347
+ },
348
+ });
349
+ await expect(testWorkflowResource.execute({
350
+ context: {},
351
+ variables: { hello: "world" },
352
+ })).rejects.toThrow("invalid wrapper workflow info, wrapper workflow resource id: not-exist");
353
+ });
@@ -131,6 +131,7 @@ test("WorkflowResource execute with portId", async () => {
131
131
  kind: "workflow",
132
132
  parentId: "root",
133
133
  settings: {
134
+ slug: "test",
134
135
  title: "",
135
136
  description: "",
136
137
  startWorkflowNodeResourceId: "start",
@@ -260,6 +261,7 @@ test("WorkflowResource execute without portId", async () => {
260
261
  kind: "workflow",
261
262
  parentId: "root",
262
263
  settings: {
264
+ slug: "test",
263
265
  title: "",
264
266
  description: "",
265
267
  startWorkflowNodeResourceId: "start",
@@ -0,0 +1,3 @@
1
+ import { WorkflowExecute } from "../../types";
2
+ export type { WorkflowExecute };
3
+ //# sourceMappingURL=wrapper-chain.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapper-chain.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/wrapper-chain.test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAyC,MAAM,aAAa,CAAC;AAqPnG,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const resource_1 = require("@ldsg/resource");
4
+ const resource_2 = require("../../resource");
5
+ const passthroughExecute = async (params) => ({
6
+ variables: params.variables,
7
+ });
8
+ const innerWorkflowNodeExecute = async (params) => {
9
+ var _a, _b;
10
+ const { context, variables: inputVariables } = params;
11
+ const { wrapperWorkflowsInfo, variables } = inputVariables;
12
+ const wrapperWorkflowResourceIdToInnerWorkflowInfoMap = (_a = wrapperWorkflowsInfo === null || wrapperWorkflowsInfo === void 0 ? void 0 : wrapperWorkflowsInfo.wrapperWorkflowResourceIdToInnerWorkflowInfoMap) !== null && _a !== void 0 ? _a : {};
13
+ const pendingWrapperWorkflowResourceIdsFromInnerToOuter = (_b = wrapperWorkflowsInfo === null || wrapperWorkflowsInfo === void 0 ? void 0 : wrapperWorkflowsInfo.pendingWrapperWorkflowResourceIdsFromInnerToOuter) !== null && _b !== void 0 ? _b : [];
14
+ const currentWrapperWorkflowResourceId = pendingWrapperWorkflowResourceIdsFromInnerToOuter[pendingWrapperWorkflowResourceIdsFromInnerToOuter.length - 1];
15
+ if (!currentWrapperWorkflowResourceId) {
16
+ throw new Error("invalid wrapper workflows info, no pending wrapper workflow resource id");
17
+ }
18
+ const innerWorkflowInfo = wrapperWorkflowResourceIdToInnerWorkflowInfoMap[currentWrapperWorkflowResourceId];
19
+ if (!(innerWorkflowInfo === null || innerWorkflowInfo === void 0 ? void 0 : innerWorkflowInfo.execute)) {
20
+ throw new Error(`invalid inner workflow info: ${currentWrapperWorkflowResourceId}`);
21
+ }
22
+ const executeRes = await innerWorkflowInfo.execute({
23
+ context,
24
+ variables: {
25
+ ...variables,
26
+ wrapperWorkflowsInfo: {
27
+ wrapperWorkflowResourceIdToInnerWorkflowInfoMap,
28
+ pendingWrapperWorkflowResourceIdsFromInnerToOuter: pendingWrapperWorkflowResourceIdsFromInnerToOuter.slice(0, -1),
29
+ },
30
+ },
31
+ });
32
+ return {
33
+ variables: {
34
+ result: executeRes.variables,
35
+ },
36
+ };
37
+ };
38
+ const startNode = {
39
+ id: "node-1",
40
+ propertiesSchema: {
41
+ type: "object",
42
+ properties: {
43
+ input: {
44
+ type: "object",
45
+ properties: {
46
+ hello: { type: "string", "x-expression": "hello" },
47
+ wrapperWorkflowsInfo: { type: "object", "x-expression": "wrapperWorkflowsInfo" },
48
+ },
49
+ },
50
+ output: {
51
+ type: "object",
52
+ properties: {
53
+ hello: { type: "string", "x-expression": "hello" },
54
+ wrapperWorkflowsInfo: { type: "object", "x-expression": "wrapperWorkflowsInfo" },
55
+ },
56
+ },
57
+ },
58
+ },
59
+ execute: passthroughExecute,
60
+ };
61
+ const innerWorkflowNode = {
62
+ id: "node-2",
63
+ propertiesSchema: {
64
+ type: "object",
65
+ properties: {
66
+ input: {
67
+ type: "object",
68
+ properties: {
69
+ variables: { type: "object", "x-expression": `$."node-1"` },
70
+ wrapperWorkflowsInfo: {
71
+ type: "object",
72
+ "x-expression": `$."node-1"."wrapperWorkflowsInfo"`,
73
+ },
74
+ },
75
+ },
76
+ output: {
77
+ type: "object",
78
+ properties: {
79
+ result: { type: "object", "x-expression": "result" },
80
+ },
81
+ },
82
+ },
83
+ },
84
+ execute: innerWorkflowNodeExecute,
85
+ };
86
+ const endNode = {
87
+ id: "node-3",
88
+ propertiesSchema: {
89
+ type: "object",
90
+ properties: {
91
+ input: {
92
+ type: "object",
93
+ properties: {
94
+ result: { type: "object", "x-expression": `$."node-2"."result"` },
95
+ },
96
+ },
97
+ output: {
98
+ type: "object",
99
+ properties: {
100
+ result: { type: "object", "x-expression": "result" },
101
+ },
102
+ },
103
+ },
104
+ },
105
+ execute: passthroughExecute,
106
+ };
107
+ const businessEndNode = {
108
+ id: "node-3",
109
+ propertiesSchema: {
110
+ type: "object",
111
+ properties: {
112
+ input: {
113
+ type: "object",
114
+ properties: {
115
+ result: { type: "string", "x-expression": `$."node-1"."hello"` },
116
+ },
117
+ },
118
+ output: {
119
+ type: "object",
120
+ properties: {
121
+ result: { type: "string", "x-expression": "result" },
122
+ },
123
+ },
124
+ },
125
+ },
126
+ execute: passthroughExecute,
127
+ };
128
+ const makeWorkflowData = (nodes) => ({
129
+ edges: nodes.slice(1).map((node, index) => ({
130
+ sourceWorkflowNodeResourceId: nodes[index].id,
131
+ targetWorkflowNodeResourceId: node.id,
132
+ })),
133
+ nodes,
134
+ });
135
+ const makeWorkflowResourceClass = (workflowData) => {
136
+ const getWorkflowDataRes = { workflowData };
137
+ return class extends resource_2.WorkflowResource {
138
+ constructor() {
139
+ super(...arguments);
140
+ this.getWorkflowData = () => getWorkflowDataRes;
141
+ }
142
+ };
143
+ };
144
+ const WrapperWorkflowResource = makeWorkflowResourceClass(makeWorkflowData([startNode, innerWorkflowNode, endNode]));
145
+ const InnermostWorkflowResource = makeWorkflowResourceClass(makeWorkflowData([startNode, businessEndNode]));
146
+ beforeEach(() => {
147
+ (0, resource_1.clearResourceRegistry)();
148
+ });
149
+ test("three layer wrapper chain with inner workflow node", async () => {
150
+ const topWorkflowResource = new WrapperWorkflowResource({
151
+ id: "workflow-top",
152
+ kind: "workflow",
153
+ parentId: "root",
154
+ settings: {
155
+ slug: "workflow-top",
156
+ title: "",
157
+ description: "",
158
+ startWorkflowNodeResourceId: "node-1",
159
+ endWorkflowNodeResourceId: "node-3",
160
+ },
161
+ });
162
+ const middleWorkflowResource = new WrapperWorkflowResource({
163
+ id: "workflow-middle",
164
+ kind: "workflow",
165
+ parentId: "root",
166
+ settings: {
167
+ slug: "workflow-middle",
168
+ title: "",
169
+ description: "",
170
+ startWorkflowNodeResourceId: "node-1",
171
+ endWorkflowNodeResourceId: "node-3",
172
+ wrapperWorkflowResourceId: topWorkflowResource.id,
173
+ },
174
+ });
175
+ const innerWorkflowResource = new InnermostWorkflowResource({
176
+ id: "workflow-inner",
177
+ kind: "workflow",
178
+ parentId: "root",
179
+ settings: {
180
+ slug: "workflow-inner",
181
+ title: "",
182
+ description: "",
183
+ startWorkflowNodeResourceId: "node-1",
184
+ endWorkflowNodeResourceId: "node-3",
185
+ wrapperWorkflowResourceId: middleWorkflowResource.id,
186
+ },
187
+ });
188
+ const { variables } = await innerWorkflowResource.execute({
189
+ context: {},
190
+ variables: { hello: "world" },
191
+ });
192
+ expect(variables).toMatchObject({
193
+ result: { result: { result: "world" } },
194
+ });
195
+ });
196
+ test("inner workflow node without pending wrapper workflow resource id", async () => {
197
+ const workflowResource = new WrapperWorkflowResource({
198
+ id: "workflow-no-chain",
199
+ kind: "workflow",
200
+ parentId: "root",
201
+ settings: {
202
+ slug: "workflow-no-chain",
203
+ title: "",
204
+ description: "",
205
+ startWorkflowNodeResourceId: "node-1",
206
+ endWorkflowNodeResourceId: "node-3",
207
+ },
208
+ });
209
+ await expect(workflowResource.execute({ context: {}, variables: { hello: "world" } })).rejects.toThrow("no pending wrapper workflow resource id");
210
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,0CAA0C,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,sDAAsD,EAAE,uBAGpE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uDAAuD,EAAE,+BAQnE,CAAC;AAEJ,eAAO,MAAM,6CAA6C,EAAE,uBAG3D,CAAC;AAEF,eAAO,MAAM,8CAA8C,EAAE,0CAG1D,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,iEAAiE;;;CAGpE,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,0CAA0C,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,sDAAsD,EAAE,uBAIpE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uDAAuD,EAAE,+BAQnE,CAAC;AAEJ,eAAO,MAAM,6CAA6C,EAAE,uBAI3D,CAAC;AAEF,eAAO,MAAM,8CAA8C,EAAE,0CAG1D,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,iEAAiE;;;CAGpE,CAAC"}
package/dist/constants.js CHANGED
@@ -6,6 +6,7 @@ exports.SPECIFIC_WORKFLOW_NODE_TYPE_TO_WORKFLOW_NODE_TYPE_RESOURCE_ID_MAP = expo
6
6
  * 实例化资源处理程序普通资源配置
7
7
  */
8
8
  exports.INSTANTIATE_RESOURCE_HANDLER_GENERAL_RESOURCE_SETTINGS = {
9
+ slug: "workflow-instantiate-resource-handler",
9
10
  title: "工作流实例化资源处理程序",
10
11
  description: "",
11
12
  };
@@ -23,6 +24,7 @@ exports.INSTANTIATE_RESOURCE_HANDLER_SPECIFIC_RESOURCE_SETTINGS = {
23
24
  ],
24
25
  };
25
26
  exports.RESOURCE_DEFINITION_GENERAL_RESOURCE_SETTINGS = {
27
+ slug: "workflow-resource-definition",
26
28
  title: "工作流资源定义",
27
29
  description: "",
28
30
  };
@@ -20,6 +20,10 @@ export declare class WorkflowResource extends Resource<WorkflowSpecificResourceS
20
20
  getWorkflowEdgeInfoList: GetWorkflowEdgeInfoList;
21
21
  getWorkflowNodeInfoList: GetWorkflowNodeInfoList;
22
22
  getWorkflowData: GetWorkflowData;
23
+ /**
24
+ * 执行当前工作流自身(不包含外层包装工作流)
25
+ */
26
+ executeWorkflowWithoutWrapper: WorkflowExecute;
23
27
  execute: WorkflowExecute;
24
28
  getWorkflowInfo: GetWorkflowInfo;
25
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAGL,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,gCAAgC,EACjC,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,gCAAgC,CAAC;IAC9E,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,eAAe,EAAE,eAAe,CAiB9B;IAEF,OAAO,EAAE,eAAe,CAuBtB;IAEF,eAAe,EAAE,eAAe,CAY9B;CACH"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAGL,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,gCAAgC,EAEjC,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;CAC1C;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,gCAAgC,CAAC;IAC9E,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,uBAAuB,EAAE,uBAAuB,CAsB9C;IAEF,eAAe,EAAE,eAAe,CAiB9B;IAEF;;OAEG;IACH,6BAA6B,EAAE,eAAe,CAuB5C;IAEF,OAAO,EAAE,eAAe,CAsFtB;IAEF,eAAe,EAAE,eAAe,CAa9B;CACH"}
package/dist/resource.js CHANGED
@@ -55,7 +55,10 @@ class WorkflowResource extends resource_1.Resource {
55
55
  };
56
56
  return res;
57
57
  };
58
- this.execute = async (params) => {
58
+ /**
59
+ * 执行当前工作流自身(不包含外层包装工作流)
60
+ */
61
+ this.executeWorkflowWithoutWrapper = async (params) => {
59
62
  const { context, presetWorkflowVariables, variables, traceContext } = params;
60
63
  const { getWorkflowData, settings: { startWorkflowNodeResourceId, endWorkflowNodeResourceId }, } = this;
61
64
  const { workflowData } = getWorkflowData();
@@ -72,9 +75,68 @@ class WorkflowResource extends resource_1.Resource {
72
75
  traceContext,
73
76
  });
74
77
  };
78
+ this.execute = async (params) => {
79
+ var _a, _b, _c;
80
+ const { context, presetWorkflowVariables, variables: workflowInputVariables, traceContext, } = params;
81
+ const { id, settings } = this;
82
+ const { wrapperWorkflowResourceId } = settings;
83
+ /**
84
+ * 若当前工作流配置了外层包装工作流,则执行包装工作流,
85
+ * 并把当前工作流自身的执行方法登记到内嵌工作流信息表中,
86
+ * 由包装工作流决定何时调用内层工作流。
87
+ */
88
+ if (wrapperWorkflowResourceId) {
89
+ const { wrapperWorkflowsInfo, ...restWorkflowInputVariables } = workflowInputVariables;
90
+ const wrapperWorkflowResourceIdToInnerWorkflowInfoMap = {
91
+ ...wrapperWorkflowsInfo === null || wrapperWorkflowsInfo === void 0 ? void 0 : wrapperWorkflowsInfo.wrapperWorkflowResourceIdToInnerWorkflowInfoMap,
92
+ };
93
+ const pendingWrapperWorkflowResourceIdsFromInnerToOuter = [
94
+ ...((_a = wrapperWorkflowsInfo === null || wrapperWorkflowsInfo === void 0 ? void 0 : wrapperWorkflowsInfo.pendingWrapperWorkflowResourceIdsFromInnerToOuter) !== null && _a !== void 0 ? _a : []),
95
+ ];
96
+ /**
97
+ * 每个工作流只有一个包装工作流,合法的包装链中各 key 不会重复。
98
+ * 若该 key 已存在,说明同一个包装工作流在链中出现了两次,即为循环引用。
99
+ */
100
+ if (wrapperWorkflowResourceIdToInnerWorkflowInfoMap[wrapperWorkflowResourceId]) {
101
+ throw new Error(`workflow wrapper cycle detected: ${wrapperWorkflowResourceId}`);
102
+ }
103
+ const wrapperWorkflowInfo = (_c = (_b = this.getFilteredResource({
104
+ id: wrapperWorkflowResourceId,
105
+ })) === null || _b === void 0 ? void 0 : _b.resource) === null || _c === void 0 ? void 0 : _c.getWorkflowInfo().workflowInfo;
106
+ if (!wrapperWorkflowInfo) {
107
+ throw new Error(`invalid wrapper workflow info, wrapper workflow resource id: ${wrapperWorkflowResourceId}`);
108
+ }
109
+ const innerWorkflowInfo = {
110
+ id,
111
+ execute: this.executeWorkflowWithoutWrapper,
112
+ };
113
+ wrapperWorkflowResourceIdToInnerWorkflowInfoMap[wrapperWorkflowResourceId] =
114
+ innerWorkflowInfo;
115
+ /**
116
+ * 登记的包装工作流 ID 入栈,包装工作流执行时消费栈末位
117
+ */
118
+ pendingWrapperWorkflowResourceIdsFromInnerToOuter.push(wrapperWorkflowResourceId);
119
+ const wrapperWorkflowInputVariables = {
120
+ ...restWorkflowInputVariables,
121
+ wrapperWorkflowsInfo: {
122
+ wrapperWorkflowResourceIdToInnerWorkflowInfoMap,
123
+ pendingWrapperWorkflowResourceIdsFromInnerToOuter,
124
+ },
125
+ };
126
+ const res = await wrapperWorkflowInfo.execute({
127
+ context,
128
+ presetWorkflowVariables,
129
+ variables: wrapperWorkflowInputVariables,
130
+ traceContext,
131
+ });
132
+ return res;
133
+ }
134
+ return this.executeWorkflowWithoutWrapper(params);
135
+ };
75
136
  this.getWorkflowInfo = () => {
76
- const { execute } = this;
137
+ const { execute, id } = this;
77
138
  const workflowInfo = {
139
+ id,
78
140
  execute,
79
141
  };
80
142
  const res = {
@@ -1,9 +1,11 @@
1
1
  import { z } from "zod";
2
2
  export declare const WorkflowSpecificResourceSettingsSchema: z.ZodObject<{
3
+ wrapperWorkflowResourceId: z.ZodOptional<z.ZodString>;
3
4
  startWorkflowNodeResourceId: z.ZodString;
4
5
  endWorkflowNodeResourceId: z.ZodString;
5
6
  }, z.core.$catchall<z.ZodAny>>;
6
7
  export declare const SpecificResourceSettingsSchema: z.ZodObject<{
8
+ wrapperWorkflowResourceId: z.ZodOptional<z.ZodString>;
7
9
  startWorkflowNodeResourceId: z.ZodString;
8
10
  endWorkflowNodeResourceId: z.ZodString;
9
11
  }, z.core.$catchall<z.ZodAny>>;
@@ -1 +1 @@
1
- {"version":3,"file":"specific-resource-settings.d.ts","sourceRoot":"","sources":["../../src/types/specific-resource-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,sCAAsC;;;8BAY/B,CAAC;AAErB,eAAO,MAAM,8BAA8B;;;8BAAyC,CAAC;AACrF,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,sCAAsC,CAC9C,CAAC"}
1
+ {"version":3,"file":"specific-resource-settings.d.ts","sourceRoot":"","sources":["../../src/types/specific-resource-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,sCAAsC;;;;8BAiB/B,CAAC;AAErB,eAAO,MAAM,8BAA8B;;;;8BAAyC,CAAC;AAErF,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,sCAAsC,CAC9C,CAAC"}
@@ -4,6 +4,10 @@ exports.SpecificResourceSettingsSchema = exports.WorkflowSpecificResourceSetting
4
4
  const zod_1 = require("zod");
5
5
  exports.WorkflowSpecificResourceSettingsSchema = zod_1.z
6
6
  .object({
7
+ /**
8
+ * Wrapper Workflow Resource ID
9
+ */
10
+ wrapperWorkflowResourceId: zod_1.z.string().optional(),
7
11
  /**
8
12
  * Start Workflow Node Resource ID
9
13
  */
@@ -36,6 +36,35 @@ export interface WorkflowExecuteRes {
36
36
  */
37
37
  export type WorkflowExecute = (params: WorkflowExecuteParams) => Promise<WorkflowExecuteRes>;
38
38
  export interface WorkflowInfo {
39
+ /**
40
+ * 工作流资源 ID
41
+ */
42
+ id: string;
39
43
  execute: WorkflowExecute;
40
44
  }
45
+ /**
46
+ * 包装工作流资源 ID 到内嵌工作流信息的映射表
47
+ *
48
+ * 以包装工作流资源 ID 为 key,值为把该包装工作流作为外层包装的工作流(即被包装者)的执行信息。
49
+ */
50
+ export type WrapperWorkflowResourceIdToInnerWorkflowInfoMap = Record<string, WorkflowInfo>;
51
+ /**
52
+ * 包装工作流链信息
53
+ *
54
+ * 由被包装者登记后随包装链由内向外透传,供「执行内嵌工作流」节点消费。
55
+ */
56
+ export interface WrapperWorkflowsInfo {
57
+ /**
58
+ * 包装工作流资源 ID 到内嵌工作流信息的映射表
59
+ */
60
+ wrapperWorkflowResourceIdToInnerWorkflowInfoMap: WrapperWorkflowResourceIdToInnerWorkflowInfoMap;
61
+ /**
62
+ * 待消费的包装工作流资源 ID 栈(由内到外)
63
+ *
64
+ * 登记由内向外发生,消费由外向内发生,因此以栈的形式使用:
65
+ * 末位为当前工作流应当消费的键,消费后弹出末位,
66
+ * 再将剩余部分透传给内嵌工作流,供更内层继续消费。
67
+ */
68
+ pendingWrapperWorkflowResourceIdsFromInnerToOuter: string[];
69
+ }
41
70
  //# sourceMappingURL=workflow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/types/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAE7B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAE5D;;;OAGG;IACH,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,CAAC;CAC1B"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/types/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAE7B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAE5D;;;OAGG;IACH,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,+CAA+C,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE3F;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,+CAA+C,EAAE,+CAA+C,CAAC;IAEjG;;;;;;OAMG;IACH,iDAAiD,EAAE,MAAM,EAAE,CAAC;CAC7D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldsg/workflow",
3
- "version": "0.3.38",
3
+ "version": "0.3.41",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -20,11 +20,11 @@
20
20
  "jsonata": "2.2.2",
21
21
  "lodash": "^4.17.21",
22
22
  "zod": "^4.1.11",
23
- "@ldsg/constants": "0.3.38",
24
- "@ldsg/handler": "0.3.38",
25
- "@ldsg/resource-definition": "0.3.38",
26
- "@ldsg/types": "0.3.38",
27
- "@ldsg/resource": "0.3.38"
23
+ "@ldsg/handler": "0.3.41",
24
+ "@ldsg/resource": "0.3.41",
25
+ "@ldsg/constants": "0.3.41",
26
+ "@ldsg/resource-definition": "0.3.41",
27
+ "@ldsg/types": "0.3.41"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/fs-extra": "^11.0.4",
@@ -1,96 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.resolveJsonSchema = void 0;
7
- const jsonata_1 = __importDefault(require("jsonata"));
8
- const lodash_1 = __importDefault(require("lodash"));
9
- const resolveJsonSchema = async (params) => {
10
- var _a;
11
- const { input, jsonSchema, source } = params;
12
- const { type: jsonSchemaType, 'x-expression': jsonSchemaExpression, const: jsonSchemaConst, prefixItems: jsonSchemaPrefixItems } = jsonSchema;
13
- let data;
14
- /**
15
- * 若常量值存在,则直接返回
16
- */
17
- if (jsonSchemaConst) {
18
- data = jsonSchemaConst;
19
- }
20
- else {
21
- data = source;
22
- /**
23
- * 若表达式存在,则尝试获取表达式值
24
- */
25
- if (jsonSchemaExpression) {
26
- const expression = (0, jsonata_1.default)(jsonSchemaExpression);
27
- data = await expression.evaluate(input);
28
- }
29
- switch (jsonSchemaType) {
30
- case 'object': {
31
- data = data !== null && data !== void 0 ? data : {};
32
- const jsonSchemaProperties = (_a = jsonSchema.properties) !== null && _a !== void 0 ? _a : {};
33
- const keys = lodash_1.default.keys(jsonSchemaProperties);
34
- const keysMapResPromises = keys.map(async (key) => {
35
- const source = lodash_1.default.get(data, key);
36
- const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
37
- jsonSchema: jsonSchemaProperties[key],
38
- input,
39
- source
40
- });
41
- const res = {
42
- key,
43
- resolveJsonSchemaRes
44
- };
45
- return res;
46
- });
47
- const keysMapRes = await Promise.all(keysMapResPromises);
48
- lodash_1.default.each(keysMapRes, ({ key, resolveJsonSchemaRes }) => {
49
- lodash_1.default.set(data, key, resolveJsonSchemaRes.data);
50
- });
51
- break;
52
- }
53
- case 'array': {
54
- /**
55
- * 如果 prefixItems 存在,则转为数组结果
56
- */
57
- if (jsonSchemaPrefixItems) {
58
- if (jsonSchemaPrefixItems.length > 0) {
59
- const itemsDataPromises = jsonSchemaPrefixItems.map(async (prefixItem) => {
60
- const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
61
- jsonSchema: prefixItem,
62
- input,
63
- source
64
- });
65
- return resolveJsonSchemaRes.data;
66
- });
67
- const itemsData = await Promise.all(itemsDataPromises);
68
- data = itemsData;
69
- }
70
- else {
71
- /**
72
- * 空数组时,初始化为空数组
73
- */
74
- data = [];
75
- }
76
- }
77
- break;
78
- }
79
- case 'boolean':
80
- case 'integer':
81
- case 'number':
82
- case 'string': {
83
- break;
84
- }
85
- case 'null': {
86
- break;
87
- }
88
- default: {
89
- break;
90
- }
91
- }
92
- }
93
- const res = { data };
94
- return res;
95
- };
96
- exports.resolveJsonSchema = resolveJsonSchema;