@nocobase/flow-engine 2.1.0-alpha.1 → 2.1.0-alpha.11

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.
Files changed (312) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/BlockScopedFlowEngine.js +0 -1
  4. package/lib/FlowDefinition.d.ts +6 -0
  5. package/lib/FlowSchemaRegistry.d.ts +154 -0
  6. package/lib/FlowSchemaRegistry.js +1427 -0
  7. package/lib/JSRunner.d.ts +15 -0
  8. package/lib/JSRunner.js +82 -7
  9. package/lib/ViewScopedFlowEngine.js +8 -1
  10. package/lib/acl/Acl.js +13 -3
  11. package/lib/components/FlowContextSelector.js +155 -10
  12. package/lib/components/MobilePopup.js +6 -5
  13. package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
  14. package/lib/components/dnd/gridDragPlanner.js +59 -3
  15. package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
  16. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
  17. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
  18. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +21 -3
  19. package/lib/components/subModel/AddSubModelButton.js +27 -1
  20. package/lib/components/subModel/utils.js +2 -2
  21. package/lib/components/variables/VariableInput.js +9 -4
  22. package/lib/components/variables/VariableTag.js +46 -39
  23. package/lib/components/variables/utils.d.ts +7 -0
  24. package/lib/components/variables/utils.js +42 -2
  25. package/lib/data-source/index.d.ts +7 -27
  26. package/lib/data-source/index.js +84 -51
  27. package/lib/executor/FlowExecutor.d.ts +2 -1
  28. package/lib/executor/FlowExecutor.js +190 -26
  29. package/lib/flow-schema-registry/fieldBinding.d.ts +32 -0
  30. package/lib/flow-schema-registry/fieldBinding.js +165 -0
  31. package/lib/flow-schema-registry/modelPatches.d.ts +16 -0
  32. package/lib/flow-schema-registry/modelPatches.js +235 -0
  33. package/lib/flow-schema-registry/schemaInference.d.ts +17 -0
  34. package/lib/flow-schema-registry/schemaInference.js +207 -0
  35. package/lib/flow-schema-registry/utils.d.ts +25 -0
  36. package/lib/flow-schema-registry/utils.js +293 -0
  37. package/lib/flowContext.d.ts +230 -7
  38. package/lib/flowContext.js +2270 -148
  39. package/lib/flowEngine.d.ts +160 -1
  40. package/lib/flowEngine.js +387 -27
  41. package/lib/flowI18n.js +6 -4
  42. package/lib/flowSettings.d.ts +14 -6
  43. package/lib/flowSettings.js +51 -17
  44. package/lib/index.d.ts +8 -1
  45. package/lib/index.js +24 -1
  46. package/lib/lazy-helper.d.ts +14 -0
  47. package/lib/lazy-helper.js +71 -0
  48. package/lib/locale/en-US.json +9 -2
  49. package/lib/locale/index.d.ts +14 -0
  50. package/lib/locale/zh-CN.json +8 -1
  51. package/lib/models/CollectionFieldModel.d.ts +1 -0
  52. package/lib/models/CollectionFieldModel.js +3 -2
  53. package/lib/models/DisplayItemModel.d.ts +1 -1
  54. package/lib/models/EditableItemModel.d.ts +1 -1
  55. package/lib/models/FilterableItemModel.d.ts +1 -1
  56. package/lib/models/flowModel.d.ts +7 -0
  57. package/lib/models/flowModel.js +83 -8
  58. package/lib/provider.js +7 -6
  59. package/lib/resources/baseRecordResource.d.ts +5 -0
  60. package/lib/resources/baseRecordResource.js +24 -0
  61. package/lib/resources/multiRecordResource.d.ts +1 -0
  62. package/lib/resources/multiRecordResource.js +11 -4
  63. package/lib/resources/singleRecordResource.js +2 -0
  64. package/lib/resources/sqlResource.d.ts +4 -3
  65. package/lib/resources/sqlResource.js +8 -3
  66. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
  67. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
  68. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
  69. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
  70. package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
  71. package/lib/runjs-context/contexts/base.js +706 -41
  72. package/lib/runjs-context/contributions.d.ts +33 -0
  73. package/lib/runjs-context/contributions.js +88 -0
  74. package/lib/runjs-context/helpers.js +12 -1
  75. package/lib/runjs-context/registry.d.ts +1 -1
  76. package/lib/runjs-context/setup.js +23 -9
  77. package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
  78. package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
  79. package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
  80. package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
  81. package/lib/runjs-context/snippets/index.d.ts +11 -1
  82. package/lib/runjs-context/snippets/index.js +61 -40
  83. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
  84. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
  85. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
  86. package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
  87. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
  88. package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
  89. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
  90. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
  91. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
  92. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
  93. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
  94. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
  95. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
  96. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
  97. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
  98. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
  99. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
  100. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
  101. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
  102. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
  103. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
  104. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
  105. package/lib/runjsLibs.d.ts +28 -0
  106. package/lib/runjsLibs.js +532 -0
  107. package/lib/scheduler/ModelOperationScheduler.d.ts +7 -1
  108. package/lib/scheduler/ModelOperationScheduler.js +28 -23
  109. package/lib/server.d.ts +10 -0
  110. package/lib/server.js +32 -0
  111. package/lib/types.d.ts +296 -1
  112. package/lib/utils/associationObjectVariable.d.ts +2 -2
  113. package/lib/utils/createCollectionContextMeta.js +1 -0
  114. package/lib/utils/createEphemeralContext.js +2 -2
  115. package/lib/utils/dateVariable.d.ts +16 -0
  116. package/lib/utils/dateVariable.js +380 -0
  117. package/lib/utils/exceptions.d.ts +7 -0
  118. package/lib/utils/exceptions.js +10 -0
  119. package/lib/utils/index.d.ts +8 -3
  120. package/lib/utils/index.js +49 -0
  121. package/lib/utils/params-resolvers.js +16 -9
  122. package/lib/utils/parsePathnameToViewParams.js +1 -1
  123. package/lib/utils/resolveModuleUrl.d.ts +58 -0
  124. package/lib/utils/resolveModuleUrl.js +65 -0
  125. package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
  126. package/lib/utils/resolveRunJSObjectValues.js +61 -0
  127. package/lib/utils/runjsModuleLoader.d.ts +58 -0
  128. package/lib/utils/runjsModuleLoader.js +422 -0
  129. package/lib/utils/runjsTemplateCompat.d.ts +35 -0
  130. package/lib/utils/runjsTemplateCompat.js +743 -0
  131. package/lib/utils/runjsValue.d.ts +29 -0
  132. package/lib/utils/runjsValue.js +275 -0
  133. package/lib/utils/safeGlobals.d.ts +18 -8
  134. package/lib/utils/safeGlobals.js +164 -17
  135. package/lib/utils/schema-utils.d.ts +17 -1
  136. package/lib/utils/schema-utils.js +80 -0
  137. package/lib/views/FlowView.d.ts +7 -1
  138. package/lib/views/createViewMeta.d.ts +0 -7
  139. package/lib/views/createViewMeta.js +19 -70
  140. package/lib/views/index.d.ts +1 -2
  141. package/lib/views/index.js +4 -3
  142. package/lib/views/runViewBeforeClose.d.ts +10 -0
  143. package/lib/views/runViewBeforeClose.js +45 -0
  144. package/lib/views/useDialog.d.ts +2 -1
  145. package/lib/views/useDialog.js +28 -6
  146. package/lib/views/useDrawer.d.ts +2 -1
  147. package/lib/views/useDrawer.js +27 -5
  148. package/lib/views/usePage.d.ts +6 -1
  149. package/lib/views/usePage.js +53 -9
  150. package/lib/views/usePopover.js +4 -1
  151. package/lib/views/viewEvents.d.ts +17 -0
  152. package/lib/views/viewEvents.js +90 -0
  153. package/package.json +5 -5
  154. package/server.d.ts +1 -0
  155. package/server.js +1 -0
  156. package/src/BlockScopedFlowEngine.ts +2 -5
  157. package/src/FlowSchemaRegistry.ts +1799 -0
  158. package/src/JSRunner.ts +111 -5
  159. package/src/ViewScopedFlowEngine.ts +8 -0
  160. package/src/__tests__/FlowSchemaRegistry.test.ts +1951 -0
  161. package/src/__tests__/JSRunner.test.ts +91 -1
  162. package/src/__tests__/createViewMeta.popup.test.ts +62 -1
  163. package/src/__tests__/flow-engine.test.ts +48 -0
  164. package/src/__tests__/flowContext.test.ts +693 -1
  165. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
  166. package/src/__tests__/flowEngine.modelLoaders.test.ts +249 -0
  167. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  168. package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
  169. package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
  170. package/src/__tests__/flowRuntimeContext.test.ts +2 -1
  171. package/src/__tests__/flowSettings.open.test.tsx +123 -19
  172. package/src/__tests__/flowSettings.test.ts +94 -15
  173. package/src/__tests__/provider.test.tsx +0 -5
  174. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  175. package/src/__tests__/runjsContext.test.ts +26 -7
  176. package/src/__tests__/runjsContextImplementations.test.ts +34 -3
  177. package/src/__tests__/runjsContextRuntime.test.ts +5 -3
  178. package/src/__tests__/runjsContributions.test.ts +89 -0
  179. package/src/__tests__/runjsExternalLibs.test.ts +242 -0
  180. package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
  181. package/src/__tests__/runjsLocales.test.ts +4 -1
  182. package/src/__tests__/runjsPreprocessDefault.test.ts +72 -0
  183. package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
  184. package/src/__tests__/runjsSnippets.test.ts +40 -3
  185. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  186. package/src/acl/Acl.tsx +3 -3
  187. package/src/components/FlowContextSelector.tsx +208 -12
  188. package/src/components/MobilePopup.tsx +4 -2
  189. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
  190. package/src/components/__tests__/gridDragPlanner.test.ts +229 -1
  191. package/src/components/dnd/gridDragPlanner.ts +68 -2
  192. package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
  193. package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
  194. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +109 -16
  195. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
  196. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +31 -4
  197. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
  198. package/src/components/subModel/AddSubModelButton.tsx +32 -2
  199. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +143 -32
  200. package/src/components/subModel/utils.ts +1 -1
  201. package/src/components/variables/VariableInput.tsx +12 -4
  202. package/src/components/variables/VariableTag.tsx +54 -45
  203. package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
  204. package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
  205. package/src/components/variables/__tests__/utils.test.ts +81 -3
  206. package/src/components/variables/utils.ts +67 -6
  207. package/src/data-source/index.ts +88 -110
  208. package/src/executor/FlowExecutor.ts +230 -28
  209. package/src/executor/__tests__/flowExecutor.test.ts +123 -0
  210. package/src/flow-schema-registry/fieldBinding.ts +171 -0
  211. package/src/flow-schema-registry/modelPatches.ts +260 -0
  212. package/src/flow-schema-registry/schemaInference.ts +210 -0
  213. package/src/flow-schema-registry/utils.ts +268 -0
  214. package/src/flowContext.ts +2989 -212
  215. package/src/flowEngine.ts +434 -23
  216. package/src/flowI18n.ts +7 -5
  217. package/src/flowSettings.ts +58 -18
  218. package/src/index.ts +15 -1
  219. package/src/lazy-helper.tsx +57 -0
  220. package/src/locale/en-US.json +9 -2
  221. package/src/locale/zh-CN.json +8 -1
  222. package/src/models/CollectionFieldModel.tsx +3 -1
  223. package/src/models/DisplayItemModel.tsx +1 -1
  224. package/src/models/EditableItemModel.tsx +1 -1
  225. package/src/models/FilterableItemModel.tsx +1 -1
  226. package/src/models/__tests__/dispatchEvent.when.test.ts +768 -0
  227. package/src/models/__tests__/flowModel.clone.test.ts +416 -0
  228. package/src/models/__tests__/flowModel.test.ts +20 -4
  229. package/src/models/flowModel.tsx +112 -7
  230. package/src/provider.tsx +9 -7
  231. package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
  232. package/src/resources/__tests__/sqlResource.test.ts +60 -0
  233. package/src/resources/baseRecordResource.ts +31 -0
  234. package/src/resources/multiRecordResource.ts +11 -4
  235. package/src/resources/singleRecordResource.ts +3 -0
  236. package/src/resources/sqlResource.ts +11 -6
  237. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
  238. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
  239. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
  240. package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
  241. package/src/runjs-context/contexts/base.ts +715 -44
  242. package/src/runjs-context/contributions.ts +88 -0
  243. package/src/runjs-context/helpers.ts +11 -1
  244. package/src/runjs-context/registry.ts +1 -1
  245. package/src/runjs-context/setup.ts +25 -9
  246. package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
  247. package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
  248. package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
  249. package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
  250. package/src/runjs-context/snippets/index.ts +75 -41
  251. package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
  252. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
  253. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
  254. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
  255. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
  256. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
  257. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
  258. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
  259. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
  260. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
  261. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
  262. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
  263. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
  264. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
  265. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
  266. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
  267. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
  268. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
  269. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
  270. package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
  271. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
  272. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
  273. package/src/runjsLibs.ts +622 -0
  274. package/src/scheduler/ModelOperationScheduler.ts +41 -24
  275. package/src/server.ts +11 -0
  276. package/src/types.ts +359 -1
  277. package/src/utils/__tests__/dateVariable.test.ts +101 -0
  278. package/src/utils/__tests__/params-resolvers.test.ts +40 -0
  279. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
  280. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
  281. package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
  282. package/src/utils/__tests__/runjsValue.test.ts +44 -0
  283. package/src/utils/__tests__/safeGlobals.test.ts +57 -2
  284. package/src/utils/__tests__/utils.test.ts +157 -0
  285. package/src/utils/associationObjectVariable.ts +2 -2
  286. package/src/utils/createCollectionContextMeta.ts +1 -0
  287. package/src/utils/createEphemeralContext.ts +5 -4
  288. package/src/utils/dateVariable.ts +397 -0
  289. package/src/utils/exceptions.ts +11 -0
  290. package/src/utils/index.ts +38 -3
  291. package/src/utils/params-resolvers.ts +23 -9
  292. package/src/utils/parsePathnameToViewParams.ts +2 -2
  293. package/src/utils/resolveModuleUrl.ts +91 -0
  294. package/src/utils/resolveRunJSObjectValues.ts +46 -0
  295. package/src/utils/runjsModuleLoader.ts +553 -0
  296. package/src/utils/runjsTemplateCompat.ts +828 -0
  297. package/src/utils/runjsValue.ts +287 -0
  298. package/src/utils/safeGlobals.ts +188 -17
  299. package/src/utils/schema-utils.ts +109 -1
  300. package/src/views/FlowView.tsx +11 -1
  301. package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
  302. package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
  303. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +44 -16
  304. package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
  305. package/src/views/createViewMeta.ts +22 -75
  306. package/src/views/index.tsx +1 -2
  307. package/src/views/runViewBeforeClose.ts +19 -0
  308. package/src/views/useDialog.tsx +34 -5
  309. package/src/views/useDrawer.tsx +33 -4
  310. package/src/views/usePage.tsx +63 -8
  311. package/src/views/usePopover.tsx +4 -1
  312. package/src/views/viewEvents.ts +55 -0
@@ -0,0 +1,416 @@
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, it, expect, beforeEach, vi } from 'vitest';
11
+ import { FlowEngine } from '../../flowEngine';
12
+ import { FlowModel } from '../flowModel';
13
+
14
+ describe('FlowModel.clone', () => {
15
+ let flowEngine: FlowEngine;
16
+
17
+ beforeEach(() => {
18
+ flowEngine = new FlowEngine();
19
+ // Mock api for FlowEngineContext
20
+ (flowEngine.context as any).api = {
21
+ auth: {
22
+ role: 'admin',
23
+ locale: 'en-US',
24
+ token: 'mock-token',
25
+ },
26
+ };
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it('should clone a simple model with a new uid', () => {
31
+ const original = flowEngine.createModel({
32
+ uid: 'original-uid',
33
+ use: 'FlowModel',
34
+ props: { title: 'Original Title' },
35
+ stepParams: { testFlow: { step1: { value: 'test' } } },
36
+ });
37
+
38
+ const cloned = original.clone();
39
+
40
+ // uid should be different
41
+ expect(cloned.uid).not.toBe(original.uid);
42
+
43
+ // props should be the same
44
+ expect(cloned.props).toEqual(original.props);
45
+
46
+ // stepParams should be the same
47
+ expect(cloned.stepParams).toEqual(original.stepParams);
48
+
49
+ // should be registered in flowEngine
50
+ expect(flowEngine.getModel(cloned.uid)).toBe(cloned);
51
+ });
52
+
53
+ it('should clone model without parent relationship', () => {
54
+ const parent = flowEngine.createModel({
55
+ uid: 'parent-uid',
56
+ use: 'FlowModel',
57
+ });
58
+
59
+ const child = parent.addSubModel('children', {
60
+ use: 'FlowModel',
61
+ props: { name: 'child' },
62
+ });
63
+
64
+ const cloned = child.clone();
65
+
66
+ // cloned model should not have parent
67
+ expect(cloned.parent).toBeUndefined();
68
+ expect(cloned['_options'].parentId).toBeUndefined();
69
+ });
70
+
71
+ it('should clone model with subModels recursively', () => {
72
+ const parent = flowEngine.createModel({
73
+ uid: 'parent-uid',
74
+ use: 'FlowModel',
75
+ props: { name: 'parent' },
76
+ });
77
+
78
+ const child1 = parent.addSubModel('items', {
79
+ use: 'FlowModel',
80
+ props: { name: 'child1' },
81
+ });
82
+
83
+ const child2 = parent.addSubModel('items', {
84
+ use: 'FlowModel',
85
+ props: { name: 'child2' },
86
+ });
87
+
88
+ const cloned = parent.clone();
89
+
90
+ // parent uid should be different
91
+ expect(cloned.uid).not.toBe(parent.uid);
92
+
93
+ // subModels should exist
94
+ expect(cloned.subModels['items']).toBeDefined();
95
+ expect(cloned.subModels['items']).toHaveLength(2);
96
+
97
+ // subModels uids should be different
98
+ const clonedChildren = cloned.subModels['items'] as FlowModel[];
99
+ expect(clonedChildren[0].uid).not.toBe(child1.uid);
100
+ expect(clonedChildren[1].uid).not.toBe(child2.uid);
101
+
102
+ // subModels props should be the same
103
+ expect(clonedChildren[0].props.name).toBe('child1');
104
+ expect(clonedChildren[1].props.name).toBe('child2');
105
+ });
106
+
107
+ it('should clone model with nested subModels', () => {
108
+ const root = flowEngine.createModel({
109
+ uid: 'root-uid',
110
+ use: 'FlowModel',
111
+ props: { level: 'root' },
112
+ });
113
+
114
+ const level1 = root.addSubModel('children', {
115
+ use: 'FlowModel',
116
+ props: { level: 'level1' },
117
+ });
118
+
119
+ const level2 = level1.addSubModel('children', {
120
+ use: 'FlowModel',
121
+ props: { level: 'level2' },
122
+ });
123
+
124
+ const cloned = root.clone();
125
+
126
+ // All uids should be different
127
+ expect(cloned.uid).not.toBe(root.uid);
128
+
129
+ const clonedLevel1 = (cloned.subModels['children'] as FlowModel[])[0];
130
+ expect(clonedLevel1.uid).not.toBe(level1.uid);
131
+ expect(clonedLevel1.props.level).toBe('level1');
132
+
133
+ const clonedLevel2 = (clonedLevel1.subModels['children'] as FlowModel[])[0];
134
+ expect(clonedLevel2.uid).not.toBe(level2.uid);
135
+ expect(clonedLevel2.props.level).toBe('level2');
136
+ });
137
+
138
+ it('should clone model with object-type subModel', () => {
139
+ const parent = flowEngine.createModel({
140
+ uid: 'parent-uid',
141
+ use: 'FlowModel',
142
+ });
143
+
144
+ const header = parent.setSubModel('header', {
145
+ use: 'FlowModel',
146
+ props: { title: 'Header' },
147
+ });
148
+
149
+ const cloned = parent.clone();
150
+
151
+ // parent uid should be different
152
+ expect(cloned.uid).not.toBe(parent.uid);
153
+
154
+ // object-type subModel should exist with different uid
155
+ const clonedHeader = cloned.subModels['header'] as FlowModel;
156
+ expect(clonedHeader).toBeDefined();
157
+ expect(clonedHeader.uid).not.toBe(header.uid);
158
+ expect(clonedHeader.props.title).toBe('Header');
159
+ });
160
+
161
+ it('should preserve sortIndex when cloning', () => {
162
+ const model = flowEngine.createModel({
163
+ uid: 'test-uid',
164
+ use: 'FlowModel',
165
+ sortIndex: 5,
166
+ });
167
+
168
+ const cloned = model.clone();
169
+
170
+ expect(cloned.sortIndex).toBe(5);
171
+ });
172
+
173
+ it('should preserve stepParams when cloning', () => {
174
+ const model = flowEngine.createModel({
175
+ uid: 'test-uid',
176
+ use: 'FlowModel',
177
+ stepParams: {
178
+ flow1: { step1: { param1: 'value1' } },
179
+ flow2: { step2: { param2: 'value2' } },
180
+ },
181
+ });
182
+
183
+ const cloned = model.clone();
184
+
185
+ expect(cloned.stepParams).toEqual({
186
+ flow1: { step1: { param1: 'value1' } },
187
+ flow2: { step2: { param2: 'value2' } },
188
+ });
189
+ });
190
+
191
+ it('should throw error if flowEngine is not set', () => {
192
+ const model = flowEngine.createModel({
193
+ uid: 'test-uid',
194
+ use: 'FlowModel',
195
+ });
196
+
197
+ // Manually remove flowEngine to simulate edge case
198
+ (model as any).flowEngine = null;
199
+
200
+ expect(() => model.clone()).toThrow('FlowEngine is not set on this model. Please set flowEngine before cloning.');
201
+ });
202
+
203
+ it('should clone model with correct type', () => {
204
+ class CustomModel extends FlowModel {
205
+ customMethod() {
206
+ return 'custom';
207
+ }
208
+ }
209
+
210
+ flowEngine.registerModels({ CustomModel });
211
+
212
+ const original = flowEngine.createModel<CustomModel>({
213
+ uid: 'custom-uid',
214
+ use: 'CustomModel',
215
+ props: { custom: true },
216
+ });
217
+
218
+ const cloned = original.clone<CustomModel>();
219
+
220
+ expect(cloned).toBeInstanceOf(CustomModel);
221
+ expect(cloned.customMethod()).toBe('custom');
222
+ expect(cloned.uid).not.toBe(original.uid);
223
+ });
224
+
225
+ it('should clone model and all cloned models should be independent', () => {
226
+ const original = flowEngine.createModel({
227
+ uid: 'original-uid',
228
+ use: 'FlowModel',
229
+ props: { value: 1 },
230
+ });
231
+
232
+ const cloned = original.clone();
233
+
234
+ // Modify original
235
+ original.setProps({ value: 2 });
236
+
237
+ // Cloned should not be affected
238
+ expect(cloned.props.value).toBe(1);
239
+ expect(original.props.value).toBe(2);
240
+ });
241
+
242
+ it('should clone model with mixed subModels (array and object)', () => {
243
+ const parent = flowEngine.createModel({
244
+ uid: 'parent-uid',
245
+ use: 'FlowModel',
246
+ });
247
+
248
+ // Add array-type subModels
249
+ parent.addSubModel('items', { use: 'FlowModel', props: { type: 'item1' } });
250
+ parent.addSubModel('items', { use: 'FlowModel', props: { type: 'item2' } });
251
+
252
+ // Add object-type subModel
253
+ parent.setSubModel('header', { use: 'FlowModel', props: { type: 'header' } });
254
+ parent.setSubModel('footer', { use: 'FlowModel', props: { type: 'footer' } });
255
+
256
+ const cloned = parent.clone();
257
+
258
+ // Check array-type subModels
259
+ const clonedItems = cloned.subModels['items'] as FlowModel[];
260
+ expect(clonedItems).toHaveLength(2);
261
+ expect(clonedItems[0].props.type).toBe('item1');
262
+ expect(clonedItems[1].props.type).toBe('item2');
263
+
264
+ // Check object-type subModels
265
+ const clonedHeader = cloned.subModels['header'] as FlowModel;
266
+ const clonedFooter = cloned.subModels['footer'] as FlowModel;
267
+ expect(clonedHeader.props.type).toBe('header');
268
+ expect(clonedFooter.props.type).toBe('footer');
269
+
270
+ // All uids should be unique
271
+ const originalItems = parent.subModels['items'] as FlowModel[];
272
+ expect(clonedItems[0].uid).not.toBe(originalItems[0].uid);
273
+ expect(clonedItems[1].uid).not.toBe(originalItems[1].uid);
274
+ expect(clonedHeader.uid).not.toBe((parent.subModels['header'] as FlowModel).uid);
275
+ expect(clonedFooter.uid).not.toBe((parent.subModels['footer'] as FlowModel).uid);
276
+ });
277
+
278
+ it('should correctly remap parentId for subModels to new parent uid', () => {
279
+ const parent = flowEngine.createModel({
280
+ uid: 'parent-uid',
281
+ use: 'FlowModel',
282
+ });
283
+
284
+ const child = parent.addSubModel('children', {
285
+ use: 'FlowModel',
286
+ props: { name: 'child' },
287
+ });
288
+
289
+ // Verify original relationship
290
+ expect(child.parentId).toBe(parent.uid);
291
+
292
+ const cloned = parent.clone();
293
+ const clonedChild = (cloned.subModels['children'] as FlowModel[])[0];
294
+
295
+ // Root model should not have parentId
296
+ expect(cloned.parentId).toBeUndefined();
297
+
298
+ // Child's parentId should be remapped to cloned parent's uid
299
+ expect(clonedChild.parentId).toBe(cloned.uid);
300
+ expect(clonedChild.parentId).not.toBe(parent.uid);
301
+ });
302
+
303
+ it('should correctly remap parentId in deeply nested subModels', () => {
304
+ const root = flowEngine.createModel({
305
+ uid: 'root-uid',
306
+ use: 'FlowModel',
307
+ });
308
+
309
+ const level1 = root.addSubModel('children', {
310
+ use: 'FlowModel',
311
+ });
312
+
313
+ const level2 = level1.addSubModel('children', {
314
+ use: 'FlowModel',
315
+ });
316
+
317
+ const cloned = root.clone();
318
+ const clonedLevel1 = (cloned.subModels['children'] as FlowModel[])[0];
319
+ const clonedLevel2 = (clonedLevel1.subModels['children'] as FlowModel[])[0];
320
+
321
+ // Root should not have parentId
322
+ expect(cloned.parentId).toBeUndefined();
323
+
324
+ // Level1's parentId should point to cloned root
325
+ expect(clonedLevel1.parentId).toBe(cloned.uid);
326
+
327
+ // Level2's parentId should point to cloned level1
328
+ expect(clonedLevel2.parentId).toBe(clonedLevel1.uid);
329
+ });
330
+
331
+ it('should replace uid references in props and other fields', () => {
332
+ const parent = flowEngine.createModel({
333
+ uid: 'parent-uid',
334
+ use: 'FlowModel',
335
+ });
336
+
337
+ const child = parent.addSubModel('children', {
338
+ use: 'FlowModel',
339
+ props: {
340
+ // Store a reference to parent uid in props
341
+ targetUid: 'parent-uid',
342
+ relatedIds: ['parent-uid'],
343
+ nested: { refId: 'parent-uid' },
344
+ },
345
+ });
346
+
347
+ const cloned = parent.clone();
348
+ const clonedChild = (cloned.subModels['children'] as FlowModel[])[0];
349
+
350
+ // Props containing old uid references should be updated to new uids
351
+ expect(clonedChild.props.targetUid).toBe(cloned.uid);
352
+ expect(clonedChild.props.relatedIds[0]).toBe(cloned.uid);
353
+ expect(clonedChild.props.nested.refId).toBe(cloned.uid);
354
+ });
355
+
356
+ it('should replace uid references in stepParams', () => {
357
+ const parent = flowEngine.createModel({
358
+ uid: 'parent-uid',
359
+ use: 'FlowModel',
360
+ });
361
+
362
+ const child = parent.addSubModel('children', {
363
+ use: 'FlowModel',
364
+ stepParams: {
365
+ someFlow: {
366
+ someStep: {
367
+ targetModelUid: 'parent-uid',
368
+ },
369
+ },
370
+ },
371
+ });
372
+
373
+ const cloned = parent.clone();
374
+ const clonedChild = (cloned.subModels['children'] as FlowModel[])[0];
375
+
376
+ // stepParams containing old uid references should be updated
377
+ expect(clonedChild.stepParams.someFlow.someStep.targetModelUid).toBe(cloned.uid);
378
+ });
379
+
380
+ it('should handle self-referencing uid in props', () => {
381
+ const model = flowEngine.createModel({
382
+ uid: 'self-uid',
383
+ use: 'FlowModel',
384
+ props: {
385
+ selfRef: 'self-uid',
386
+ },
387
+ });
388
+
389
+ const cloned = model.clone();
390
+
391
+ // Self-reference should be updated to new uid
392
+ expect(cloned.props.selfRef).toBe(cloned.uid);
393
+ expect(cloned.props.selfRef).not.toBe('self-uid');
394
+ });
395
+
396
+ it('should not replace strings that are not uids', () => {
397
+ const model = flowEngine.createModel({
398
+ uid: 'model-uid',
399
+ use: 'FlowModel',
400
+ props: {
401
+ title: 'Some Title',
402
+ description: 'This is a description',
403
+ count: 42,
404
+ enabled: true,
405
+ },
406
+ });
407
+
408
+ const cloned = model.clone();
409
+
410
+ // Non-uid strings should remain unchanged
411
+ expect(cloned.props.title).toBe('Some Title');
412
+ expect(cloned.props.description).toBe('This is a description');
413
+ expect(cloned.props.count).toBe(42);
414
+ expect(cloned.props.enabled).toBe(true);
415
+ });
416
+ });
@@ -353,7 +353,7 @@ describe('FlowModel', () => {
353
353
  }).toThrow('FlowModel must be initialized with a FlowEngine instance.');
354
354
  });
355
355
 
356
- test('should handle FlowExitException correctly', async () => {
356
+ test('should handle ctx.exit() as FlowExitAllException in applyFlow', async () => {
357
357
  const exitFlow: FlowDefinitionOptions = {
358
358
  key: 'exitFlow',
359
359
  steps: {
@@ -374,14 +374,14 @@ describe('FlowModel', () => {
374
374
 
375
375
  const result = await model.applyFlow('exitFlow');
376
376
 
377
- expect(result).toEqual({});
377
+ expect(result).toBeInstanceOf(FlowExitAllException);
378
378
  expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
379
379
  expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
380
380
 
381
381
  consoleSpy.mockRestore();
382
382
  });
383
383
 
384
- test('should handle FlowExitException correctly', async () => {
384
+ test('should handle ctx.exit() as FlowExitAllException in beforeRender dispatch', async () => {
385
385
  const exitFlow: FlowDefinitionOptions = {
386
386
  key: 'exitFlow',
387
387
  steps: {
@@ -413,7 +413,7 @@ describe('FlowModel', () => {
413
413
  await model.dispatchEvent('beforeRender');
414
414
 
415
415
  expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
416
- expect(exitFlow2.steps.step2.handler).toHaveBeenCalled();
416
+ expect(exitFlow2.steps.step2.handler).not.toHaveBeenCalled();
417
417
  expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowEngine]'));
418
418
 
419
419
  loggerSpy.mockRestore();
@@ -1558,6 +1558,22 @@ describe('FlowModel', () => {
1558
1558
  expect(model.forks.size).toBe(1);
1559
1559
  });
1560
1560
 
1561
+ test('should recreate cached fork after dispose to avoid state leakage', () => {
1562
+ const fork1 = model.createFork({ foo: 'bar' }, 'cacheKey');
1563
+ fork1.hidden = true;
1564
+ fork1.setProps({ disabled: true });
1565
+
1566
+ fork1.dispose();
1567
+
1568
+ expect(model.getFork('cacheKey')).toBeUndefined();
1569
+
1570
+ const fork2 = model.createFork({}, 'cacheKey');
1571
+
1572
+ expect(fork2).not.toBe(fork1);
1573
+ expect(fork2.hidden).toBe(false);
1574
+ expect(fork2.localProps).toEqual({});
1575
+ });
1576
+
1561
1577
  test('should create different instances for different keys', () => {
1562
1578
  const fork1 = model.createFork({}, 'key1');
1563
1579
  const fork2 = model.createFork({}, 'key2');
@@ -11,8 +11,6 @@ import { batch, define, observable, observe } from '@formily/reactive';
11
11
  import _ from 'lodash';
12
12
  import React from 'react';
13
13
  import { uid } from 'uid/secure';
14
- import { openRequiredParamsStepFormDialog as openRequiredParamsStepFormDialogFn } from '../components/settings/wrappers/contextual/StepRequiredSettingsDialog';
15
- import { openStepSettingsDialog as openStepSettingsDialogFn } from '../components/settings/wrappers/contextual/StepSettingsDialog';
16
14
  import { Emitter } from '../emitter';
17
15
  import { InstanceFlowRegistry } from '../flow-registry/InstanceFlowRegistry';
18
16
  import { FlowContext, FlowModelContext, FlowRuntimeContext } from '../flowContext';
@@ -36,7 +34,7 @@ import type {
36
34
  import { IModelComponentProps, ReadonlyModelProps } from '../types';
37
35
  import { isInheritedFrom, setupRuntimeContextSteps } from '../utils';
38
36
  // import { FlowExitAllException } from '../utils/exceptions';
39
- import { Typography } from 'antd/lib';
37
+ import { Typography } from 'antd';
40
38
  import { ModelActionRegistry } from '../action-registry/ModelActionRegistry';
41
39
  import { buildSubModelItem } from '../components/subModel/utils';
42
40
  import { ModelEventRegistry } from '../event-registry/ModelEventRegistry';
@@ -88,6 +86,16 @@ type ExtraMenuItemEntry = {
88
86
 
89
87
  const classMenuExtensions = new WeakMap<typeof FlowModel, Set<ExtraMenuItemEntry>>();
90
88
 
89
+ async function loadOpenStepSettingsDialog() {
90
+ const mod = await import('../components/settings/wrappers/contextual/StepSettingsDialog');
91
+ return mod.openStepSettingsDialog;
92
+ }
93
+
94
+ async function loadOpenRequiredParamsStepFormDialog() {
95
+ const mod = await import('../components/settings/wrappers/contextual/StepRequiredSettingsDialog');
96
+ return mod.openRequiredParamsStepFormDialog;
97
+ }
98
+
91
99
  export enum ModelRenderMode {
92
100
  ReactElement = 'reactElement',
93
101
  RenderFunction = 'renderFunction',
@@ -424,7 +432,19 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
424
432
  const meta = Cls.meta as any;
425
433
  const metaCreate = meta?.createModelOptions;
426
434
  if (metaCreate && typeof metaCreate === 'object' && metaCreate.subModels) {
427
- mergedSubModels = _.merge({}, _.cloneDeep(metaCreate.subModels || {}), _.cloneDeep(subModels || {}));
435
+ const replaceArrays = (objValue: unknown, srcValue: unknown) => {
436
+ if (Array.isArray(objValue) && Array.isArray(srcValue)) {
437
+ // Arrays should be replaced, not merged by index.
438
+ return srcValue;
439
+ }
440
+ return undefined;
441
+ };
442
+ mergedSubModels = _.mergeWith(
443
+ {},
444
+ _.cloneDeep(metaCreate.subModels || {}),
445
+ _.cloneDeep(subModels || {}),
446
+ replaceArrays,
447
+ );
428
448
  }
429
449
  } catch (e) {
430
450
  // Fallback silently if meta defaults resolution fails
@@ -1357,7 +1377,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1357
1377
  * @param {string} stepKey 步骤的唯一标识符
1358
1378
  * @returns {void}
1359
1379
  */
1360
- openStepSettingsDialog(flowKey: string, stepKey: string) {
1380
+ async openStepSettingsDialog(flowKey: string, stepKey: string) {
1361
1381
  // 创建流程运行时上下文
1362
1382
  const flow = this.getFlow(flowKey);
1363
1383
  const step = flow?.steps?.[stepKey];
@@ -1371,7 +1391,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1371
1391
  setupRuntimeContextSteps(ctx, flow.steps, this, flowKey);
1372
1392
  ctx.defineProperty('currentStep', { value: step });
1373
1393
 
1374
- return openStepSettingsDialogFn({
1394
+ const openStepSettingsDialog = await loadOpenStepSettingsDialog();
1395
+
1396
+ return openStepSettingsDialog({
1375
1397
  model: this,
1376
1398
  flowKey,
1377
1399
  stepKey,
@@ -1387,7 +1409,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1387
1409
  * @returns {Promise<any>} 返回表单提交的值
1388
1410
  */
1389
1411
  async configureRequiredSteps(dialogWidth?: number | string, dialogTitle?: string) {
1390
- return openRequiredParamsStepFormDialogFn({
1412
+ const openRequiredParamsStepFormDialog = await loadOpenRequiredParamsStepFormDialog();
1413
+
1414
+ return openRequiredParamsStepFormDialog({
1391
1415
  model: this,
1392
1416
  dialogWidth,
1393
1417
  dialogTitle,
@@ -1444,6 +1468,87 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1444
1468
  return data;
1445
1469
  }
1446
1470
 
1471
+ /**
1472
+ * 复制当前模型实例为一个新的实例。
1473
+ * 新实例及其所有子模型都会有新的 uid,且不保留 root model 的 parent 关系。
1474
+ * 内部所有引用旧 uid 的地方(如 parentId, parentUid 等)都会被替换为对应的新 uid。
1475
+ * @returns {FlowModel} 复制后的新模型实例
1476
+ */
1477
+ clone<T extends FlowModel = this>(): T {
1478
+ if (!this.flowEngine) {
1479
+ throw new Error('FlowEngine is not set on this model. Please set flowEngine before cloning.');
1480
+ }
1481
+
1482
+ // 序列化当前实例
1483
+ const serialized = this.serialize();
1484
+
1485
+ // 第一步:收集所有 uid 并建立 oldUid -> newUid 的映射
1486
+ const uidMap = new Map<string, string>();
1487
+
1488
+ const collectUids = (data: Record<string, any>): void => {
1489
+ if (data.uid && typeof data.uid === 'string') {
1490
+ uidMap.set(data.uid, uid());
1491
+ }
1492
+
1493
+ // 递归处理 subModels
1494
+ if (data.subModels) {
1495
+ for (const key in data.subModels) {
1496
+ const subModel = data.subModels[key];
1497
+ if (Array.isArray(subModel)) {
1498
+ subModel.forEach((item) => collectUids(item));
1499
+ } else if (subModel && typeof subModel === 'object') {
1500
+ collectUids(subModel);
1501
+ }
1502
+ }
1503
+ }
1504
+ };
1505
+
1506
+ collectUids(serialized);
1507
+
1508
+ // 第二步:深度遍历并替换所有 uid 引用
1509
+ const replaceUidReferences = (data: any, isRoot = false): any => {
1510
+ if (data === null || data === undefined) {
1511
+ return data;
1512
+ }
1513
+
1514
+ // 如果是字符串,检查是否是需要替换的 uid
1515
+ if (typeof data === 'string') {
1516
+ return uidMap.get(data) ?? data;
1517
+ }
1518
+
1519
+ // 如果是数组,递归处理每个元素
1520
+ if (Array.isArray(data)) {
1521
+ return data.map((item) => replaceUidReferences(item, false));
1522
+ }
1523
+
1524
+ // 如果是对象,递归处理每个属性
1525
+ if (typeof data === 'object') {
1526
+ const result: Record<string, any> = {};
1527
+
1528
+ for (const key in data) {
1529
+ if (!Object.prototype.hasOwnProperty.call(data, key)) continue;
1530
+
1531
+ // 只删除 root model 的 parentId
1532
+ if (isRoot && key === 'parentId') {
1533
+ continue;
1534
+ }
1535
+
1536
+ result[key] = replaceUidReferences(data[key], false);
1537
+ }
1538
+
1539
+ return result;
1540
+ }
1541
+
1542
+ // 其他类型(number, boolean 等)直接返回
1543
+ return data;
1544
+ };
1545
+
1546
+ const clonedData = replaceUidReferences(serialized, true);
1547
+
1548
+ // 使用 flowEngine 创建新实例
1549
+ return this.flowEngine.createModel<T>(clonedData as any);
1550
+ }
1551
+
1447
1552
  /**
1448
1553
  * Opens the flow settings dialog for this flow model.
1449
1554
  * @param options - Configuration options for opening flow settings, excluding the model property
package/src/provider.tsx CHANGED
@@ -58,18 +58,19 @@ export const FlowEngineGlobalsContextProvider: React.FC<{ children: React.ReactN
58
58
  cache: false,
59
59
  get: (ctx) => new FlowViewer(ctx, { drawer, embed, popover, dialog }),
60
60
  });
61
- // 将 themeToken 定义为 observable, 使组件能够响应主题的变更
62
- engine.context.defineProperty('themeToken', {
63
- get: () => token,
64
- observable: true,
65
- cache: true,
66
- });
67
61
  for (const item of Object.entries(context)) {
68
62
  const [key, value] = item;
69
63
  if (value) {
70
64
  engine.context.defineProperty(key, { value });
71
65
  }
72
66
  }
67
+ // 将 themeToken 定义为 observable, 使组件能够响应主题的变更
68
+ // NOTE: 必须在 antdConfig 写入后再更新 themeToken;否则会读取到旧 antdConfig 的值。
69
+ engine.context.defineProperty('themeToken', {
70
+ get: () => token,
71
+ observable: true,
72
+ cache: true,
73
+ });
73
74
  engine.reactView.refresh();
74
75
  }, [engine, drawer, modal, message, notification, config, popover, token, dialog, embed]);
75
76
 
@@ -90,9 +91,10 @@ export const useFlowEngine = ({ throwError = true } = {}): FlowEngine => {
90
91
  if (!context && throwError) {
91
92
  // This error should ideally not be hit if FlowEngineProvider is used correctly at the root
92
93
  // and always supplied with an engine.
93
- throw new Error(
94
+ console.warn(
94
95
  'useFlowEngine must be used within a FlowEngineProvider, and FlowEngineProvider must be supplied with an engine.',
95
96
  );
97
+ return;
96
98
  }
97
99
  return context;
98
100
  };