@nocobase/flow-engine 2.0.0-beta.2 → 2.0.0-beta.20
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/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/JSRunner.d.ts +6 -0
- package/lib/JSRunner.js +2 -1
- package/lib/ViewScopedFlowEngine.js +3 -0
- package/lib/acl/Acl.js +13 -3
- package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
- package/lib/components/dnd/gridDragPlanner.js +53 -1
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +11 -3
- package/lib/components/variables/VariableInput.js +8 -2
- package/lib/data-source/index.js +6 -0
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +156 -22
- package/lib/flowContext.d.ts +4 -1
- package/lib/flowContext.js +176 -107
- package/lib/flowEngine.d.ts +21 -0
- package/lib/flowEngine.js +38 -0
- package/lib/flowSettings.js +12 -10
- package/lib/index.d.ts +3 -0
- package/lib/index.js +16 -0
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.d.ts +7 -0
- package/lib/models/flowModel.js +66 -1
- package/lib/provider.js +7 -6
- package/lib/resources/baseRecordResource.d.ts +5 -0
- package/lib/resources/baseRecordResource.js +24 -0
- package/lib/resources/multiRecordResource.d.ts +1 -0
- package/lib/resources/multiRecordResource.js +11 -4
- package/lib/resources/singleRecordResource.js +2 -0
- package/lib/resources/sqlResource.d.ts +1 -0
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/base.js +10 -4
- package/lib/runjsLibs.d.ts +28 -0
- package/lib/runjsLibs.js +532 -0
- package/lib/scheduler/ModelOperationScheduler.d.ts +2 -0
- package/lib/scheduler/ModelOperationScheduler.js +21 -21
- package/lib/types.d.ts +15 -0
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +10 -0
- package/lib/utils/params-resolvers.js +16 -9
- package/lib/utils/resolveModuleUrl.d.ts +58 -0
- package/lib/utils/resolveModuleUrl.js +65 -0
- package/lib/utils/runjsModuleLoader.d.ts +58 -0
- package/lib/utils/runjsModuleLoader.js +422 -0
- package/lib/utils/runjsTemplateCompat.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +743 -0
- package/lib/utils/safeGlobals.d.ts +5 -9
- package/lib/utils/safeGlobals.js +129 -17
- package/lib/views/createViewMeta.d.ts +0 -7
- package/lib/views/createViewMeta.js +19 -70
- package/lib/views/index.d.ts +1 -2
- package/lib/views/index.js +4 -3
- package/lib/views/useDialog.js +8 -3
- package/lib/views/useDrawer.js +7 -2
- package/lib/views/usePage.d.ts +4 -0
- package/lib/views/usePage.js +43 -6
- package/lib/views/usePopover.js +4 -1
- package/lib/views/viewEvents.d.ts +17 -0
- package/lib/views/viewEvents.js +90 -0
- package/package.json +4 -4
- package/src/BlockScopedFlowEngine.ts +2 -5
- package/src/JSRunner.ts +8 -1
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowSettings.open.test.tsx +69 -15
- package/src/__tests__/provider.test.tsx +0 -5
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
- package/src/acl/Acl.tsx +3 -3
- package/src/components/__tests__/gridDragPlanner.test.ts +141 -1
- package/src/components/dnd/gridDragPlanner.ts +60 -0
- package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
- package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +11 -3
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +63 -4
- package/src/components/variables/VariableInput.tsx +8 -2
- package/src/data-source/index.ts +6 -0
- package/src/executor/FlowExecutor.ts +193 -23
- package/src/executor/__tests__/flowExecutor.test.ts +66 -0
- package/src/flowContext.ts +234 -118
- package/src/flowEngine.ts +41 -0
- package/src/flowSettings.ts +12 -11
- package/src/index.ts +10 -0
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +356 -0
- package/src/models/__tests__/flowModel.clone.test.ts +416 -0
- package/src/models/__tests__/flowModel.test.ts +16 -0
- package/src/models/flowModel.tsx +94 -1
- package/src/provider.tsx +9 -7
- package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
- package/src/resources/__tests__/sqlResource.test.ts +60 -0
- package/src/resources/baseRecordResource.ts +31 -0
- package/src/resources/multiRecordResource.ts +11 -4
- package/src/resources/singleRecordResource.ts +3 -0
- package/src/resources/sqlResource.ts +8 -3
- package/src/runjs-context/contexts/base.ts +9 -2
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +23 -21
- package/src/types.ts +26 -1
- package/src/utils/__tests__/params-resolvers.test.ts +40 -0
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
- package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
- package/src/utils/__tests__/safeGlobals.test.ts +49 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/safeGlobals.ts +133 -16
- package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +35 -8
- package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
- package/src/views/createViewMeta.ts +22 -75
- package/src/views/index.tsx +1 -2
- package/src/views/useDialog.tsx +9 -2
- package/src/views/useDrawer.tsx +8 -1
- package/src/views/usePage.tsx +51 -5
- package/src/views/usePopover.tsx +4 -1
- package/src/views/viewEvents.ts +55 -0
package/src/flowSettings.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
shouldHideStepInSettings,
|
|
33
33
|
} from './utils';
|
|
34
34
|
import { FlowStepContext } from './hooks/useFlowStep';
|
|
35
|
+
import { GLOBAL_EMBED_CONTAINER_ID, EMBED_REPLACING_DATA_KEY } from './views';
|
|
35
36
|
|
|
36
37
|
const Panel = Collapse.Panel;
|
|
37
38
|
|
|
@@ -682,14 +683,10 @@ export class FlowSettings {
|
|
|
682
683
|
typeof resolvedUiMode === 'object' && resolvedUiMode ? resolvedUiMode.props || {} : {};
|
|
683
684
|
|
|
684
685
|
if (modeType === 'embed') {
|
|
685
|
-
const target = document.querySelector<HTMLDivElement>(
|
|
686
|
+
const target = document.querySelector<HTMLDivElement>(`#${GLOBAL_EMBED_CONTAINER_ID}`);
|
|
686
687
|
const onOpen = modeProps.onOpen;
|
|
687
688
|
const onClose = modeProps.onClose;
|
|
688
689
|
|
|
689
|
-
if (target) {
|
|
690
|
-
target.innerHTML = ''; // 清空容器内原有内容
|
|
691
|
-
}
|
|
692
|
-
|
|
693
690
|
modeProps = {
|
|
694
691
|
target,
|
|
695
692
|
styles: {
|
|
@@ -699,15 +696,19 @@ export class FlowSettings {
|
|
|
699
696
|
},
|
|
700
697
|
...modeProps,
|
|
701
698
|
onOpen() {
|
|
702
|
-
target
|
|
703
|
-
|
|
704
|
-
|
|
699
|
+
if (target) {
|
|
700
|
+
target.style.width = modeProps.width || '33.3%';
|
|
701
|
+
target.style.maxWidth = modeProps.maxWidth || '800px';
|
|
702
|
+
target.style.minWidth = modeProps.minWidth || '0px';
|
|
703
|
+
}
|
|
705
704
|
onOpen?.();
|
|
706
705
|
},
|
|
707
706
|
onClose() {
|
|
708
|
-
target.
|
|
709
|
-
|
|
710
|
-
|
|
707
|
+
if (target && target.dataset[EMBED_REPLACING_DATA_KEY] !== '1') {
|
|
708
|
+
target.style.width = 'auto';
|
|
709
|
+
target.style.maxWidth = 'none';
|
|
710
|
+
target.style.minWidth = 'auto';
|
|
711
|
+
}
|
|
711
712
|
onClose?.();
|
|
712
713
|
},
|
|
713
714
|
};
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,8 @@ export * from './types';
|
|
|
13
13
|
// 工具函数
|
|
14
14
|
export * from './utils';
|
|
15
15
|
export { compileRunJs } from './utils/jsxTransform';
|
|
16
|
+
export { registerRunJSLib } from './runjsLibs';
|
|
17
|
+
export type { RunJSLibCache, RunJSLibLoader } from './runjsLibs';
|
|
16
18
|
|
|
17
19
|
// 资源类
|
|
18
20
|
export * from './resources';
|
|
@@ -42,6 +44,14 @@ export { setupRunJSContexts } from './runjs-context/setup';
|
|
|
42
44
|
export { getSnippetBody, listSnippetsForContext } from './runjs-context/snippets';
|
|
43
45
|
|
|
44
46
|
export * from './views';
|
|
47
|
+
export {
|
|
48
|
+
DATA_SOURCE_DIRTY_EVENT,
|
|
49
|
+
ENGINE_SCOPE_KEY,
|
|
50
|
+
getEmitterViewActivatedVersion,
|
|
51
|
+
VIEW_ACTIVATED_EVENT,
|
|
52
|
+
VIEW_ACTIVATED_VERSION,
|
|
53
|
+
VIEW_ENGINE_SCOPE,
|
|
54
|
+
} from './views/viewEvents';
|
|
45
55
|
|
|
46
56
|
export * from './FlowDefinition';
|
|
47
57
|
export { createViewScopedEngine } from './ViewScopedFlowEngine';
|
|
@@ -190,7 +190,7 @@ export class CollectionFieldModel<T extends DefaultStructure = DefaultStructure>
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
// Filter the mappings based on the `when` condition
|
|
193
|
-
const bindings = this.bindings.get(interfaceName);
|
|
193
|
+
const bindings = this.bindings.get(interfaceName).sort((a, b) => a.order - b.order);
|
|
194
194
|
return bindings.filter(
|
|
195
195
|
(binding) => ctx.engine.getModelClass(binding.modelName) && binding.when(ctx, collectionField),
|
|
196
196
|
);
|
|
@@ -261,6 +261,7 @@ export class CollectionFieldModel<T extends DefaultStructure = DefaultStructure>
|
|
|
261
261
|
interfaceName: string | string[],
|
|
262
262
|
options: {
|
|
263
263
|
isDefault?: boolean;
|
|
264
|
+
order?: number;
|
|
264
265
|
defaultProps?: object | ((ctx: FlowEngineContext, fieldInstance: CollectionField) => object);
|
|
265
266
|
when?: (ctx: FlowEngineContext, fieldInstance: CollectionField) => boolean;
|
|
266
267
|
} = {},
|
|
@@ -281,6 +282,7 @@ export class CollectionFieldModel<T extends DefaultStructure = DefaultStructure>
|
|
|
281
282
|
isDefault: options.isDefault || false,
|
|
282
283
|
defaultProps: options.defaultProps || null,
|
|
283
284
|
when: options.when || defaultWhen,
|
|
285
|
+
order: options.order,
|
|
284
286
|
});
|
|
285
287
|
|
|
286
288
|
// Update the map
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test, expect } from 'vitest';
|
|
11
|
+
import { FlowEngine } from '../../flowEngine';
|
|
12
|
+
import { FlowModel } from '../flowModel';
|
|
13
|
+
|
|
14
|
+
describe('dispatchEvent dynamic event flow phase (scheduleModelOperation integration)', () => {
|
|
15
|
+
test('default (phase undefined): instance flows run before static flows', async () => {
|
|
16
|
+
const engine = new FlowEngine();
|
|
17
|
+
class M extends FlowModel {}
|
|
18
|
+
engine.registerModels({ M });
|
|
19
|
+
|
|
20
|
+
const calls: string[] = [];
|
|
21
|
+
|
|
22
|
+
M.registerFlow({
|
|
23
|
+
key: 'S',
|
|
24
|
+
on: { eventName: 'go' },
|
|
25
|
+
steps: {
|
|
26
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const model = engine.createModel({ use: 'M' });
|
|
31
|
+
model.registerFlow('D', {
|
|
32
|
+
on: { eventName: 'go' },
|
|
33
|
+
steps: {
|
|
34
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await model.dispatchEvent('go');
|
|
39
|
+
expect(calls).toEqual(['dynamic', 'static-a']);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("phase='afterAllFlows': instance flow runs after static flows", async () => {
|
|
43
|
+
const engine = new FlowEngine();
|
|
44
|
+
class M extends FlowModel {}
|
|
45
|
+
engine.registerModels({ M });
|
|
46
|
+
|
|
47
|
+
const calls: string[] = [];
|
|
48
|
+
|
|
49
|
+
M.registerFlow({
|
|
50
|
+
key: 'S',
|
|
51
|
+
on: { eventName: 'go' },
|
|
52
|
+
steps: {
|
|
53
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const model = engine.createModel({ use: 'M' });
|
|
58
|
+
model.registerFlow('D', {
|
|
59
|
+
on: { eventName: 'go', phase: 'afterAllFlows' },
|
|
60
|
+
steps: {
|
|
61
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await model.dispatchEvent('go');
|
|
66
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("phase='beforeFlow': instance flow runs before the target static flow", async () => {
|
|
70
|
+
const engine = new FlowEngine();
|
|
71
|
+
class M extends FlowModel {}
|
|
72
|
+
engine.registerModels({ M });
|
|
73
|
+
|
|
74
|
+
const calls: string[] = [];
|
|
75
|
+
|
|
76
|
+
M.registerFlow({
|
|
77
|
+
key: 'S',
|
|
78
|
+
on: { eventName: 'go' },
|
|
79
|
+
steps: {
|
|
80
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
81
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const model = engine.createModel({ use: 'M' });
|
|
86
|
+
model.registerFlow('D', {
|
|
87
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'S' },
|
|
88
|
+
steps: {
|
|
89
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await model.dispatchEvent('go');
|
|
94
|
+
expect(calls).toEqual(['dynamic', 'static-a', 'static-b']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("phase='afterFlow': instance flow runs after the target static flow", async () => {
|
|
98
|
+
const engine = new FlowEngine();
|
|
99
|
+
class M extends FlowModel {}
|
|
100
|
+
engine.registerModels({ M });
|
|
101
|
+
|
|
102
|
+
const calls: string[] = [];
|
|
103
|
+
|
|
104
|
+
M.registerFlow({
|
|
105
|
+
key: 'S',
|
|
106
|
+
on: { eventName: 'go' },
|
|
107
|
+
steps: {
|
|
108
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
109
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const model = engine.createModel({ use: 'M' });
|
|
114
|
+
model.registerFlow('D', {
|
|
115
|
+
on: { eventName: 'go', phase: 'afterFlow', flowKey: 'S' },
|
|
116
|
+
steps: {
|
|
117
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await model.dispatchEvent('go');
|
|
122
|
+
expect(calls).toEqual(['static-a', 'static-b', 'dynamic']);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("phase='beforeStep': instance flow runs before the target static step", async () => {
|
|
126
|
+
const engine = new FlowEngine();
|
|
127
|
+
class M extends FlowModel {}
|
|
128
|
+
engine.registerModels({ M });
|
|
129
|
+
|
|
130
|
+
const calls: string[] = [];
|
|
131
|
+
|
|
132
|
+
M.registerFlow({
|
|
133
|
+
key: 'S',
|
|
134
|
+
on: { eventName: 'go' },
|
|
135
|
+
steps: {
|
|
136
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
137
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const model = engine.createModel({ use: 'M' });
|
|
142
|
+
model.registerFlow('D', {
|
|
143
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
144
|
+
steps: {
|
|
145
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await model.dispatchEvent('go');
|
|
150
|
+
expect(calls).toEqual(['dynamic', 'static-a', 'static-b']);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("phase='afterStep': instance flow runs after the target static step", async () => {
|
|
154
|
+
const engine = new FlowEngine();
|
|
155
|
+
class M extends FlowModel {}
|
|
156
|
+
engine.registerModels({ M });
|
|
157
|
+
|
|
158
|
+
const calls: string[] = [];
|
|
159
|
+
|
|
160
|
+
M.registerFlow({
|
|
161
|
+
key: 'S',
|
|
162
|
+
on: { eventName: 'go' },
|
|
163
|
+
steps: {
|
|
164
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
165
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const model = engine.createModel({ use: 'M' });
|
|
170
|
+
model.registerFlow('D', {
|
|
171
|
+
on: { eventName: 'go', phase: 'afterStep', flowKey: 'S', stepKey: 'a' },
|
|
172
|
+
steps: {
|
|
173
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await model.dispatchEvent('go');
|
|
178
|
+
expect(calls).toEqual(['static-a', 'dynamic', 'static-b']);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("phase='beforeFlow' missing flow: falls back to afterAllFlows", async () => {
|
|
182
|
+
const engine = new FlowEngine();
|
|
183
|
+
class M extends FlowModel {}
|
|
184
|
+
engine.registerModels({ M });
|
|
185
|
+
|
|
186
|
+
const calls: string[] = [];
|
|
187
|
+
|
|
188
|
+
M.registerFlow({
|
|
189
|
+
key: 'S',
|
|
190
|
+
on: { eventName: 'go' },
|
|
191
|
+
steps: {
|
|
192
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const model = engine.createModel({ use: 'M' });
|
|
197
|
+
model.registerFlow('D', {
|
|
198
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'missing' },
|
|
199
|
+
steps: {
|
|
200
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await model.dispatchEvent('go');
|
|
205
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("phase='beforeStep' missing step: falls back to afterAllFlows", async () => {
|
|
209
|
+
const engine = new FlowEngine();
|
|
210
|
+
class M extends FlowModel {}
|
|
211
|
+
engine.registerModels({ M });
|
|
212
|
+
|
|
213
|
+
const calls: string[] = [];
|
|
214
|
+
|
|
215
|
+
M.registerFlow({
|
|
216
|
+
key: 'S',
|
|
217
|
+
on: { eventName: 'go' },
|
|
218
|
+
steps: {
|
|
219
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const model = engine.createModel({ use: 'M' });
|
|
224
|
+
model.registerFlow('D', {
|
|
225
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'missing' },
|
|
226
|
+
steps: {
|
|
227
|
+
d: { handler: async () => void calls.push('dynamic') } as any,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await model.dispatchEvent('go');
|
|
232
|
+
expect(calls).toEqual(['static-a', 'dynamic']);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('multiple flows on same anchor: executes by flow.sort asc (stable)', async () => {
|
|
236
|
+
const engine = new FlowEngine();
|
|
237
|
+
class M extends FlowModel {}
|
|
238
|
+
engine.registerModels({ M });
|
|
239
|
+
|
|
240
|
+
const calls: string[] = [];
|
|
241
|
+
|
|
242
|
+
M.registerFlow({
|
|
243
|
+
key: 'S',
|
|
244
|
+
on: { eventName: 'go' },
|
|
245
|
+
steps: {
|
|
246
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const model = engine.createModel({ use: 'M' });
|
|
251
|
+
model.registerFlow('D5', {
|
|
252
|
+
sort: 5,
|
|
253
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
254
|
+
steps: {
|
|
255
|
+
d: { handler: async () => void calls.push('dynamic-5') } as any,
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
model.registerFlow('D0', {
|
|
259
|
+
sort: 0,
|
|
260
|
+
on: { eventName: 'go', phase: 'beforeStep', flowKey: 'S', stepKey: 'a' },
|
|
261
|
+
steps: {
|
|
262
|
+
d: { handler: async () => void calls.push('dynamic-0') } as any,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await model.dispatchEvent('go');
|
|
267
|
+
expect(calls).toEqual(['dynamic-0', 'dynamic-5', 'static-a']);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('dispatchEvent static flow phase (scheduleModelOperation integration)', () => {
|
|
272
|
+
test("phase='beforeFlow': static flow runs before the target static flow", async () => {
|
|
273
|
+
const engine = new FlowEngine();
|
|
274
|
+
class M extends FlowModel {}
|
|
275
|
+
engine.registerModels({ M });
|
|
276
|
+
|
|
277
|
+
const calls: string[] = [];
|
|
278
|
+
|
|
279
|
+
M.registerFlow({
|
|
280
|
+
key: 'S',
|
|
281
|
+
on: { eventName: 'go' },
|
|
282
|
+
steps: {
|
|
283
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
M.registerFlow({
|
|
288
|
+
key: 'P',
|
|
289
|
+
on: { eventName: 'go', phase: 'beforeFlow', flowKey: 'S' },
|
|
290
|
+
steps: {
|
|
291
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const model = engine.createModel({ use: 'M' });
|
|
296
|
+
await model.dispatchEvent('go');
|
|
297
|
+
expect(calls).toEqual(['phase', 'static-a']);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("phase='afterStep': static flow runs after the target static step", async () => {
|
|
301
|
+
const engine = new FlowEngine();
|
|
302
|
+
class M extends FlowModel {}
|
|
303
|
+
engine.registerModels({ M });
|
|
304
|
+
|
|
305
|
+
const calls: string[] = [];
|
|
306
|
+
|
|
307
|
+
M.registerFlow({
|
|
308
|
+
key: 'S',
|
|
309
|
+
on: { eventName: 'go' },
|
|
310
|
+
steps: {
|
|
311
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
312
|
+
b: { handler: async () => void calls.push('static-b') } as any,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
M.registerFlow({
|
|
317
|
+
key: 'P',
|
|
318
|
+
on: { eventName: 'go', phase: 'afterStep', flowKey: 'S', stepKey: 'a' },
|
|
319
|
+
steps: {
|
|
320
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const model = engine.createModel({ use: 'M' });
|
|
325
|
+
await model.dispatchEvent('go');
|
|
326
|
+
expect(calls).toEqual(['static-a', 'phase', 'static-b']);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("phase='afterAllFlows': static flow runs after static flows", async () => {
|
|
330
|
+
const engine = new FlowEngine();
|
|
331
|
+
class M extends FlowModel {}
|
|
332
|
+
engine.registerModels({ M });
|
|
333
|
+
|
|
334
|
+
const calls: string[] = [];
|
|
335
|
+
|
|
336
|
+
M.registerFlow({
|
|
337
|
+
key: 'S',
|
|
338
|
+
on: { eventName: 'go' },
|
|
339
|
+
steps: {
|
|
340
|
+
a: { handler: async () => void calls.push('static-a') } as any,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
M.registerFlow({
|
|
345
|
+
key: 'P',
|
|
346
|
+
on: { eventName: 'go', phase: 'afterAllFlows' },
|
|
347
|
+
steps: {
|
|
348
|
+
p: { handler: async () => void calls.push('phase') } as any,
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const model = engine.createModel({ use: 'M' });
|
|
353
|
+
await model.dispatchEvent('go');
|
|
354
|
+
expect(calls).toEqual(['static-a', 'phase']);
|
|
355
|
+
});
|
|
356
|
+
});
|