@nocobase/flow-engine 2.0.0-alpha.7 → 2.0.0-alpha.71

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 (589) hide show
  1. package/lib/BlockScopedFlowEngine.d.ts +23 -0
  2. package/lib/BlockScopedFlowEngine.js +91 -0
  3. package/lib/FlowContextProvider.d.ts +2 -2
  4. package/lib/FlowContextProvider.js +3 -3
  5. package/lib/FlowDefinition.d.ts +6 -4
  6. package/lib/JSRunner.d.ts +6 -0
  7. package/lib/JSRunner.js +27 -1
  8. package/lib/ViewScopedFlowEngine.d.ts +1 -1
  9. package/lib/ViewScopedFlowEngine.js +18 -1
  10. package/lib/acl/Acl.d.ts +12 -12
  11. package/lib/acl/Acl.js +88 -30
  12. package/lib/components/DynamicFlowsEditor.js +2 -4
  13. package/lib/components/FieldModelRenderer.js +17 -9
  14. package/lib/components/FieldSkeleton.d.ts +10 -0
  15. package/lib/components/FieldSkeleton.js +64 -0
  16. package/lib/components/FlowContextSelector.js +19 -3
  17. package/lib/components/FlowModelRenderer.d.ts +4 -6
  18. package/lib/components/FlowModelRenderer.js +35 -53
  19. package/lib/components/FormItem.js +5 -1
  20. package/lib/components/MobilePopup.d.ts +20 -0
  21. package/lib/components/MobilePopup.js +102 -0
  22. package/lib/components/MobilePopup.style.d.ts +17 -0
  23. package/lib/components/MobilePopup.style.js +186 -0
  24. package/lib/components/common/withFlowDesignMode.d.ts +1 -1
  25. package/lib/components/common/withFlowDesignMode.js +5 -5
  26. package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
  27. package/lib/components/dnd/gridDragPlanner.js +53 -1
  28. package/lib/components/index.d.ts +1 -0
  29. package/lib/components/index.js +3 -1
  30. package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +71 -53
  31. package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +19 -0
  32. package/lib/components/settings/wrappers/component/SelectWithTitle.js +136 -0
  33. package/lib/components/settings/wrappers/component/SwitchWithTitle.d.ts +10 -0
  34. package/lib/components/settings/wrappers/component/SwitchWithTitle.js +111 -0
  35. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +233 -97
  36. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +71 -54
  37. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +2 -2
  38. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +63 -23
  39. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +13 -2
  40. package/lib/components/settings/wrappers/embedded/FlowSettings.js +42 -28
  41. package/lib/components/settings/wrappers/embedded/FlowsSettings.js +3 -3
  42. package/lib/components/settings/wrappers/embedded/FlowsSettingsContent.js +52 -32
  43. package/lib/components/subModel/AddSubModelButton.d.ts +7 -0
  44. package/lib/components/subModel/AddSubModelButton.js +78 -8
  45. package/lib/components/subModel/LazyDropdown.js +14 -15
  46. package/lib/components/subModel/utils.d.ts +1 -1
  47. package/lib/components/subModel/utils.js +21 -11
  48. package/lib/components/variables/VariableInput.js +26 -6
  49. package/lib/components/variables/VariableTag.js +43 -2
  50. package/lib/components/variables/types.d.ts +2 -0
  51. package/lib/components/variables/utils.js +4 -2
  52. package/lib/data-source/index.d.ts +23 -4
  53. package/lib/data-source/index.js +135 -14
  54. package/lib/data-source/jioToJoiSchema.js +1 -0
  55. package/lib/emitter.d.ts +6 -0
  56. package/lib/emitter.js +12 -0
  57. package/lib/executor/FlowExecutor.d.ts +6 -6
  58. package/lib/executor/FlowExecutor.js +302 -99
  59. package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
  60. package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
  61. package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
  62. package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
  63. package/lib/flowContext.d.ts +105 -6
  64. package/lib/flowContext.js +447 -139
  65. package/lib/flowEngine.d.ts +71 -1
  66. package/lib/flowEngine.js +319 -16
  67. package/lib/flowSettings.d.ts +4 -3
  68. package/lib/flowSettings.js +45 -21
  69. package/lib/hooks/useApplyAutoFlows.d.ts +1 -0
  70. package/lib/hooks/useApplyAutoFlows.js +3 -2
  71. package/lib/index.d.ts +14 -3
  72. package/lib/index.js +54 -7
  73. package/lib/locale/de-DE.json +62 -0
  74. package/lib/locale/en-US.json +57 -45
  75. package/lib/locale/es-ES.json +62 -0
  76. package/lib/locale/fr-FR.json +62 -0
  77. package/lib/locale/hu-HU.json +62 -0
  78. package/lib/locale/id-ID.json +62 -0
  79. package/lib/locale/index.d.ts +114 -90
  80. package/lib/locale/it-IT.json +62 -0
  81. package/lib/locale/ja-JP.json +62 -0
  82. package/lib/locale/ko-KR.json +62 -0
  83. package/lib/locale/nl-NL.json +62 -0
  84. package/lib/locale/pt-BR.json +62 -0
  85. package/lib/locale/ru-RU.json +62 -0
  86. package/lib/locale/tr-TR.json +62 -0
  87. package/lib/locale/uk-UA.json +62 -0
  88. package/lib/locale/vi-VN.json +62 -0
  89. package/lib/locale/zh-CN.json +58 -46
  90. package/lib/locale/zh-TW.json +62 -0
  91. package/lib/models/CollectionFieldModel.d.ts +7 -2
  92. package/lib/models/CollectionFieldModel.js +63 -16
  93. package/lib/models/flowModel.d.ts +76 -32
  94. package/lib/models/flowModel.js +300 -112
  95. package/lib/models/forkFlowModel.d.ts +8 -4
  96. package/lib/models/forkFlowModel.js +38 -8
  97. package/lib/provider.d.ts +3 -1
  98. package/lib/provider.js +14 -11
  99. package/lib/reactive/index.d.ts +10 -0
  100. package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → reactive/index.js} +14 -15
  101. package/lib/reactive/observer.d.ts +19 -0
  102. package/lib/reactive/observer.js +109 -0
  103. package/lib/resources/baseRecordResource.d.ts +6 -0
  104. package/lib/resources/baseRecordResource.js +38 -3
  105. package/lib/resources/multiRecordResource.d.ts +5 -2
  106. package/lib/resources/multiRecordResource.js +26 -10
  107. package/lib/resources/singleRecordResource.js +8 -3
  108. package/lib/resources/sqlResource.d.ts +5 -3
  109. package/lib/resources/sqlResource.js +30 -28
  110. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
  111. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +37 -20
  112. package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
  113. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
  114. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
  115. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
  116. package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
  117. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
  118. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
  119. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
  120. package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
  121. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
  122. package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
  123. package/lib/runjs-context/contexts/JSItemRunJSContext.js +34 -20
  124. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
  125. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
  126. package/lib/runjs-context/contexts/base.d.ts +9 -0
  127. package/lib/runjs-context/contexts/base.js +879 -0
  128. package/lib/runjs-context/contributions.d.ts +33 -0
  129. package/lib/runjs-context/contributions.js +88 -0
  130. package/lib/runjs-context/helpers.d.ts +5 -2
  131. package/lib/runjs-context/helpers.js +36 -27
  132. package/lib/runjs-context/registry.d.ts +7 -4
  133. package/lib/runjs-context/registry.js +10 -42
  134. package/lib/runjs-context/setup.d.ts +9 -0
  135. package/lib/runjs-context/setup.js +88 -0
  136. package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
  137. package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
  138. package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
  139. package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
  140. package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
  141. package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
  142. package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
  143. package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
  144. package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
  145. package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
  146. package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
  147. package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
  148. package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
  149. package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
  150. package/lib/runjs-context/snippets/index.d.ts +24 -3
  151. package/lib/runjs-context/snippets/index.js +183 -40
  152. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
  153. package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
  154. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
  155. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
  156. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
  157. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
  158. package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
  159. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.d.ts +11 -0
  160. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.js +65 -0
  161. package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
  162. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
  163. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
  164. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
  165. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
  166. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
  167. package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
  168. package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
  169. package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
  170. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
  171. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
  172. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
  173. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
  174. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
  175. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
  176. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
  177. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
  178. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
  179. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
  180. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
  181. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
  182. package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
  183. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
  184. package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
  185. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
  186. package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
  187. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
  188. package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
  189. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
  190. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
  191. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
  192. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
  193. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
  194. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
  195. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
  196. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
  197. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
  198. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
  199. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
  200. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
  201. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
  202. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
  203. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
  204. package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
  205. package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
  206. package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
  207. package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
  208. package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
  209. package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
  210. package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
  211. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
  212. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
  213. package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
  214. package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
  215. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
  216. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
  217. package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
  218. package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
  219. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
  220. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
  221. package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
  222. package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
  223. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
  224. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
  225. package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
  226. package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
  227. package/lib/runjs-context/snippets/types.d.ts +9 -1
  228. package/lib/runjsLibs.d.ts +28 -0
  229. package/lib/runjsLibs.js +532 -0
  230. package/lib/scheduler/ModelOperationScheduler.d.ts +53 -0
  231. package/lib/scheduler/ModelOperationScheduler.js +262 -0
  232. package/lib/types.d.ts +66 -7
  233. package/lib/types.js +4 -3
  234. package/lib/utils/associationObjectVariable.d.ts +32 -0
  235. package/lib/utils/associationObjectVariable.js +157 -0
  236. package/lib/utils/createCollectionContextMeta.d.ts +1 -1
  237. package/lib/utils/createCollectionContextMeta.js +9 -4
  238. package/lib/utils/createEphemeralContext.d.ts +13 -0
  239. package/lib/utils/createEphemeralContext.js +140 -0
  240. package/lib/utils/flows.d.ts +10 -0
  241. package/lib/{runjs-context/snippets/global/console-log-ctx.snippet.js → utils/flows.js} +21 -14
  242. package/lib/utils/index.d.ts +9 -3
  243. package/lib/utils/index.js +30 -2
  244. package/lib/utils/jsxTransform.d.ts +15 -0
  245. package/lib/utils/jsxTransform.js +68 -0
  246. package/lib/utils/params-resolvers.js +19 -12
  247. package/lib/utils/parsePathnameToViewParams.d.ts +1 -1
  248. package/lib/utils/parsePathnameToViewParams.js +41 -5
  249. package/lib/utils/pruneFilter.d.ts +21 -0
  250. package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
  251. package/lib/utils/resolveModuleUrl.d.ts +58 -0
  252. package/lib/utils/resolveModuleUrl.js +65 -0
  253. package/lib/utils/runjsModuleLoader.d.ts +58 -0
  254. package/lib/utils/runjsModuleLoader.js +422 -0
  255. package/lib/utils/runjsTemplateCompat.d.ts +35 -0
  256. package/lib/utils/runjsTemplateCompat.js +743 -0
  257. package/lib/utils/safeGlobals.d.ts +6 -8
  258. package/lib/utils/safeGlobals.js +169 -14
  259. package/lib/utils/schema-utils.d.ts +6 -0
  260. package/lib/utils/schema-utils.js +71 -6
  261. package/lib/utils/serverContextParams.d.ts +4 -0
  262. package/lib/utils/serverContextParams.js +2 -0
  263. package/lib/utils/translation.d.ts +4 -1
  264. package/lib/utils/translation.js +6 -2
  265. package/lib/utils/variablesParams.d.ts +22 -5
  266. package/lib/utils/variablesParams.js +141 -61
  267. package/lib/views/DialogComponent.js +1 -5
  268. package/lib/views/DrawerComponent.js +18 -9
  269. package/lib/views/PageComponent.js +5 -5
  270. package/lib/views/ViewNavigation.d.ts +11 -15
  271. package/lib/views/ViewNavigation.js +37 -19
  272. package/lib/views/createViewMeta.d.ts +27 -5
  273. package/lib/views/createViewMeta.js +338 -72
  274. package/lib/views/index.d.ts +1 -1
  275. package/lib/views/index.js +4 -0
  276. package/lib/views/useDialog.d.ts +10 -9
  277. package/lib/views/useDialog.js +46 -34
  278. package/lib/views/useDrawer.d.ts +10 -9
  279. package/lib/views/useDrawer.js +74 -48
  280. package/lib/views/usePage.d.ts +14 -9
  281. package/lib/views/usePage.js +82 -33
  282. package/lib/views/usePopover.js +4 -1
  283. package/lib/views/viewEvents.d.ts +17 -0
  284. package/lib/views/viewEvents.js +90 -0
  285. package/package.json +6 -3
  286. package/src/BlockScopedFlowEngine.ts +85 -0
  287. package/src/FlowContextProvider.tsx +4 -2
  288. package/src/JSRunner.ts +39 -1
  289. package/src/ViewScopedFlowEngine.ts +21 -1
  290. package/src/__tests__/JSRunner.test.ts +125 -52
  291. package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
  292. package/src/__tests__/createViewMeta.popup.test.ts +203 -0
  293. package/src/__tests__/flow-engine.test.ts +3 -0
  294. package/src/__tests__/flowContext.test.ts +160 -0
  295. package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
  296. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
  297. package/src/__tests__/flowEngine.destroyModel.test.ts +74 -0
  298. package/src/__tests__/flowEngine.moveModel.test.ts +43 -0
  299. package/src/__tests__/flowEngine.removeModel.test.ts +72 -0
  300. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  301. package/src/__tests__/flowModel.openView.navigation.test.ts +31 -2
  302. package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
  303. package/src/__tests__/flowSettings.open.test.tsx +71 -15
  304. package/src/__tests__/flowSettings.test.ts +2 -0
  305. package/src/__tests__/globalFlowRegistry.test.ts +1 -1
  306. package/src/__tests__/modelOperationScheduler.test.ts +346 -0
  307. package/src/__tests__/objectVariable.test.ts +464 -0
  308. package/src/__tests__/provider.test.tsx +0 -5
  309. package/src/__tests__/runjsContext.test.ts +219 -35
  310. package/src/__tests__/runjsContextImplementations.test.ts +217 -0
  311. package/src/__tests__/runjsContextRuntime.test.ts +269 -0
  312. package/src/__tests__/runjsEdgeCases.test.ts +281 -0
  313. package/src/__tests__/runjsExternalLibs.test.ts +242 -0
  314. package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
  315. package/src/__tests__/runjsLocales.test.ts +39 -0
  316. package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
  317. package/src/__tests__/runjsRuntimeFeatures.test.ts +461 -0
  318. package/src/__tests__/runjsSnippets.test.ts +140 -0
  319. package/src/__tests__/viewScopedFlowEngine.test.ts +101 -3
  320. package/src/acl/Acl.tsx +85 -31
  321. package/src/acl/__tests__/Acl.test.tsx +43 -1
  322. package/src/components/DynamicFlowsEditor.tsx +0 -10
  323. package/src/components/FieldModelRenderer.tsx +22 -9
  324. package/src/components/FieldSkeleton.tsx +27 -0
  325. package/src/components/FlowContextSelector.tsx +20 -2
  326. package/src/components/FlowModelRenderer.tsx +52 -84
  327. package/src/components/FormItem.tsx +8 -1
  328. package/src/components/MobilePopup.style.ts +220 -0
  329. package/src/components/MobilePopup.tsx +86 -0
  330. package/src/components/__tests__/FlowModelRenderer.test.tsx +89 -0
  331. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
  332. package/src/components/__tests__/gridDragPlanner.test.ts +141 -1
  333. package/src/components/common/withFlowDesignMode.tsx +5 -5
  334. package/src/components/dnd/gridDragPlanner.ts +60 -0
  335. package/src/components/index.ts +1 -0
  336. package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +34 -17
  337. package/src/components/settings/wrappers/component/SelectWithTitle.tsx +110 -0
  338. package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +83 -0
  339. package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
  340. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +272 -125
  341. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +34 -18
  342. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +56 -18
  343. package/src/components/settings/wrappers/contextual/StepSettings.tsx +1 -2
  344. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +13 -1
  345. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +624 -0
  346. package/src/components/settings/wrappers/embedded/FlowSettings.tsx +47 -35
  347. package/src/components/settings/wrappers/embedded/FlowsSettings.tsx +1 -1
  348. package/src/components/settings/wrappers/embedded/FlowsSettingsContent.tsx +64 -42
  349. package/src/components/subModel/AddSubModelButton.tsx +104 -9
  350. package/src/components/subModel/LazyDropdown.tsx +14 -14
  351. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +168 -7
  352. package/src/components/subModel/__tests__/utils.test.ts +12 -12
  353. package/src/components/subModel/utils.ts +25 -6
  354. package/src/components/variables/VariableInput.tsx +32 -6
  355. package/src/components/variables/VariableTag.tsx +54 -2
  356. package/src/components/variables/types.ts +2 -0
  357. package/src/components/variables/utils.ts +7 -3
  358. package/src/data-source/index.ts +143 -12
  359. package/src/data-source/jioToJoiSchema.ts +1 -0
  360. package/src/emitter.ts +14 -0
  361. package/src/executor/FlowExecutor.ts +383 -119
  362. package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
  363. package/src/executor/__tests__/flowExecutor.test.ts +217 -5
  364. package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
  365. package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
  366. package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
  367. package/src/flowContext.ts +646 -158
  368. package/src/flowEngine.ts +385 -14
  369. package/src/flowSettings.ts +59 -30
  370. package/src/hooks/useApplyAutoFlows.ts +5 -3
  371. package/src/index.ts +26 -3
  372. package/src/locale/de-DE.json +62 -0
  373. package/src/locale/en-US.json +57 -45
  374. package/src/locale/es-ES.json +62 -0
  375. package/src/locale/fr-FR.json +62 -0
  376. package/src/locale/hu-HU.json +62 -0
  377. package/src/locale/id-ID.json +62 -0
  378. package/src/locale/it-IT.json +62 -0
  379. package/src/locale/ja-JP.json +62 -0
  380. package/src/locale/ko-KR.json +62 -0
  381. package/src/locale/nl-NL.json +62 -0
  382. package/src/locale/pt-BR.json +62 -0
  383. package/src/locale/ru-RU.json +62 -0
  384. package/src/locale/tr-TR.json +62 -0
  385. package/src/locale/uk-UA.json +62 -0
  386. package/src/locale/vi-VN.json +62 -0
  387. package/src/locale/zh-CN.json +58 -46
  388. package/src/locale/zh-TW.json +62 -0
  389. package/src/models/CollectionFieldModel.tsx +82 -18
  390. package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
  391. package/src/models/__tests__/dispatchEvent.when.test.ts +356 -0
  392. package/src/models/__tests__/flowEngine.resolveUse.test.ts +170 -0
  393. package/src/models/__tests__/flowModel.clone.test.ts +416 -0
  394. package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
  395. package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
  396. package/src/models/__tests__/flowModel.test.ts +296 -119
  397. package/src/models/__tests__/forkFlowModel.test.ts +40 -7
  398. package/src/models/flowModel.tsx +426 -148
  399. package/src/models/forkFlowModel.ts +48 -8
  400. package/src/provider.tsx +18 -14
  401. package/src/reactive/__tests__/observer.test.tsx +211 -0
  402. package/src/reactive/index.ts +11 -0
  403. package/src/reactive/observer.tsx +101 -0
  404. package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
  405. package/src/resources/__tests__/sqlResource.test.ts +60 -0
  406. package/src/resources/baseRecordResource.ts +46 -3
  407. package/src/resources/multiRecordResource.ts +28 -12
  408. package/src/resources/singleRecordResource.ts +9 -3
  409. package/src/resources/sqlResource.ts +33 -32
  410. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +38 -21
  411. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +50 -34
  412. package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
  413. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
  414. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
  415. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
  416. package/src/runjs-context/contexts/JSItemRunJSContext.ts +35 -21
  417. package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
  418. package/src/runjs-context/contexts/base.ts +871 -0
  419. package/src/runjs-context/contributions.ts +88 -0
  420. package/src/runjs-context/helpers.ts +32 -30
  421. package/src/runjs-context/registry.ts +16 -47
  422. package/src/runjs-context/setup.ts +57 -0
  423. package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
  424. package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
  425. package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
  426. package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
  427. package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
  428. package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
  429. package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
  430. package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
  431. package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
  432. package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
  433. package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
  434. package/src/runjs-context/snippets/index.ts +212 -39
  435. package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
  436. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
  437. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
  438. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
  439. package/src/runjs-context/snippets/scene/block/render-antd-icons.snippet.ts +46 -0
  440. package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
  441. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
  442. package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
  443. package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
  444. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
  445. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
  446. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
  447. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
  448. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
  449. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
  450. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
  451. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
  452. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
  453. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
  454. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
  455. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
  456. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
  457. package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
  458. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
  459. package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
  460. package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
  461. package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
  462. package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
  463. package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
  464. package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
  465. package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
  466. package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
  467. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
  468. package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
  469. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
  470. package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
  471. package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
  472. package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
  473. package/src/runjs-context/snippets/types.ts +5 -1
  474. package/src/runjsLibs.ts +622 -0
  475. package/src/scheduler/ModelOperationScheduler.ts +306 -0
  476. package/src/types.ts +86 -5
  477. package/src/utils/__tests__/createCollectionContextMeta.test.ts +51 -0
  478. package/src/utils/__tests__/flows.test.ts +65 -0
  479. package/src/utils/__tests__/jsxTransform.test.ts +38 -0
  480. package/src/utils/__tests__/params-resolvers.test.ts +40 -0
  481. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
  482. package/src/utils/__tests__/pruneFilter.test.ts +38 -0
  483. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
  484. package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
  485. package/src/utils/__tests__/safeGlobals.test.ts +79 -2
  486. package/src/utils/__tests__/utils.test.ts +114 -15
  487. package/src/utils/__tests__/variablesParams.test.ts +120 -0
  488. package/src/utils/associationObjectVariable.ts +180 -0
  489. package/src/utils/createCollectionContextMeta.ts +9 -3
  490. package/src/utils/createEphemeralContext.ts +142 -0
  491. package/src/utils/flows.ts +23 -0
  492. package/src/utils/index.ts +17 -3
  493. package/src/utils/jsxTransform.ts +39 -0
  494. package/src/utils/params-resolvers.ts +25 -11
  495. package/src/utils/parsePathnameToViewParams.ts +50 -6
  496. package/src/utils/pruneFilter.ts +41 -0
  497. package/src/utils/resolveModuleUrl.ts +91 -0
  498. package/src/utils/runjsModuleLoader.ts +553 -0
  499. package/src/utils/runjsTemplateCompat.ts +828 -0
  500. package/src/utils/safeGlobals.ts +181 -15
  501. package/src/utils/schema-utils.ts +81 -3
  502. package/src/utils/serverContextParams.ts +6 -0
  503. package/src/utils/translation.ts +7 -2
  504. package/src/utils/variablesParams.ts +164 -72
  505. package/src/views/DialogComponent.tsx +1 -4
  506. package/src/views/DrawerComponent.tsx +19 -7
  507. package/src/views/PageComponent.tsx +3 -5
  508. package/src/views/ViewNavigation.ts +49 -43
  509. package/src/views/__tests__/FlowView.usePage.test.tsx +186 -0
  510. package/src/views/__tests__/ViewNavigation.test.ts +54 -34
  511. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +159 -0
  512. package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
  513. package/src/views/createViewMeta.ts +402 -73
  514. package/src/views/index.tsx +1 -1
  515. package/src/views/useDialog.tsx +52 -31
  516. package/src/views/useDrawer.tsx +99 -55
  517. package/src/views/usePage.tsx +101 -33
  518. package/src/views/usePopover.tsx +4 -1
  519. package/src/views/viewEvents.ts +55 -0
  520. package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
  521. package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
  522. package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
  523. package/lib/runjs-context/index.d.ts +0 -19
  524. package/lib/runjs-context/index.js +0 -57
  525. package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
  526. package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
  527. package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
  528. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
  529. package/lib/runjs-context/snippets/global/requireAsync.snippet.d.ts +0 -16
  530. package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
  531. package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
  532. package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
  533. package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
  534. package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
  535. package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
  536. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
  537. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
  538. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
  539. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
  540. package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
  541. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
  542. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
  543. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
  544. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js +0 -46
  545. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
  546. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
  547. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
  548. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
  549. package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
  550. package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
  551. package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
  552. package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
  553. package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
  554. package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
  555. package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
  556. package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
  557. package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
  558. package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
  559. package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
  560. package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
  561. package/src/runjs-context/index.ts +0 -20
  562. package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
  563. package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
  564. package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
  565. package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
  566. package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
  567. package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
  568. package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
  569. package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
  570. package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
  571. package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
  572. package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
  573. package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
  574. package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
  575. package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
  576. package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
  577. package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
  578. package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
  579. package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
  580. package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
  581. package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
  582. package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
  583. package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
  584. package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
  585. /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
  586. /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
  587. /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
  588. /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
  589. /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
@@ -7,18 +7,17 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { autorun, observable, reaction, reactive } from '@nocobase/flow-engine';
11
- import { APIClient } from '@nocobase/sdk';
10
+ import { reaction } from '@nocobase/flow-engine';
12
11
  import { render, waitFor } from '@testing-library/react';
13
12
  import React from 'react';
14
13
  import { vi } from 'vitest';
15
14
  import { FlowModelRenderer } from '../../components/FlowModelRenderer';
15
+ import { FlowEngineProvider } from '../../provider';
16
16
  import { FlowEngine } from '../../flowEngine';
17
- import type { DefaultStructure, FlowDefinitionOptions, FlowModelOptions, ModelConstructor } from '../../types';
17
+ import type { DefaultStructure, FlowDefinitionOptions, FlowModelOptions } from '../../types';
18
18
  import { FlowExitException } from '../../utils';
19
19
  import { FlowExitAllException } from '../../utils/exceptions';
20
- import { defineFlow, FlowModel, ModelRenderMode } from '../flowModel';
21
- import { ForkFlowModel } from '../forkFlowModel';
20
+ import { FlowModel, ModelRenderMode } from '../flowModel';
22
21
 
23
22
  // 全局处理测试中的未处理 Promise rejection
24
23
  const originalUnhandledRejection = process.listeners('unhandledRejection');
@@ -134,6 +133,14 @@ let modelOptions: FlowModelOptions;
134
133
 
135
134
  beforeEach(() => {
136
135
  flowEngine = createMockFlowEngine();
136
+ // Mock api for FlowEngineContext
137
+ (flowEngine.context as any).api = {
138
+ auth: {
139
+ role: 'admin',
140
+ locale: 'en-US',
141
+ token: 'mock-token',
142
+ },
143
+ };
137
144
  modelOptions = {
138
145
  uid: 'test-model-uid',
139
146
  flowEngine,
@@ -295,6 +302,17 @@ describe('FlowModel', () => {
295
302
  model.setStepParams(null as any);
296
303
  expect(model.stepParams).toEqual(originalParams);
297
304
  });
305
+
306
+ test('should emit onStepParamsChanged when params updated', () => {
307
+ const listener = vi.fn();
308
+ model.emitter.on('onStepParamsChanged', listener);
309
+
310
+ model.setStepParams('flow1', 'step1', { foo: 'bar' });
311
+
312
+ expect(listener).toHaveBeenCalledTimes(1);
313
+
314
+ model.emitter.off('onStepParamsChanged', listener);
315
+ });
298
316
  });
299
317
  });
300
318
 
@@ -392,7 +410,7 @@ describe('FlowModel', () => {
392
410
  TestFlowModel.registerFlow(exitFlow2);
393
411
  const loggerSpy = vi.spyOn(model.context.logger, 'info').mockImplementation(() => {});
394
412
 
395
- await model.applyAutoFlows();
413
+ await model.dispatchEvent('beforeRender');
396
414
 
397
415
  expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
398
416
  expect(exitFlow2.steps.step2.handler).toHaveBeenCalled();
@@ -430,7 +448,7 @@ describe('FlowModel', () => {
430
448
  TestFlowModel.registerFlow(exitFlow2);
431
449
  const loggerSpy = vi.spyOn(model.context.logger, 'info').mockImplementation(() => {});
432
450
 
433
- await model.applyAutoFlows();
451
+ await model.dispatchEvent('beforeRender');
434
452
 
435
453
  expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
436
454
  expect(exitFlow2.steps.step2.handler).not.toHaveBeenCalled();
@@ -530,7 +548,7 @@ describe('FlowModel', () => {
530
548
  });
531
549
  });
532
550
 
533
- describe('applyAutoFlows', () => {
551
+ describe('beforeRender flows', () => {
534
552
  test('should execute all auto flows', async () => {
535
553
  const autoFlow1 = { ...createAutoFlowDefinition(), key: 'auto1', sort: 1 };
536
554
  const autoFlow2 = { ...createAutoFlowDefinition(), key: 'auto2', sort: 2 };
@@ -540,7 +558,7 @@ describe('FlowModel', () => {
540
558
  TestFlowModel.registerFlow(autoFlow2);
541
559
  TestFlowModel.registerFlow(manualFlow);
542
560
 
543
- const results = await model.applyAutoFlows();
561
+ const results = await model.dispatchEvent('beforeRender');
544
562
 
545
563
  expect(results).toHaveLength(2);
546
564
  expect(autoFlow1.steps.autoStep.handler).toHaveBeenCalled();
@@ -594,13 +612,13 @@ describe('FlowModel', () => {
594
612
  TestFlowModel.registerFlow(autoFlow2);
595
613
  TestFlowModel.registerFlow(autoFlow3);
596
614
 
597
- await model.applyAutoFlows();
615
+ await model.dispatchEvent('beforeRender');
598
616
 
599
617
  expect(executionOrder).toEqual(['auto2', 'auto3', 'auto1']);
600
618
  });
601
619
 
602
620
  test('should no results when no auto flows found', async () => {
603
- const results = await model.applyAutoFlows();
621
+ const results = await model.dispatchEvent('beforeRender');
604
622
 
605
623
  expect(results).toEqual([]);
606
624
  // Note: Log output may be captured in stderr, not console.log
@@ -617,16 +635,14 @@ describe('FlowModel', () => {
617
635
  afterHookSpy = vi.fn();
618
636
  errorHookSpy = vi.fn();
619
637
  TestFlowModelWithHooks = class extends TestFlowModel {
620
- async onBeforeAutoFlows(inputArgs?: Record<string, any>) {
621
- beforeHookSpy(inputArgs);
638
+ async onDispatchEventStart(eventName: string, _opts?: any, inputArgs?: Record<string, any>) {
639
+ if (eventName === 'beforeRender') beforeHookSpy(inputArgs);
622
640
  }
623
-
624
- async onAfterAutoFlows(results: any[], inputArgs?: Record<string, any>) {
625
- afterHookSpy(results, inputArgs);
641
+ async onDispatchEventEnd(eventName: string, _opts?: any, inputArgs?: Record<string, any>, results?: any[]) {
642
+ if (eventName === 'beforeRender') afterHookSpy(results, inputArgs);
626
643
  }
627
-
628
- async onAutoFlowsError(error: Error, inputArgs?: Record<string, any>) {
629
- errorHookSpy(error, inputArgs);
644
+ async onDispatchEventError(eventName: string, _opts?: any, inputArgs?: Record<string, any>, error?: Error) {
645
+ if (eventName === 'beforeRender') errorHookSpy(error, inputArgs);
630
646
  }
631
647
  };
632
648
  });
@@ -638,7 +654,7 @@ describe('FlowModel', () => {
638
654
  const modelWithHooks = new TestFlowModelWithHooks(modelOptions);
639
655
  const inputArgs = { test: 'value' };
640
656
 
641
- const results = await modelWithHooks.applyAutoFlows(inputArgs);
657
+ const results = await modelWithHooks.dispatchEvent('beforeRender', inputArgs);
642
658
 
643
659
  // Verify hooks were called
644
660
  expect(beforeHookSpy).toHaveBeenCalledTimes(1);
@@ -654,22 +670,22 @@ describe('FlowModel', () => {
654
670
  );
655
671
  });
656
672
 
657
- test('should allow onBeforeAutoFlows to terminate flow via ctx.exit()', async () => {
673
+ test("should allow onDispatchEventStart('beforeRender') to terminate flow via ctx.exit()", async () => {
658
674
  const autoFlow1 = { ...createAutoFlowDefinition(), key: 'auto1' };
659
675
  const autoFlow2 = { ...createAutoFlowDefinition(), key: 'auto2' };
660
676
 
661
677
  const TestFlowModelWithExitHooks = class extends TestFlowModel {
662
- async onBeforeAutoFlows(inputArgs?: Record<string, any>) {
663
- beforeHookSpy(inputArgs);
664
- throw new FlowExitException('autoFlows', this.uid);
678
+ async onDispatchEventStart(eventName: string, _opts?: any, inputArgs?: Record<string, any>) {
679
+ if (eventName === 'beforeRender') {
680
+ beforeHookSpy(inputArgs);
681
+ throw new FlowExitException('beforeRender', this.uid);
682
+ }
665
683
  }
666
-
667
- async onAfterAutoFlows(results: any[], inputArgs?: Record<string, any>) {
668
- afterHookSpy(results, inputArgs);
684
+ async onDispatchEventEnd(eventName: string, _o?: any, _i?: any, _r?: any[]) {
685
+ if (eventName === 'beforeRender') afterHookSpy(_r, _i);
669
686
  }
670
-
671
- async onAutoFlowsError(error: Error, inputArgs?: Record<string, any>) {
672
- errorHookSpy(error, inputArgs);
687
+ async onDispatchEventError(eventName: string, _o?: any, i?: any, e?: Error) {
688
+ if (eventName === 'beforeRender') errorHookSpy(e, i);
673
689
  }
674
690
  };
675
691
 
@@ -678,9 +694,9 @@ describe('FlowModel', () => {
678
694
  TestFlowModelWithExitHooks.registerFlow(autoFlow2);
679
695
 
680
696
  const modelWithHooks = new TestFlowModelWithExitHooks(modelOptions);
681
- const results = await modelWithHooks.applyAutoFlows();
697
+ const results = await modelWithHooks.dispatchEvent('beforeRender');
682
698
 
683
- // Should have called onBeforeAutoFlows but not onAfterAutoFlows
699
+ // Should have called onDispatchEventStart but not onDispatchEventEnd
684
700
  expect(beforeHookSpy).toHaveBeenCalledTimes(1);
685
701
  expect(afterHookSpy).not.toHaveBeenCalled();
686
702
  expect(errorHookSpy).not.toHaveBeenCalled();
@@ -688,12 +704,12 @@ describe('FlowModel', () => {
688
704
  // Should return empty results since flow was terminated early
689
705
  expect(results).toEqual([]);
690
706
 
691
- // Auto flows should not have been executed
707
+ // flows should not have been executed
692
708
  expect(autoFlow1.steps.autoStep.handler).not.toHaveBeenCalled();
693
709
  expect(autoFlow2.steps.autoStep.handler).not.toHaveBeenCalled();
694
710
  });
695
711
 
696
- test('should call onAutoFlowsError when flow execution fails', async () => {
712
+ test("should call onDispatchEventError('beforeRender') when flow execution fails", async () => {
697
713
  const errorFlow = {
698
714
  key: 'errorFlow',
699
715
 
@@ -708,11 +724,8 @@ describe('FlowModel', () => {
708
724
  TestFlowModelWithHooks.registerFlow(errorFlow);
709
725
 
710
726
  const modelWithHooks = new TestFlowModelWithHooks(modelOptions);
711
-
712
- // 测试错误处理钩子功能
713
- await expect(modelWithHooks.applyAutoFlows()).rejects.toThrow('Test error');
714
-
715
- // Verify hooks were called
727
+ await expect(modelWithHooks.dispatchEvent('beforeRender')).rejects.toThrow('Test error');
728
+ // Verify hooks were called (error path)
716
729
  expect(beforeHookSpy).toHaveBeenCalledTimes(1);
717
730
  expect(afterHookSpy).not.toHaveBeenCalled();
718
731
  expect(errorHookSpy).toHaveBeenCalledTimes(1);
@@ -726,14 +739,14 @@ describe('FlowModel', () => {
726
739
  );
727
740
  });
728
741
 
729
- test('should provide access to step results in onAfterAutoFlows', async () => {
742
+ test('should provide access to step results in onDispatchEventEnd(beforeRender)', async () => {
730
743
  const autoFlow1 = { ...createAutoFlowDefinition(), key: 'auto1' };
731
744
  const autoFlow2 = { ...createAutoFlowDefinition(), key: 'auto2' };
732
745
  TestFlowModelWithHooks.registerFlow(autoFlow1);
733
746
  TestFlowModelWithHooks.registerFlow(autoFlow2);
734
747
 
735
748
  const modelWithHooks = new TestFlowModelWithHooks(modelOptions);
736
- await modelWithHooks.applyAutoFlows();
749
+ await modelWithHooks.dispatchEvent('beforeRender');
737
750
 
738
751
  expect(afterHookSpy).toHaveBeenCalledTimes(1);
739
752
 
@@ -777,6 +790,82 @@ describe('FlowModel', () => {
777
790
  }
778
791
  });
779
792
 
793
+ test('getFlows includes beforeRender and no-on flows; excludes manual and other events', async () => {
794
+ const TestM = class extends FlowModel {};
795
+ const beforeHandler = vi.fn();
796
+ const noOnHandler = vi.fn();
797
+ const manualHandler = vi.fn();
798
+
799
+ TestM.registerFlow({ key: 'beforeA', on: 'beforeRender', sort: 2, steps: { s: { handler: beforeHandler } } });
800
+ TestM.registerFlow({ key: 'noOnB', sort: 1, steps: { s: { handler: noOnHandler } } });
801
+ TestM.registerFlow({ key: 'manualSkip', manual: true, steps: { s: { handler: manualHandler } } });
802
+ TestM.registerFlow({ key: 'otherEvent', on: 'click', steps: { s: { handler: vi.fn() } } });
803
+
804
+ const m = new TestM(modelOptions);
805
+ const autoKeys = m.getEventFlows('beforeRender').map((f) => f.key);
806
+
807
+ // sort 按 1,2 排序:noOnB -> beforeA
808
+ expect(autoKeys).toEqual(['noOnB', 'beforeA']);
809
+
810
+ // beforeRender 会运行两者
811
+ await m.dispatchEvent('beforeRender');
812
+ expect(beforeHandler).toHaveBeenCalledTimes(1);
813
+ expect(noOnHandler).toHaveBeenCalledTimes(1);
814
+ expect(manualHandler).not.toHaveBeenCalled();
815
+ });
816
+
817
+ test('beforeRender executes in sort order mixing no-on and beforeRender', async () => {
818
+ const TestM = class extends FlowModel {};
819
+ const calls: string[] = [];
820
+ TestM.registerFlow({ key: 'noOn1', sort: 1, steps: { s: { handler: () => calls.push('noOn1') } } });
821
+ TestM.registerFlow({
822
+ key: 'before2',
823
+ on: 'beforeRender',
824
+ sort: 2,
825
+ steps: { s: { handler: () => calls.push('before2') } },
826
+ });
827
+ TestM.registerFlow({ key: 'noOn3', sort: 3, steps: { s: { handler: () => calls.push('noOn3') } } });
828
+
829
+ const m = new TestM(modelOptions);
830
+ await m.dispatchEvent('beforeRender');
831
+ expect(calls).toEqual(['noOn1', 'before2', 'noOn3']);
832
+ });
833
+
834
+ test("model.dispatchEvent('beforeRender') uses cache keyed by eventName", async () => {
835
+ const TestM = class extends FlowModel {};
836
+ const handler = vi.fn();
837
+ TestM.registerFlow({ key: 'autoA', steps: { s: { handler } } });
838
+
839
+ const m = new TestM(modelOptions);
840
+ await m.dispatchEvent('beforeRender');
841
+ await m.dispatchEvent('beforeRender');
842
+ expect(handler).toHaveBeenCalledTimes(1);
843
+ });
844
+
845
+ test('invalidFlowCache should clear specific event cache', async () => {
846
+ const TestM = class extends FlowModel {};
847
+ const h1 = vi.fn();
848
+ const h2 = vi.fn();
849
+ TestM.registerFlow({ key: 'onFoo', on: 'foo', steps: { s: { handler: h1 } } });
850
+ TestM.registerFlow({ key: 'onBar', on: 'bar', steps: { s: { handler: h2 } } });
851
+
852
+ const m = new TestM(modelOptions);
853
+ await m.dispatchEvent('foo', {}, { useCache: true });
854
+ await m.dispatchEvent('bar', {}, { useCache: true });
855
+
856
+ // cached
857
+ await m.dispatchEvent('foo', {}, { useCache: true });
858
+ await m.dispatchEvent('bar', {}, { useCache: true });
859
+ expect(h1).toHaveBeenCalledTimes(1);
860
+ expect(h2).toHaveBeenCalledTimes(1);
861
+
862
+ // invalidate foo only
863
+ m.invalidateFlowCache('foo');
864
+ await m.dispatchEvent('foo', {}, { useCache: true });
865
+ await m.dispatchEvent('bar', {}, { useCache: true });
866
+ expect(h1).toHaveBeenCalledTimes(2);
867
+ expect(h2).toHaveBeenCalledTimes(1);
868
+ });
780
869
  test('should handle multiple flows for same event', async () => {
781
870
  const eventFlow1 = { ...createEventFlowDefinition('sharedEvent'), key: 'event1' };
782
871
  const eventFlow2 = { ...createEventFlowDefinition('sharedEvent'), key: 'event2' };
@@ -802,10 +891,17 @@ describe('FlowModel', () => {
802
891
  const _dispatchEventWithDebounceSpy = vi.spyOn(model as any, '_dispatchEventWithDebounce');
803
892
 
804
893
  // Test with debounce enabled
805
- await model.dispatchEvent('debouncedEvent', { data: 'test' }, { debounce: true });
894
+ await model.dispatchEvent('debouncedEvent', { data: 'test' }, { debounce: true, sequential: true });
806
895
 
807
- expect(_dispatchEventWithDebounceSpy).toHaveBeenCalledWith('debouncedEvent', { data: 'test' });
808
- expect(_dispatchEventSpy).not.toHaveBeenCalled();
896
+ expect(_dispatchEventWithDebounceSpy).toHaveBeenCalledWith(
897
+ 'debouncedEvent',
898
+ { data: 'test' },
899
+ expect.objectContaining({ sequential: true }),
900
+ );
901
+ // _dispatchEvent is called by _dispatchEventWithDebounce after debounce
902
+ // So we check it's called at least once eventually
903
+ await new Promise((resolve) => setTimeout(resolve, 150));
904
+ expect(_dispatchEventSpy).toHaveBeenCalledTimes(1);
809
905
 
810
906
  _dispatchEventSpy.mockRestore();
811
907
  _dispatchEventWithDebounceSpy.mockRestore();
@@ -821,7 +917,11 @@ describe('FlowModel', () => {
821
917
  // Test with debounce disabled
822
918
  await model.dispatchEvent('normalEvent', { data: 'test' }, { debounce: false });
823
919
 
824
- expect(_dispatchEventSpy).toHaveBeenCalledWith('normalEvent', { data: 'test' });
920
+ expect(_dispatchEventSpy).toHaveBeenCalledWith(
921
+ 'normalEvent',
922
+ { data: 'test' },
923
+ expect.objectContaining({ sequential: true }),
924
+ );
825
925
  expect(_dispatchEventWithDebounceSpy).not.toHaveBeenCalled();
826
926
 
827
927
  _dispatchEventSpy.mockRestore();
@@ -838,7 +938,11 @@ describe('FlowModel', () => {
838
938
  // Test without debounce option
839
939
  await model.dispatchEvent('defaultEvent', { data: 'test' });
840
940
 
841
- expect(_dispatchEventSpy).toHaveBeenCalledWith('defaultEvent', { data: 'test' });
941
+ expect(_dispatchEventSpy).toHaveBeenCalledWith(
942
+ 'defaultEvent',
943
+ { data: 'test' },
944
+ expect.objectContaining({ sequential: true }),
945
+ );
842
946
  expect(_dispatchEventWithDebounceSpy).not.toHaveBeenCalled();
843
947
 
844
948
  _dispatchEventSpy.mockRestore();
@@ -855,7 +959,11 @@ describe('FlowModel', () => {
855
959
  // Test with undefined options
856
960
  await model.dispatchEvent('undefinedOptionsEvent', { data: 'test' }, undefined);
857
961
 
858
- expect(_dispatchEventSpy).toHaveBeenCalledWith('undefinedOptionsEvent', { data: 'test' });
962
+ expect(_dispatchEventSpy).toHaveBeenCalledWith(
963
+ 'undefinedOptionsEvent',
964
+ { data: 'test' },
965
+ expect.objectContaining({ sequential: true }),
966
+ );
859
967
  expect(_dispatchEventWithDebounceSpy).not.toHaveBeenCalled();
860
968
 
861
969
  _dispatchEventSpy.mockRestore();
@@ -881,7 +989,7 @@ describe('FlowModel', () => {
881
989
  expect(handlerSpy).toHaveBeenCalledTimes(1);
882
990
  expect(handlerSpy).toHaveBeenLastCalledWith(
883
991
  expect.objectContaining({
884
- inputArgs: { call: 3 },
992
+ inputArgs: expect.objectContaining({ call: 1 }),
885
993
  }),
886
994
  expect.any(Object),
887
995
  );
@@ -904,21 +1012,21 @@ describe('FlowModel', () => {
904
1012
  expect(handlerSpy).toHaveBeenNthCalledWith(
905
1013
  1,
906
1014
  expect.objectContaining({
907
- inputArgs: { call: 1 },
1015
+ inputArgs: expect.objectContaining({ call: 1 }),
908
1016
  }),
909
1017
  expect.any(Object),
910
1018
  );
911
1019
  expect(handlerSpy).toHaveBeenNthCalledWith(
912
1020
  2,
913
1021
  expect.objectContaining({
914
- inputArgs: { call: 2 },
1022
+ inputArgs: expect.objectContaining({ call: 2 }),
915
1023
  }),
916
1024
  expect.any(Object),
917
1025
  );
918
1026
  expect(handlerSpy).toHaveBeenNthCalledWith(
919
1027
  3,
920
1028
  expect.objectContaining({
921
- inputArgs: { call: 3 },
1029
+ inputArgs: expect.objectContaining({ call: 3 }),
922
1030
  }),
923
1031
  expect.any(Object),
924
1032
  );
@@ -946,14 +1054,14 @@ describe('FlowModel', () => {
946
1054
  expect(handlerSpy).toHaveBeenNthCalledWith(
947
1055
  1,
948
1056
  expect.objectContaining({
949
- inputArgs: { type: 'immediate' },
1057
+ inputArgs: expect.objectContaining({ type: 'immediate' }),
950
1058
  }),
951
1059
  expect.any(Object),
952
1060
  );
953
1061
  expect(handlerSpy).toHaveBeenNthCalledWith(
954
1062
  2,
955
1063
  expect.objectContaining({
956
- inputArgs: { type: 'debounced', call: 2 },
1064
+ inputArgs: expect.objectContaining({ type: 'debounced', call: 1 }),
957
1065
  }),
958
1066
  expect.any(Object),
959
1067
  );
@@ -981,7 +1089,7 @@ describe('FlowModel', () => {
981
1089
  expect(handlerSpy).toHaveBeenCalledTimes(1);
982
1090
  expect(handlerSpy).toHaveBeenCalledWith(
983
1091
  expect.objectContaining({
984
- inputArgs,
1092
+ inputArgs: expect.objectContaining(inputArgs),
985
1093
  }),
986
1094
  expect.any(Object),
987
1095
  );
@@ -1265,41 +1373,63 @@ describe('FlowModel', () => {
1265
1373
  });
1266
1374
  });
1267
1375
 
1268
- describe('applySubModelsAutoFlows', () => {
1376
+ describe('applySubModelsBeforeRenderFlows', () => {
1269
1377
  test('should apply auto flows to all array subModels', async () => {
1270
1378
  const child1 = new FlowModel({ uid: 'child1', flowEngine });
1271
1379
  const child2 = new FlowModel({ uid: 'child2', flowEngine });
1272
1380
 
1273
- child1.applyAutoFlows = vi.fn().mockResolvedValue([]);
1274
- child2.applyAutoFlows = vi.fn().mockResolvedValue([]);
1381
+ child1.dispatchEvent = vi.fn().mockResolvedValue(undefined) as any;
1382
+ child2.dispatchEvent = vi.fn().mockResolvedValue(undefined) as any;
1275
1383
 
1276
1384
  parentModel.addSubModel('children', child1);
1277
1385
  parentModel.addSubModel('children', child2);
1278
1386
 
1279
1387
  const runtimeData = { test: 'extra' };
1280
1388
 
1281
- await parentModel.applySubModelsAutoFlows('children', runtimeData);
1389
+ await parentModel.applySubModelsBeforeRenderFlows('children', runtimeData);
1282
1390
 
1283
- expect(child1.applyAutoFlows).toHaveBeenCalledWith(runtimeData);
1284
- expect(child2.applyAutoFlows).toHaveBeenCalledWith(runtimeData);
1391
+ expect(child1.dispatchEvent).toHaveBeenCalledWith('beforeRender', runtimeData);
1392
+ expect(child2.dispatchEvent).toHaveBeenCalledWith('beforeRender', runtimeData);
1285
1393
  });
1286
1394
 
1287
1395
  test('should apply auto flows to single subModel', async () => {
1288
1396
  const child = new FlowModel({ uid: 'child', flowEngine });
1289
1397
 
1290
- child.applyAutoFlows = vi.fn().mockResolvedValue([]);
1398
+ child.dispatchEvent = vi.fn().mockResolvedValue(undefined) as any;
1291
1399
 
1292
1400
  parentModel.setSubModel('child', child);
1293
1401
 
1294
1402
  const runtimeData = { test: 'extra' };
1295
1403
 
1296
- await parentModel.applySubModelsAutoFlows('child', runtimeData);
1404
+ await parentModel.applySubModelsBeforeRenderFlows('child', runtimeData);
1297
1405
 
1298
- expect(child.applyAutoFlows).toHaveBeenCalledWith(runtimeData);
1406
+ expect(child.dispatchEvent).toHaveBeenCalledWith('beforeRender', runtimeData);
1299
1407
  });
1300
1408
 
1301
1409
  test('should handle empty subModels gracefully', async () => {
1302
- await expect(parentModel.applySubModelsAutoFlows('nonExistent')).resolves.not.toThrow();
1410
+ await expect(parentModel.applySubModelsBeforeRenderFlows('nonExistent')).resolves.not.toThrow();
1411
+ });
1412
+ });
1413
+
1414
+ describe('beforeRender cache invalidation on inputArgs change', () => {
1415
+ test('should rerun when inputArgs changed and reuse when equal', async () => {
1416
+ const TestM = class extends FlowModel {};
1417
+ const handler = vi.fn();
1418
+ TestM.registerFlow({ key: 'auto1', steps: { s: { handler } } });
1419
+
1420
+ const m = new TestM(modelOptions);
1421
+
1422
+ // First run with {a:1}
1423
+ await m.dispatchEvent('beforeRender', { a: 1 });
1424
+ expect(handler).toHaveBeenCalledTimes(1);
1425
+
1426
+ // Second run with same args should hit cache -> no extra handler calls
1427
+ await m.dispatchEvent('beforeRender', { a: 1 });
1428
+ expect(handler).toHaveBeenCalledTimes(1);
1429
+
1430
+ // Different args invalidate cache -> handler called again
1431
+ await m.dispatchEvent('beforeRender', { a: 2 });
1432
+ expect(handler).toHaveBeenCalledTimes(2);
1303
1433
  });
1304
1434
  });
1305
1435
 
@@ -1415,8 +1545,6 @@ describe('FlowModel', () => {
1415
1545
  const fork1 = model.createFork();
1416
1546
  const fork2 = model.createFork();
1417
1547
 
1418
- expect(fork1.forkId).toBe(0);
1419
- expect(fork2.forkId).toBe(1);
1420
1548
  expect(model.forks.size).toBe(2);
1421
1549
  expect(model.forks.has(fork1)).toBe(true);
1422
1550
  expect(model.forks.has(fork2)).toBe(true);
@@ -1430,13 +1558,27 @@ describe('FlowModel', () => {
1430
1558
  expect(model.forks.size).toBe(1);
1431
1559
  });
1432
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
+
1433
1577
  test('should create different instances for different keys', () => {
1434
1578
  const fork1 = model.createFork({}, 'key1');
1435
1579
  const fork2 = model.createFork({}, 'key2');
1436
1580
 
1437
1581
  expect(fork1).not.toBe(fork2);
1438
- expect(fork1.forkId).toBe(0);
1439
- expect(fork2.forkId).toBe(1);
1440
1582
  });
1441
1583
 
1442
1584
  test('should create fork with local props', () => {
@@ -1627,7 +1769,7 @@ describe('FlowModel', () => {
1627
1769
  });
1628
1770
 
1629
1771
  describe('rendering operations', () => {
1630
- test('should not pre-call render for RenderFunction mode and call exactly once on render', () => {
1772
+ test('should not pre-call render for RenderFunction mode and call exactly once on render', async () => {
1631
1773
  const renderSpy = vi.fn(() => vi.fn());
1632
1774
 
1633
1775
  class CallbackRenderModel extends FlowModel {
@@ -1645,17 +1787,20 @@ describe('FlowModel', () => {
1645
1787
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
1646
1788
  try {
1647
1789
  const { unmount } = render(
1648
- React.createElement(FlowModelRenderer, { model: callbackModel, skipApplyAutoFlows: true }),
1790
+ React.createElement(
1791
+ FlowEngineProvider,
1792
+ { engine: callbackModel.flowEngine },
1793
+ React.createElement(FlowModelRenderer, { model: callbackModel }),
1794
+ ),
1649
1795
  );
1650
-
1651
- expect(renderSpy).toHaveBeenCalledTimes(1);
1796
+ await waitFor(() => expect(renderSpy).toHaveBeenCalledTimes(1));
1652
1797
  unmount();
1653
1798
  } finally {
1654
1799
  warnSpy.mockRestore();
1655
1800
  }
1656
1801
  });
1657
1802
 
1658
- test('should not pre-call render for ReactElement mode and call exactly once on actual render', () => {
1803
+ test('should not pre-call render for ReactElement mode and call exactly once on actual render', async () => {
1659
1804
  const renderSpy = vi.fn(() => React.createElement('div', { 'data-testid': 'elt' }, 'Elt'));
1660
1805
 
1661
1806
  class ElementRenderModel extends FlowModel {
@@ -1670,11 +1815,15 @@ describe('FlowModel', () => {
1670
1815
  expect(renderSpy).toHaveBeenCalledTimes(0);
1671
1816
 
1672
1817
  const { getByTestId, unmount } = render(
1673
- React.createElement(FlowModelRenderer, { model: elementModel, skipApplyAutoFlows: true }),
1818
+ React.createElement(
1819
+ FlowEngineProvider,
1820
+ { engine: elementModel.flowEngine },
1821
+ React.createElement(FlowModelRenderer, { model: elementModel }),
1822
+ ),
1674
1823
  );
1675
1824
 
1676
1825
  // Render should be called exactly once during mount
1677
- expect(renderSpy).toHaveBeenCalledTimes(1);
1826
+ await waitFor(() => expect(renderSpy).toHaveBeenCalledTimes(1));
1678
1827
  expect(getByTestId('elt')).toBeTruthy();
1679
1828
 
1680
1829
  unmount();
@@ -1690,13 +1839,15 @@ describe('FlowModel', () => {
1690
1839
  expect(result.type.displayName).toBe('ReactiveWrapper(FlowModel)');
1691
1840
  });
1692
1841
 
1693
- test('should rerender with previous auto flows', async () => {
1842
+ test('should rerender triggers beforeRender without cache', async () => {
1694
1843
  const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
1695
- model.applyAutoFlows = vi.fn().mockResolvedValue([]);
1844
+ model.dispatchEvent = vi.fn().mockResolvedValue(undefined) as any;
1696
1845
 
1697
1846
  try {
1698
1847
  await expect(model.rerender()).resolves.not.toThrow();
1699
- expect(model.applyAutoFlows).toHaveBeenCalled();
1848
+ expect(model.dispatchEvent).toHaveBeenCalledWith('beforeRender', undefined, {
1849
+ useCache: false,
1850
+ });
1700
1851
  } finally {
1701
1852
  consoleSpy.mockRestore();
1702
1853
  }
@@ -1713,12 +1864,14 @@ describe('FlowModel', () => {
1713
1864
 
1714
1865
  const serialized = model.serialize();
1715
1866
 
1716
- expect(serialized).toEqual({
1717
- uid: model.uid,
1718
- stepParams: expect.objectContaining({ flow1: { step1: { param1: 'value1' } } }),
1719
- sortIndex: 5,
1720
- subModels: expect.any(Object),
1721
- });
1867
+ expect(serialized).toEqual(
1868
+ expect.objectContaining({
1869
+ uid: model.uid,
1870
+ stepParams: expect.objectContaining({ flow1: { step1: { param1: 'value1' } } }),
1871
+ sortIndex: 5,
1872
+ subModels: expect.any(Object),
1873
+ }),
1874
+ );
1722
1875
  // props should be excluded from serialization
1723
1876
  expect(serialized.props).toBeUndefined();
1724
1877
  expect(serialized.flowEngine).toBeUndefined();
@@ -1736,12 +1889,14 @@ describe('FlowModel', () => {
1736
1889
 
1737
1890
  const serialized = emptyModel.serialize();
1738
1891
 
1739
- expect(serialized).toEqual({
1740
- uid: 'empty-model',
1741
- stepParams: expect.any(Object),
1742
- sortIndex: expect.any(Number),
1743
- subModels: expect.any(Object),
1744
- });
1892
+ expect(serialized).toEqual(
1893
+ expect.objectContaining({
1894
+ uid: 'empty-model',
1895
+ stepParams: expect.any(Object),
1896
+ sortIndex: expect.any(Number),
1897
+ subModels: expect.any(Object),
1898
+ }),
1899
+ );
1745
1900
  expect(serialized.flowEngine).toBeUndefined();
1746
1901
  });
1747
1902
  });
@@ -1861,6 +2016,22 @@ describe('FlowModel', () => {
1861
2016
  expect(model.title).toBe('Custom Title');
1862
2017
  });
1863
2018
 
2019
+ test('should be reactive when title changes', () => {
2020
+ const seen: string[] = [];
2021
+ const dispose = reaction(
2022
+ () => model.title,
2023
+ (next) => {
2024
+ if (typeof next === 'string') seen.push(next);
2025
+ },
2026
+ );
2027
+
2028
+ model.setTitle('First Title');
2029
+ model.setTitle('Second Title');
2030
+
2031
+ dispose();
2032
+ expect(seen).toEqual(['First Title', 'Second Title']);
2033
+ });
2034
+
1864
2035
  test('should update title when called multiple times', () => {
1865
2036
  model.setTitle('First Title');
1866
2037
  expect(model.title).toBe('First Title');
@@ -2039,16 +2210,16 @@ describe('FlowModel', () => {
2039
2210
  vi.restoreAllMocks();
2040
2211
  });
2041
2212
 
2042
- describe('invalidateAutoFlowCache', () => {
2213
+ describe('invalidateFlowCache', () => {
2043
2214
  test('should delete auto flow cache for current model', () => {
2044
- const expectedCacheKey = 'autoFlow-all-test-model-uid';
2215
+ const expectedCacheKey = 'event:beforeRender-beforeRender-test-model-uid';
2045
2216
  realFlowEngine.applyFlowCache.set(expectedCacheKey, {
2046
2217
  status: 'resolved',
2047
2218
  data: [],
2048
2219
  promise: Promise.resolve([]),
2049
2220
  });
2050
2221
 
2051
- model.invalidateAutoFlowCache();
2222
+ model.invalidateFlowCache('beforeRender');
2052
2223
 
2053
2224
  expect(deleteSpy).toHaveBeenCalledWith(expectedCacheKey);
2054
2225
  });
@@ -2057,8 +2228,8 @@ describe('FlowModel', () => {
2057
2228
  const fork1 = model.createFork();
2058
2229
  const fork2 = model.createFork();
2059
2230
 
2060
- const fork1CacheKey = `${fork1.forkId}-all-test-model-uid`;
2061
- const fork2CacheKey = `${fork2.forkId}-all-test-model-uid`;
2231
+ const fork1CacheKey = `event:beforeRender:${fork1.forkId}-beforeRender-test-model-uid`;
2232
+ const fork2CacheKey = `event:beforeRender:${fork2.forkId}-beforeRender-test-model-uid`;
2062
2233
 
2063
2234
  realFlowEngine.applyFlowCache.set(fork1CacheKey, {
2064
2235
  status: 'resolved',
@@ -2071,7 +2242,7 @@ describe('FlowModel', () => {
2071
2242
  promise: Promise.resolve([]),
2072
2243
  });
2073
2244
 
2074
- model.invalidateAutoFlowCache();
2245
+ model.invalidateFlowCache('beforeRender');
2075
2246
 
2076
2247
  expect(deleteSpy).toHaveBeenCalledWith(fork1CacheKey);
2077
2248
  expect(deleteSpy).toHaveBeenCalledWith(fork2CacheKey);
@@ -2081,27 +2252,27 @@ describe('FlowModel', () => {
2081
2252
  const childModel1 = new FlowModel({ uid: 'child1', flowEngine: realFlowEngine });
2082
2253
  const childModel2 = new FlowModel({ uid: 'child2', flowEngine: realFlowEngine });
2083
2254
 
2084
- const child1Spy = vi.spyOn(childModel1, 'invalidateAutoFlowCache');
2085
- const child2Spy = vi.spyOn(childModel2, 'invalidateAutoFlowCache');
2255
+ const child1Spy = vi.spyOn(childModel1, 'invalidateFlowCache');
2256
+ const child2Spy = vi.spyOn(childModel2, 'invalidateFlowCache');
2086
2257
 
2087
2258
  model.addSubModel('children', childModel1);
2088
2259
  model.addSubModel('children', childModel2);
2089
2260
 
2090
- model.invalidateAutoFlowCache(true);
2261
+ model.invalidateFlowCache('beforeRender', true);
2091
2262
 
2092
- expect(child1Spy).toHaveBeenCalledWith(true);
2093
- expect(child2Spy).toHaveBeenCalledWith(true);
2263
+ expect(child1Spy).toHaveBeenCalledWith('beforeRender', true);
2264
+ expect(child2Spy).toHaveBeenCalledWith('beforeRender', true);
2094
2265
  });
2095
2266
 
2096
2267
  test('should recursively invalidate cache for object subModels', () => {
2097
2268
  const childModel = new FlowModel({ uid: 'child', flowEngine: realFlowEngine });
2098
- const childSpy = vi.spyOn(childModel, 'invalidateAutoFlowCache');
2269
+ const childSpy = vi.spyOn(childModel, 'invalidateFlowCache');
2099
2270
 
2100
2271
  model.setSubModel('child', childModel);
2101
2272
 
2102
- model.invalidateAutoFlowCache(true);
2273
+ model.invalidateFlowCache('beforeRender', true);
2103
2274
 
2104
- expect(childSpy).toHaveBeenCalledWith(true);
2275
+ expect(childSpy).toHaveBeenCalledWith('beforeRender', true);
2105
2276
  });
2106
2277
 
2107
2278
  test('should handle mixed array and object subModels', () => {
@@ -2109,25 +2280,31 @@ describe('FlowModel', () => {
2109
2280
  const arrayChild2 = new FlowModel({ uid: 'arrayChild2', flowEngine: realFlowEngine });
2110
2281
  const objectChild = new FlowModel({ uid: 'objectChild', flowEngine: realFlowEngine });
2111
2282
 
2112
- const array1Spy = vi.spyOn(arrayChild1, 'invalidateAutoFlowCache');
2113
- const array2Spy = vi.spyOn(arrayChild2, 'invalidateAutoFlowCache');
2114
- const objectSpy = vi.spyOn(objectChild, 'invalidateAutoFlowCache');
2283
+ const array1Spy = vi.spyOn(arrayChild1, 'invalidateFlowCache');
2284
+ const array2Spy = vi.spyOn(arrayChild2, 'invalidateFlowCache');
2285
+ const objectSpy = vi.spyOn(objectChild, 'invalidateFlowCache');
2115
2286
 
2116
2287
  model.addSubModel('arrayChildren', arrayChild1);
2117
2288
  model.addSubModel('arrayChildren', arrayChild2);
2118
2289
  model.setSubModel('objectChild', objectChild);
2119
2290
 
2120
- model.invalidateAutoFlowCache(true);
2291
+ model.invalidateFlowCache('beforeRender', true);
2121
2292
 
2122
- expect(array1Spy).toHaveBeenCalledWith(true);
2123
- expect(array2Spy).toHaveBeenCalledWith(true);
2124
- expect(objectSpy).toHaveBeenCalledWith(true);
2293
+ expect(array1Spy).toHaveBeenCalledWith('beforeRender', true);
2294
+ expect(array2Spy).toHaveBeenCalledWith('beforeRender', true);
2295
+ expect(objectSpy).toHaveBeenCalledWith('beforeRender', true);
2125
2296
  });
2126
2297
 
2127
2298
  test('should handle empty subModels without error', () => {
2128
- model.invalidateAutoFlowCache();
2299
+ realFlowEngine.applyFlowCache.set('event:beforeRender-beforeRender-test-model-uid', {
2300
+ status: 'resolved',
2301
+ data: [],
2302
+ promise: Promise.resolve([]),
2303
+ });
2304
+
2305
+ model.invalidateFlowCache('beforeRender');
2129
2306
 
2130
- expect(deleteSpy).toHaveBeenCalledWith('autoFlow-all-test-model-uid');
2307
+ expect(deleteSpy).toHaveBeenCalledWith('event:beforeRender-beforeRender-test-model-uid');
2131
2308
  });
2132
2309
 
2133
2310
  test('should handle null flowEngine gracefully', () => {
@@ -2135,19 +2312,19 @@ describe('FlowModel', () => {
2135
2312
  modelWithValidEngine.flowEngine = null;
2136
2313
 
2137
2314
  expect(() => {
2138
- modelWithValidEngine.invalidateAutoFlowCache();
2315
+ modelWithValidEngine.invalidateFlowCache('beforeRender');
2139
2316
  }).not.toThrow();
2140
2317
  });
2141
2318
 
2142
2319
  test('should pass deep parameter to recursive calls', () => {
2143
2320
  const childModel = new FlowModel({ uid: 'child', flowEngine: realFlowEngine });
2144
- const childSpy = vi.spyOn(childModel, 'invalidateAutoFlowCache');
2321
+ const childSpy = vi.spyOn(childModel, 'invalidateFlowCache');
2145
2322
 
2146
2323
  model.setSubModel('child', childModel);
2147
2324
 
2148
- model.invalidateAutoFlowCache(true);
2325
+ model.invalidateFlowCache('beforeRender', true);
2149
2326
 
2150
- expect(childSpy).toHaveBeenCalledWith(true);
2327
+ expect(childSpy).toHaveBeenCalledWith('beforeRender', true);
2151
2328
  });
2152
2329
  });
2153
2330
  });
@@ -2198,7 +2375,7 @@ describe('FlowModel', () => {
2198
2375
  });
2199
2376
 
2200
2377
  const handlerSpy = flow.steps.testStep.handler as any;
2201
- await model.applyAutoFlows();
2378
+ await model.dispatchEvent('beforeRender');
2202
2379
  expect(handlerSpy).toHaveBeenCalled();
2203
2380
  });
2204
2381
 
@@ -2245,7 +2422,7 @@ describe('FlowModel', () => {
2245
2422
  });
2246
2423
 
2247
2424
  const handlerSpy = flow.steps.nestedStep.handler as any;
2248
- await model.applyAutoFlows();
2425
+ await model.dispatchEvent('beforeRender');
2249
2426
  expect(handlerSpy).toHaveBeenCalled();
2250
2427
  });
2251
2428
  });