@nocobase/flow-engine 2.0.0-beta.9 → 2.0.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/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/FlowDefinition.d.ts +2 -0
- package/lib/JSRunner.d.ts +6 -0
- package/lib/JSRunner.js +32 -2
- package/lib/ViewScopedFlowEngine.js +3 -0
- package/lib/acl/Acl.js +13 -3
- package/lib/components/FlowContextSelector.js +155 -10
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +5 -1
- package/lib/components/variables/VariableInput.js +9 -4
- package/lib/components/variables/VariableTag.js +46 -39
- package/lib/components/variables/utils.d.ts +7 -0
- package/lib/components/variables/utils.js +42 -2
- package/lib/data-source/index.d.ts +7 -27
- package/lib/data-source/index.js +81 -51
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +163 -22
- package/lib/flowContext.d.ts +230 -7
- package/lib/flowContext.js +2267 -148
- package/lib/flowEngine.d.ts +21 -0
- package/lib/flowEngine.js +56 -8
- package/lib/flowI18n.js +6 -4
- package/lib/flowSettings.js +17 -11
- package/lib/index.d.ts +7 -1
- package/lib/index.js +21 -0
- package/lib/locale/en-US.json +9 -2
- package/lib/locale/index.d.ts +14 -0
- package/lib/locale/zh-CN.json +8 -1
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.js +12 -1
- package/lib/provider.js +5 -5
- 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 +4 -3
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/base.js +706 -41
- package/lib/runjs-context/contributions.d.ts +33 -0
- package/lib/runjs-context/contributions.js +88 -0
- package/lib/runjs-context/helpers.js +12 -1
- package/lib/runjs-context/setup.js +6 -0
- package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
- package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
- package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
- package/lib/runjs-context/snippets/index.d.ts +11 -1
- package/lib/runjs-context/snippets/index.js +61 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
- 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 +25 -21
- package/lib/types.d.ts +27 -0
- package/lib/utils/associationObjectVariable.d.ts +2 -2
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/createEphemeralContext.js +2 -2
- package/lib/utils/dateVariable.d.ts +16 -0
- package/lib/utils/dateVariable.js +380 -0
- package/lib/utils/exceptions.d.ts +7 -0
- package/lib/utils/exceptions.js +10 -0
- package/lib/utils/index.d.ts +8 -3
- package/lib/utils/index.js +45 -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/resolveRunJSObjectValues.d.ts +16 -0
- package/lib/utils/resolveRunJSObjectValues.js +61 -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/runjsValue.d.ts +29 -0
- package/lib/utils/runjsValue.js +275 -0
- package/lib/utils/safeGlobals.d.ts +18 -8
- package/lib/utils/safeGlobals.js +164 -17
- package/lib/utils/schema-utils.d.ts +10 -0
- package/lib/utils/schema-utils.js +61 -0
- 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 +7 -2
- 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 +44 -2
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/JSRunner.test.ts +64 -0
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowContext.test.ts +693 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
- package/src/__tests__/flowRuntimeContext.test.ts +2 -1
- package/src/__tests__/flowSettings.open.test.tsx +123 -19
- package/src/__tests__/runjsContext.test.ts +10 -7
- package/src/__tests__/runjsContextImplementations.test.ts +34 -3
- package/src/__tests__/runjsContextRuntime.test.ts +3 -3
- package/src/__tests__/runjsContributions.test.ts +89 -0
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsLocales.test.ts +4 -1
- package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
- package/src/__tests__/runjsSnippets.test.ts +40 -3
- package/src/acl/Acl.tsx +3 -3
- package/src/components/FlowContextSelector.tsx +208 -12
- 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 +109 -16
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +13 -2
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
- package/src/components/variables/VariableInput.tsx +12 -4
- package/src/components/variables/VariableTag.tsx +54 -45
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
- package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
- package/src/components/variables/__tests__/utils.test.ts +81 -3
- package/src/components/variables/utils.ts +67 -6
- package/src/data-source/index.ts +85 -110
- package/src/executor/FlowExecutor.ts +200 -23
- package/src/executor/__tests__/flowExecutor.test.ts +66 -0
- package/src/flowContext.ts +2986 -211
- package/src/flowEngine.ts +59 -8
- package/src/flowI18n.ts +7 -5
- package/src/flowSettings.ts +18 -12
- package/src/index.ts +14 -1
- package/src/locale/en-US.json +9 -2
- package/src/locale/zh-CN.json +8 -1
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +554 -0
- package/src/models/__tests__/flowModel.test.ts +20 -4
- package/src/models/flowModel.tsx +13 -1
- package/src/provider.tsx +7 -6
- 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 +11 -6
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/base.ts +715 -44
- package/src/runjs-context/contributions.ts +88 -0
- package/src/runjs-context/helpers.ts +11 -1
- package/src/runjs-context/setup.ts +6 -0
- package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
- package/src/runjs-context/snippets/index.ts +75 -41
- package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +27 -21
- package/src/types.ts +38 -1
- package/src/utils/__tests__/dateVariable.test.ts +101 -0
- 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__/runjsValue.test.ts +44 -0
- package/src/utils/__tests__/safeGlobals.test.ts +57 -2
- package/src/utils/__tests__/utils.test.ts +95 -0
- package/src/utils/associationObjectVariable.ts +2 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/createEphemeralContext.ts +5 -4
- package/src/utils/dateVariable.ts +397 -0
- package/src/utils/exceptions.ts +11 -0
- package/src/utils/index.ts +37 -3
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/resolveRunJSObjectValues.ts +46 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/runjsValue.ts +287 -0
- package/src/utils/safeGlobals.ts +188 -17
- package/src/utils/schema-utils.ts +79 -0
- 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 +8 -1
- 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/data-source/index.ts
CHANGED
|
@@ -99,7 +99,7 @@ export class DataSource {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
get displayName() {
|
|
102
|
-
return this.
|
|
102
|
+
return this.flowEngine.translate(this.options.displayName, { ns: 'lm-collections' }) || this.key;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
get key() {
|
|
@@ -187,6 +187,9 @@ export interface CollectionOptions {
|
|
|
187
187
|
export class CollectionManager {
|
|
188
188
|
collections: Map<string, Collection>;
|
|
189
189
|
|
|
190
|
+
allCollectionsInheritChain: string[];
|
|
191
|
+
protected childrenCollectionsName: { supportView?: string[]; notSupportView?: string[] } = {};
|
|
192
|
+
|
|
190
193
|
constructor(public dataSource: DataSource) {
|
|
191
194
|
this.collections = observable.shallow<Map<string, Collection>>(new Map());
|
|
192
195
|
}
|
|
@@ -339,117 +342,80 @@ export class CollectionManager {
|
|
|
339
342
|
};
|
|
340
343
|
return getChildrens(name);
|
|
341
344
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
interfaces?: string | string[],
|
|
347
|
-
opts?: {
|
|
348
|
-
dataSource?: string;
|
|
349
|
-
cached?: Record<string, any>;
|
|
350
|
-
collectionNames?: string[];
|
|
351
|
-
/**
|
|
352
|
-
* 为 true 时允许查询所有关联字段
|
|
353
|
-
* 为 Array<string> 时仅允许查询指定的关联字段
|
|
354
|
-
*/
|
|
355
|
-
association?: boolean | string[];
|
|
356
|
-
/**
|
|
357
|
-
* Max depth of recursion
|
|
358
|
-
*/
|
|
359
|
-
maxDepth?: number;
|
|
360
|
-
allowAllTypes?: boolean;
|
|
361
|
-
/**
|
|
362
|
-
* 排除这些接口的字段
|
|
363
|
-
*/
|
|
364
|
-
exceptInterfaces?: string[];
|
|
365
|
-
/**
|
|
366
|
-
* field value 的前缀,用 . 连接,比如 a.b.c
|
|
367
|
-
*/
|
|
368
|
-
prefixFieldValue?: string;
|
|
369
|
-
/**
|
|
370
|
-
* 是否使用 prefixFieldValue 作为 field value
|
|
371
|
-
*/
|
|
372
|
-
usePrefix?: boolean;
|
|
373
|
-
},
|
|
374
|
-
) {
|
|
375
|
-
const {
|
|
376
|
-
association = false,
|
|
377
|
-
cached = {},
|
|
378
|
-
collectionNames = [collectionName],
|
|
379
|
-
maxDepth = 1,
|
|
380
|
-
allowAllTypes = false,
|
|
381
|
-
exceptInterfaces = [],
|
|
382
|
-
prefixFieldValue = '',
|
|
383
|
-
usePrefix = false,
|
|
384
|
-
dataSource: customDataSourceNameValue,
|
|
385
|
-
} = opts || {};
|
|
386
|
-
|
|
387
|
-
if (collectionNames.length - 1 > maxDepth) {
|
|
388
|
-
return;
|
|
345
|
+
getChildrenCollectionsName(name, isSupportView = false) {
|
|
346
|
+
const cacheKey = isSupportView ? 'supportView' : 'notSupportView';
|
|
347
|
+
if (this.childrenCollectionsName[cacheKey]) {
|
|
348
|
+
return this.childrenCollectionsName[cacheKey].slice();
|
|
389
349
|
}
|
|
390
350
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
? association.includes(field.interface)
|
|
415
|
-
: false)),
|
|
416
|
-
)
|
|
417
|
-
?.map((field) => {
|
|
418
|
-
const result: CascaderProps<any>['options'][0] = {
|
|
419
|
-
value: usePrefix && prefixFieldValue ? `${prefixFieldValue}.${field.name}` : field.name,
|
|
420
|
-
label: field?.uiSchema?.title || field.name,
|
|
421
|
-
...field,
|
|
422
|
-
};
|
|
351
|
+
const children: string[] = [];
|
|
352
|
+
const collections = [...this.getCollections()];
|
|
353
|
+
const getChildrenCollectionsInner = (collectionName: string) => {
|
|
354
|
+
const inheritCollections = collections.filter((v: any) => {
|
|
355
|
+
return [...v.inherits]?.includes(collectionName);
|
|
356
|
+
});
|
|
357
|
+
inheritCollections.forEach((v) => {
|
|
358
|
+
const collectionKey = v.name;
|
|
359
|
+
children.push(collectionKey);
|
|
360
|
+
return getChildrenCollectionsInner(collectionKey);
|
|
361
|
+
});
|
|
362
|
+
if (isSupportView) {
|
|
363
|
+
const sourceCollections = collections.filter((v: any) => {
|
|
364
|
+
return [...v.sources]?.length === 1 && v?.sources[0] === collectionName;
|
|
365
|
+
});
|
|
366
|
+
sourceCollections.forEach((v) => {
|
|
367
|
+
const collectionKey = v.name;
|
|
368
|
+
children.push(v.name);
|
|
369
|
+
return getChildrenCollectionsInner(collectionKey);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
return _.uniq(children);
|
|
373
|
+
};
|
|
423
374
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
375
|
+
this.childrenCollectionsName[cacheKey] = getChildrenCollectionsInner(name);
|
|
376
|
+
return this.childrenCollectionsName[cacheKey];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
getAllCollectionsInheritChain(name) {
|
|
380
|
+
if (this.allCollectionsInheritChain) {
|
|
381
|
+
return this.allCollectionsInheritChain.slice();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const collectionsInheritChain = [name];
|
|
385
|
+
const getInheritChain = (name: string) => {
|
|
386
|
+
const collection = this.getCollection(name);
|
|
387
|
+
if (collection) {
|
|
388
|
+
const { inherits } = collection as any;
|
|
389
|
+
const children = this.getChildrenCollectionsName(name);
|
|
390
|
+
// 搜寻祖先表
|
|
391
|
+
if (inherits) {
|
|
392
|
+
for (let index = 0; index < inherits.length; index++) {
|
|
393
|
+
const collectionKey = inherits[index];
|
|
394
|
+
if (collectionsInheritChain.includes(collectionKey)) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
collectionsInheritChain.push(collectionKey);
|
|
398
|
+
getInheritChain(collectionKey);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// 搜寻后代表
|
|
402
|
+
if (children) {
|
|
403
|
+
for (let index = 0; index < children.length; index++) {
|
|
404
|
+
const collection = this.getCollection(children[index]);
|
|
405
|
+
const collectionKey = collection.name;
|
|
406
|
+
if (collectionsInheritChain.includes(collectionKey)) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
collectionsInheritChain.push(collectionKey);
|
|
410
|
+
getInheritChain(collectionKey);
|
|
443
411
|
}
|
|
444
412
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
.filter(Boolean);
|
|
413
|
+
}
|
|
414
|
+
return collectionsInheritChain;
|
|
415
|
+
};
|
|
449
416
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return options;
|
|
417
|
+
this.allCollectionsInheritChain = getInheritChain(name);
|
|
418
|
+
return this.allCollectionsInheritChain || [];
|
|
453
419
|
}
|
|
454
420
|
}
|
|
455
421
|
|
|
@@ -486,6 +452,9 @@ export class Collection {
|
|
|
486
452
|
if (typeof this.filterTargetKey === 'string') {
|
|
487
453
|
return record[this.filterTargetKey];
|
|
488
454
|
}
|
|
455
|
+
if (Array.isArray(this.filterTargetKey) && this.filterTargetKey.length === 1) {
|
|
456
|
+
return record[this.filterTargetKey[0]];
|
|
457
|
+
}
|
|
489
458
|
return _.pick(record, this.filterTargetKey);
|
|
490
459
|
}
|
|
491
460
|
|
|
@@ -527,7 +496,7 @@ export class Collection {
|
|
|
527
496
|
return this.options.storage || 'local';
|
|
528
497
|
}
|
|
529
498
|
get title() {
|
|
530
|
-
return this.
|
|
499
|
+
return this.flowEngine.translate(this.options.title, { ns: 'lm-collections' }) || this.name;
|
|
531
500
|
}
|
|
532
501
|
|
|
533
502
|
get titleCollectionField() {
|
|
@@ -800,8 +769,8 @@ export class CollectionField {
|
|
|
800
769
|
}
|
|
801
770
|
|
|
802
771
|
get title() {
|
|
803
|
-
const titleValue = this.options?.uiSchema?.title || this.options?.title
|
|
804
|
-
return this.flowEngine.translate(titleValue);
|
|
772
|
+
const titleValue = this.options?.uiSchema?.title || this.options?.title;
|
|
773
|
+
return this.flowEngine.translate(titleValue, { ns: 'lm-collections' }) || this.options.name;
|
|
805
774
|
}
|
|
806
775
|
|
|
807
776
|
set title(value: string) {
|
|
@@ -820,11 +789,17 @@ export class CollectionField {
|
|
|
820
789
|
}
|
|
821
790
|
return {
|
|
822
791
|
...v,
|
|
792
|
+
label: v.label ? this.flowEngine.translate(v.label, { ns: 'lm-collections' }) : v.label,
|
|
823
793
|
value: Number(v.value),
|
|
824
794
|
};
|
|
825
795
|
});
|
|
826
796
|
}
|
|
827
|
-
return options
|
|
797
|
+
return options.map((v) => {
|
|
798
|
+
return {
|
|
799
|
+
...v,
|
|
800
|
+
label: this.flowEngine.translate(v.label, { ns: 'lm-collections' }),
|
|
801
|
+
};
|
|
802
|
+
});
|
|
828
803
|
}
|
|
829
804
|
|
|
830
805
|
get defaultValue() {
|
|
@@ -17,10 +17,20 @@ import { FlowExitException, resolveDefaultParams } from '../utils';
|
|
|
17
17
|
import { FlowExitAllException } from '../utils/exceptions';
|
|
18
18
|
import { setupRuntimeContextSteps } from '../utils/setupRuntimeContextSteps';
|
|
19
19
|
import { createEphemeralContext } from '../utils/createEphemeralContext';
|
|
20
|
+
import type { ScheduledCancel } from '../scheduler/ModelOperationScheduler';
|
|
20
21
|
|
|
21
22
|
export class FlowExecutor {
|
|
22
23
|
constructor(private readonly engine: FlowEngine) {}
|
|
23
24
|
|
|
25
|
+
private async emitModelEventIf(
|
|
26
|
+
eventName: string | undefined,
|
|
27
|
+
topic: string,
|
|
28
|
+
payload: Record<string, any>,
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
if (!eventName) return;
|
|
31
|
+
await this.engine.emitter.emitAsync(`model:event:${eventName}:${topic}`, payload);
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
/** Cache wrapper for applyFlow cache lifecycle */
|
|
25
35
|
private async withApplyFlowCache<T>(cacheKey: string | null, executor: () => Promise<T>): Promise<T> {
|
|
26
36
|
if (!cacheKey || !this.engine) return await executor();
|
|
@@ -57,7 +67,13 @@ export class FlowExecutor {
|
|
|
57
67
|
/**
|
|
58
68
|
* Execute a single flow on model.
|
|
59
69
|
*/
|
|
60
|
-
async runFlow(
|
|
70
|
+
async runFlow(
|
|
71
|
+
model: FlowModel,
|
|
72
|
+
flowKey: string,
|
|
73
|
+
inputArgs?: Record<string, any>,
|
|
74
|
+
runId?: string,
|
|
75
|
+
eventName?: string,
|
|
76
|
+
): Promise<any> {
|
|
61
77
|
const flow = model.getFlow(flowKey);
|
|
62
78
|
|
|
63
79
|
if (!flow) {
|
|
@@ -99,6 +115,16 @@ export class FlowExecutor {
|
|
|
99
115
|
setupRuntimeContextSteps(flowContext, stepDefs, model, flowKey);
|
|
100
116
|
const stepsRuntime = flowContext.steps as Record<string, { params: any; uiSchema?: any; result?: any }>;
|
|
101
117
|
|
|
118
|
+
const flowEventBasePayload = {
|
|
119
|
+
uid: model.uid,
|
|
120
|
+
model,
|
|
121
|
+
runId: flowContext.runId,
|
|
122
|
+
inputArgs,
|
|
123
|
+
flowKey,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:start`, flowEventBasePayload);
|
|
127
|
+
|
|
102
128
|
for (const [stepKey, step] of Object.entries(stepDefs) as [string, StepDefinition][]) {
|
|
103
129
|
// Resolve handler and params
|
|
104
130
|
let handler: ActionDefinition<FlowModel, FlowRuntimeContext>['handler'] | undefined;
|
|
@@ -156,6 +182,11 @@ export class FlowExecutor {
|
|
|
156
182
|
);
|
|
157
183
|
continue;
|
|
158
184
|
}
|
|
185
|
+
|
|
186
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:step:${stepKey}:start`, {
|
|
187
|
+
...flowEventBasePayload,
|
|
188
|
+
stepKey,
|
|
189
|
+
});
|
|
159
190
|
const currentStepResult = handler(runtimeCtx, combinedParams);
|
|
160
191
|
const isAwait = step.isAwait !== false;
|
|
161
192
|
lastResult = isAwait ? await currentStepResult : currentStepResult;
|
|
@@ -163,15 +194,47 @@ export class FlowExecutor {
|
|
|
163
194
|
// Store step result and update context
|
|
164
195
|
stepResults[stepKey] = lastResult;
|
|
165
196
|
stepsRuntime[stepKey].result = stepResults[stepKey];
|
|
197
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:step:${stepKey}:end`, {
|
|
198
|
+
...flowEventBasePayload,
|
|
199
|
+
result: lastResult,
|
|
200
|
+
stepKey,
|
|
201
|
+
});
|
|
166
202
|
} catch (error) {
|
|
203
|
+
if (!(error instanceof FlowExitException) && !(error instanceof FlowExitAllException)) {
|
|
204
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:step:${stepKey}:error`, {
|
|
205
|
+
...flowEventBasePayload,
|
|
206
|
+
error,
|
|
207
|
+
stepKey,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
167
210
|
if (error instanceof FlowExitException) {
|
|
168
211
|
flowContext.logger.info(`[FlowEngine] ${error.message}`);
|
|
212
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:step:${stepKey}:end`, {
|
|
213
|
+
...flowEventBasePayload,
|
|
214
|
+
stepKey,
|
|
215
|
+
});
|
|
216
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:end`, {
|
|
217
|
+
...flowEventBasePayload,
|
|
218
|
+
result: stepResults,
|
|
219
|
+
});
|
|
169
220
|
return Promise.resolve(stepResults);
|
|
170
221
|
}
|
|
171
222
|
if (error instanceof FlowExitAllException) {
|
|
172
223
|
flowContext.logger.info(`[FlowEngine] ${error.message}`);
|
|
224
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:step:${stepKey}:end`, {
|
|
225
|
+
...flowEventBasePayload,
|
|
226
|
+
stepKey,
|
|
227
|
+
});
|
|
228
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:end`, {
|
|
229
|
+
...flowEventBasePayload,
|
|
230
|
+
result: error,
|
|
231
|
+
});
|
|
173
232
|
return Promise.resolve(error);
|
|
174
233
|
}
|
|
234
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:error`, {
|
|
235
|
+
...flowEventBasePayload,
|
|
236
|
+
error,
|
|
237
|
+
});
|
|
175
238
|
flowContext.logger.error(
|
|
176
239
|
{ err: error },
|
|
177
240
|
`BaseModel.applyFlow: Error executing step '${stepKey}' in flow '${flowKey}':`,
|
|
@@ -179,11 +242,13 @@ export class FlowExecutor {
|
|
|
179
242
|
return Promise.reject(error);
|
|
180
243
|
}
|
|
181
244
|
}
|
|
245
|
+
await this.emitModelEventIf(eventName, `flow:${flowKey}:end`, {
|
|
246
|
+
...flowEventBasePayload,
|
|
247
|
+
result: stepResults,
|
|
248
|
+
});
|
|
182
249
|
return Promise.resolve(stepResults);
|
|
183
250
|
}
|
|
184
251
|
|
|
185
|
-
// runAutoFlows 已移除:统一通过 dispatchEvent('beforeRender') + useCache 控制
|
|
186
|
-
|
|
187
252
|
/**
|
|
188
253
|
* Dispatch an event to flows bound via flow.on and execute them.
|
|
189
254
|
*/
|
|
@@ -202,14 +267,15 @@ export class FlowExecutor {
|
|
|
202
267
|
|
|
203
268
|
const runId = `${model.uid}-${eventName}-${Date.now()}`;
|
|
204
269
|
const logger = model.context.logger;
|
|
270
|
+
const eventBasePayload = {
|
|
271
|
+
uid: model.uid,
|
|
272
|
+
model,
|
|
273
|
+
runId,
|
|
274
|
+
inputArgs,
|
|
275
|
+
};
|
|
205
276
|
|
|
206
277
|
try {
|
|
207
|
-
await this.
|
|
208
|
-
uid: model.uid,
|
|
209
|
-
model,
|
|
210
|
-
runId,
|
|
211
|
-
inputArgs,
|
|
212
|
-
});
|
|
278
|
+
await this.emitModelEventIf(eventName, 'start', eventBasePayload);
|
|
213
279
|
await model.onDispatchEventStart?.(eventName, options, inputArgs);
|
|
214
280
|
} catch (err) {
|
|
215
281
|
if (isBeforeRender && err instanceof FlowExitException) {
|
|
@@ -237,11 +303,25 @@ export class FlowExecutor {
|
|
|
237
303
|
return false;
|
|
238
304
|
});
|
|
239
305
|
|
|
306
|
+
// 路由系统的“重放打开视图”会再次 dispatchEvent('click'),但这不应重复触发用户配置的动态事件流。
|
|
307
|
+
// 约定:由路由重放触发时,会在 inputArgs 中携带 triggerByRouter: true
|
|
308
|
+
const isRouterReplayClick = eventName === 'click' && inputArgs?.triggerByRouter === true;
|
|
309
|
+
const flowsToRun = isRouterReplayClick
|
|
310
|
+
? flows.filter((flow) => {
|
|
311
|
+
const reg = flow['flowRegistry'] as any;
|
|
312
|
+
const type = reg?.constructor?._type as 'instance' | 'global' | undefined;
|
|
313
|
+
return type !== 'instance';
|
|
314
|
+
})
|
|
315
|
+
: flows;
|
|
316
|
+
|
|
317
|
+
// 记录本次 dispatchEvent 内注册的调度任务,用于在结束/错误后兜底清理未触发的任务
|
|
318
|
+
const scheduledCancels: ScheduledCancel[] = [];
|
|
319
|
+
|
|
240
320
|
// 组装执行函数(返回值用于缓存;beforeRender 返回 results:any[],其它返回 true)
|
|
241
321
|
const execute = async () => {
|
|
242
322
|
if (sequential) {
|
|
243
323
|
// 顺序执行:动态流(实例级)优先,其次静态流;各自组内再按 sort 升序,最后保持原始顺序稳定
|
|
244
|
-
const flowsWithIndex =
|
|
324
|
+
const flowsWithIndex = flowsToRun.map((f, i) => ({ f, i }));
|
|
245
325
|
const ordered = flowsWithIndex
|
|
246
326
|
.slice()
|
|
247
327
|
.sort((a, b) => {
|
|
@@ -259,18 +339,116 @@ export class FlowExecutor {
|
|
|
259
339
|
})
|
|
260
340
|
.map((x) => x.f);
|
|
261
341
|
const results: any[] = [];
|
|
342
|
+
|
|
343
|
+
// 预处理:当事件流配置了 on.phase 时,将其执行移动到指定节点,并从“立即执行列表”中移除
|
|
344
|
+
const staticFlowsByKey = new Map(
|
|
345
|
+
ordered
|
|
346
|
+
.filter((f) => {
|
|
347
|
+
const reg = f['flowRegistry'] as any;
|
|
348
|
+
const type = reg?.constructor?._type as 'instance' | 'global' | undefined;
|
|
349
|
+
return type !== 'instance';
|
|
350
|
+
})
|
|
351
|
+
.map((f) => [f.key, f] as const),
|
|
352
|
+
);
|
|
353
|
+
const scheduled = new Set<string>();
|
|
354
|
+
const scheduleGroups = new Map<string, Array<{ flow: any; order: number }>>();
|
|
355
|
+
ordered.forEach((flow, indexInOrdered) => {
|
|
356
|
+
const on = flow.on;
|
|
357
|
+
const onObj = typeof on === 'object' ? (on as any) : undefined;
|
|
358
|
+
if (!onObj) return;
|
|
359
|
+
|
|
360
|
+
const phase: any = onObj.phase;
|
|
361
|
+
const flowKey: any = onObj.flowKey;
|
|
362
|
+
const stepKey: any = onObj.stepKey;
|
|
363
|
+
|
|
364
|
+
// 默认:beforeAllFlows(保持现有行为)
|
|
365
|
+
if (!phase || phase === 'beforeAllFlows') return;
|
|
366
|
+
|
|
367
|
+
let whenKey: string | null = null;
|
|
368
|
+
if (phase === 'afterAllFlows') {
|
|
369
|
+
whenKey = `event:${eventName}:end`;
|
|
370
|
+
} else if (phase === 'beforeFlow' || phase === 'afterFlow') {
|
|
371
|
+
if (!flowKey) {
|
|
372
|
+
// 配置不完整:降级到“全部静态流之后”
|
|
373
|
+
whenKey = `event:${eventName}:end`;
|
|
374
|
+
} else {
|
|
375
|
+
const anchorFlow = staticFlowsByKey.get(String(flowKey));
|
|
376
|
+
if (anchorFlow) {
|
|
377
|
+
const anchorPhase = phase === 'beforeFlow' ? 'start' : 'end';
|
|
378
|
+
whenKey = `event:${eventName}:flow:${String(flowKey)}:${anchorPhase}`;
|
|
379
|
+
} else {
|
|
380
|
+
// 锚点不存在(flow 被删除或覆盖等):降级到“全部静态流之后”
|
|
381
|
+
whenKey = `event:${eventName}:end`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} else if (phase === 'beforeStep' || phase === 'afterStep') {
|
|
385
|
+
if (!flowKey || !stepKey) {
|
|
386
|
+
// 配置不完整:降级到“全部静态流之后”
|
|
387
|
+
whenKey = `event:${eventName}:end`;
|
|
388
|
+
} else {
|
|
389
|
+
const anchorFlow = staticFlowsByKey.get(String(flowKey));
|
|
390
|
+
const anchorStepExists = !!anchorFlow?.hasStep?.(String(stepKey));
|
|
391
|
+
if (anchorFlow && anchorStepExists) {
|
|
392
|
+
const anchorPhase = phase === 'beforeStep' ? 'start' : 'end';
|
|
393
|
+
whenKey = `event:${eventName}:flow:${String(flowKey)}:step:${String(stepKey)}:${anchorPhase}`;
|
|
394
|
+
} else {
|
|
395
|
+
// 锚点不存在(flow/step 被删除或覆盖等):降级到“全部静态流之后”
|
|
396
|
+
whenKey = `event:${eventName}:end`;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
// 未知 phase:忽略
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!whenKey) return;
|
|
405
|
+
scheduled.add(flow.key);
|
|
406
|
+
const list = scheduleGroups.get(whenKey) || [];
|
|
407
|
+
list.push({ flow, order: indexInOrdered });
|
|
408
|
+
scheduleGroups.set(whenKey, list);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// 注册调度(同锚点按 flow.sort 升序;sort 相同保持稳定顺序)
|
|
412
|
+
for (const [whenKey, list] of scheduleGroups.entries()) {
|
|
413
|
+
const sorted = list.slice().sort((a, b) => {
|
|
414
|
+
const sa = a.flow.sort ?? 0;
|
|
415
|
+
const sb = b.flow.sort ?? 0;
|
|
416
|
+
if (sa !== sb) return sa - sb;
|
|
417
|
+
return a.order - b.order;
|
|
418
|
+
});
|
|
419
|
+
for (const it of sorted) {
|
|
420
|
+
const cancel = model.scheduleModelOperation(
|
|
421
|
+
model.uid,
|
|
422
|
+
async (m) => {
|
|
423
|
+
const res = await this.runFlow(m, it.flow.key, inputArgs, runId, eventName);
|
|
424
|
+
if (res instanceof FlowExitAllException) {
|
|
425
|
+
throw res;
|
|
426
|
+
}
|
|
427
|
+
results.push(res);
|
|
428
|
+
},
|
|
429
|
+
{ when: whenKey as any },
|
|
430
|
+
);
|
|
431
|
+
scheduledCancels.push(cancel);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
262
435
|
for (const flow of ordered) {
|
|
436
|
+
if (scheduled.has(flow.key)) continue;
|
|
263
437
|
try {
|
|
264
438
|
logger.debug(
|
|
265
439
|
`BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}' (sequential).`,
|
|
266
440
|
);
|
|
267
|
-
const result = await this.runFlow(model, flow.key, inputArgs, runId);
|
|
441
|
+
const result = await this.runFlow(model, flow.key, inputArgs, runId, eventName);
|
|
268
442
|
if (result instanceof FlowExitAllException) {
|
|
269
443
|
logger.debug(`[FlowEngine.dispatchEvent] ${result.message}`);
|
|
270
444
|
break; // 终止后续
|
|
271
445
|
}
|
|
272
446
|
results.push(result);
|
|
273
447
|
} catch (error) {
|
|
448
|
+
if (error instanceof FlowExitAllException) {
|
|
449
|
+
logger.debug(`[FlowEngine.dispatchEvent] ${error.message}`);
|
|
450
|
+
break; // 终止后续
|
|
451
|
+
}
|
|
274
452
|
logger.error(
|
|
275
453
|
{ err: error },
|
|
276
454
|
`BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}' (sequential):`,
|
|
@@ -283,10 +461,10 @@ export class FlowExecutor {
|
|
|
283
461
|
|
|
284
462
|
// 并行
|
|
285
463
|
const results = await Promise.all(
|
|
286
|
-
|
|
464
|
+
flowsToRun.map(async (flow) => {
|
|
287
465
|
logger.debug(`BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}'.`);
|
|
288
466
|
try {
|
|
289
|
-
return await this.runFlow(model, flow.key, inputArgs, runId);
|
|
467
|
+
return await this.runFlow(model, flow.key, inputArgs, runId, eventName);
|
|
290
468
|
} catch (error) {
|
|
291
469
|
logger.error(
|
|
292
470
|
{ err: error },
|
|
@@ -318,11 +496,8 @@ export class FlowExecutor {
|
|
|
318
496
|
} catch (hookErr) {
|
|
319
497
|
logger.error({ err: hookErr }, `BaseModel.dispatchEvent: End hook error for event '${eventName}'`);
|
|
320
498
|
}
|
|
321
|
-
await this.
|
|
322
|
-
|
|
323
|
-
model,
|
|
324
|
-
runId,
|
|
325
|
-
inputArgs,
|
|
499
|
+
await this.emitModelEventIf(eventName, 'end', {
|
|
500
|
+
...eventBasePayload,
|
|
326
501
|
result,
|
|
327
502
|
});
|
|
328
503
|
return result;
|
|
@@ -337,14 +512,16 @@ export class FlowExecutor {
|
|
|
337
512
|
{ err: error },
|
|
338
513
|
`BaseModel.dispatchEvent: Error executing event '${eventName}' for model '${model.uid}':`,
|
|
339
514
|
);
|
|
340
|
-
await this.
|
|
341
|
-
|
|
342
|
-
model,
|
|
343
|
-
runId,
|
|
344
|
-
inputArgs,
|
|
515
|
+
await this.emitModelEventIf(eventName, 'error', {
|
|
516
|
+
...eventBasePayload,
|
|
345
517
|
error,
|
|
346
518
|
});
|
|
347
519
|
if (throwOnError) throw error;
|
|
520
|
+
} finally {
|
|
521
|
+
// 清理未触发的调度任务,避免跨事件/跨 runId 残留导致意外执行
|
|
522
|
+
for (const cancel of scheduledCancels) {
|
|
523
|
+
cancel();
|
|
524
|
+
}
|
|
348
525
|
}
|
|
349
526
|
}
|
|
350
527
|
}
|
|
@@ -139,6 +139,72 @@ describe('FlowExecutor', () => {
|
|
|
139
139
|
expect(submitHandler).not.toHaveBeenCalled();
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
it("dispatchEvent('click') skips instance flows when triggerByRouter is true", async () => {
|
|
143
|
+
class MyModel extends FlowModel {}
|
|
144
|
+
|
|
145
|
+
const globalHandler = vi.fn().mockResolvedValue('global-ok');
|
|
146
|
+
MyModel.registerFlow('globalClick', {
|
|
147
|
+
on: 'click',
|
|
148
|
+
steps: {
|
|
149
|
+
s: { handler: globalHandler },
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const instanceHandler = vi.fn().mockResolvedValue('instance-ok');
|
|
154
|
+
const model = new MyModel({
|
|
155
|
+
uid: 'm-click-router-replay',
|
|
156
|
+
flowEngine: engine,
|
|
157
|
+
flowRegistry: {
|
|
158
|
+
instanceClick: {
|
|
159
|
+
on: 'click',
|
|
160
|
+
steps: {
|
|
161
|
+
s: { handler: instanceHandler },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
stepParams: {},
|
|
166
|
+
subModels: {},
|
|
167
|
+
} as FlowModelOptions);
|
|
168
|
+
|
|
169
|
+
await engine.executor.dispatchEvent(model, 'click', { triggerByRouter: true }, { sequential: true });
|
|
170
|
+
|
|
171
|
+
expect(globalHandler).toHaveBeenCalledTimes(1);
|
|
172
|
+
expect(instanceHandler).not.toHaveBeenCalled();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("dispatchEvent('click') keeps instance flows when triggerByRouter is not true", async () => {
|
|
176
|
+
class MyModel extends FlowModel {}
|
|
177
|
+
|
|
178
|
+
const globalHandler = vi.fn().mockResolvedValue('global-ok');
|
|
179
|
+
MyModel.registerFlow('globalClick', {
|
|
180
|
+
on: 'click',
|
|
181
|
+
steps: {
|
|
182
|
+
s: { handler: globalHandler },
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const instanceHandler = vi.fn().mockResolvedValue('instance-ok');
|
|
187
|
+
const model = new MyModel({
|
|
188
|
+
uid: 'm-click-normal',
|
|
189
|
+
flowEngine: engine,
|
|
190
|
+
flowRegistry: {
|
|
191
|
+
instanceClick: {
|
|
192
|
+
on: 'click',
|
|
193
|
+
steps: {
|
|
194
|
+
s: { handler: instanceHandler },
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
stepParams: {},
|
|
199
|
+
subModels: {},
|
|
200
|
+
} as FlowModelOptions);
|
|
201
|
+
|
|
202
|
+
await engine.executor.dispatchEvent(model, 'click', { triggerByRouter: false }, { sequential: true });
|
|
203
|
+
|
|
204
|
+
expect(globalHandler).toHaveBeenCalledTimes(1);
|
|
205
|
+
expect(instanceHandler).toHaveBeenCalledTimes(1);
|
|
206
|
+
});
|
|
207
|
+
|
|
142
208
|
it('dispatchEvent default parallel does not stop on exitAll', async () => {
|
|
143
209
|
const calls: string[] = [];
|
|
144
210
|
const mkFlow = (key: string, opts?: { exitAll?: boolean }) => ({
|