@nocobase/flow-engine 2.0.0-alpha.4 → 2.0.0-alpha.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/lib/BlockScopedFlowEngine.d.ts +23 -0
  2. package/lib/BlockScopedFlowEngine.js +90 -0
  3. package/lib/FlowContextProvider.d.ts +2 -2
  4. package/lib/FlowContextProvider.js +3 -3
  5. package/lib/FlowDefinition.d.ts +4 -2
  6. package/lib/JSRunner.js +3 -0
  7. package/lib/ViewScopedFlowEngine.d.ts +1 -1
  8. package/lib/ViewScopedFlowEngine.js +12 -0
  9. package/lib/acl/Acl.d.ts +1 -0
  10. package/lib/acl/Acl.js +11 -0
  11. package/lib/components/FieldModelRenderer.js +14 -6
  12. package/lib/components/FieldSkeleton.d.ts +10 -0
  13. package/lib/components/FieldSkeleton.js +64 -0
  14. package/lib/components/FlowContextSelector.js +7 -2
  15. package/lib/components/FlowModelRenderer.d.ts +3 -6
  16. package/lib/components/FlowModelRenderer.js +16 -47
  17. package/lib/components/FormItem.js +5 -1
  18. package/lib/components/MobilePopup.d.ts +20 -0
  19. package/lib/components/MobilePopup.js +102 -0
  20. package/lib/components/MobilePopup.style.d.ts +17 -0
  21. package/lib/components/MobilePopup.style.js +186 -0
  22. package/lib/components/index.d.ts +1 -0
  23. package/lib/components/index.js +3 -1
  24. package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +2 -2
  25. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +17 -5
  26. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +2 -2
  27. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +2 -2
  28. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +35 -6
  29. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
  30. package/lib/components/variables/VariableInput.js +19 -5
  31. package/lib/components/variables/VariableTag.js +43 -2
  32. package/lib/components/variables/types.d.ts +2 -0
  33. package/lib/data-source/index.d.ts +15 -4
  34. package/lib/data-source/index.js +42 -13
  35. package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
  36. package/lib/data-source/sortCollectionsByInherits.js +71 -0
  37. package/lib/emitter.d.ts +6 -0
  38. package/lib/emitter.js +12 -0
  39. package/lib/executor/FlowExecutor.d.ts +4 -5
  40. package/lib/executor/FlowExecutor.js +168 -99
  41. package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
  42. package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
  43. package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
  44. package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
  45. package/lib/flowContext.d.ts +37 -5
  46. package/lib/flowContext.js +224 -87
  47. package/lib/flowEngine.d.ts +24 -1
  48. package/lib/flowEngine.js +81 -8
  49. package/lib/flowSettings.d.ts +2 -1
  50. package/lib/flowSettings.js +16 -8
  51. package/lib/hooks/useApplyAutoFlows.js +2 -1
  52. package/lib/index.d.ts +7 -1
  53. package/lib/index.js +32 -3
  54. package/lib/locale/en-US.json +4 -2
  55. package/lib/locale/index.d.ts +4 -0
  56. package/lib/locale/zh-CN.json +4 -2
  57. package/lib/models/CollectionFieldModel.d.ts +3 -0
  58. package/lib/models/CollectionFieldModel.js +52 -6
  59. package/lib/models/flowModel.d.ts +30 -29
  60. package/lib/models/flowModel.js +153 -100
  61. package/lib/models/forkFlowModel.d.ts +8 -4
  62. package/lib/models/forkFlowModel.js +38 -8
  63. package/lib/provider.d.ts +3 -1
  64. package/lib/provider.js +7 -5
  65. package/lib/resources/multiRecordResource.js +25 -1
  66. package/lib/resources/singleRecordResource.js +19 -1
  67. package/lib/resources/sqlResource.d.ts +1 -0
  68. package/lib/resources/sqlResource.js +20 -24
  69. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
  70. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +27 -20
  71. package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
  72. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
  73. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
  74. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
  75. package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
  76. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
  77. package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
  78. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
  79. package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
  80. package/lib/runjs-context/contexts/JSItemRunJSContext.js +24 -20
  81. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
  82. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
  83. package/lib/runjs-context/contexts/base.d.ts +9 -0
  84. package/lib/runjs-context/contexts/base.js +205 -0
  85. package/lib/runjs-context/helpers.d.ts +5 -2
  86. package/lib/runjs-context/helpers.js +36 -27
  87. package/lib/runjs-context/registry.d.ts +7 -4
  88. package/lib/runjs-context/registry.js +10 -42
  89. package/lib/runjs-context/setup.d.ts +9 -0
  90. package/lib/runjs-context/setup.js +82 -0
  91. package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
  92. package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
  93. package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
  94. package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
  95. package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
  96. package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
  97. package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
  98. package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
  99. package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
  100. package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
  101. package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
  102. package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
  103. package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
  104. package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
  105. package/lib/runjs-context/snippets/index.d.ts +14 -3
  106. package/lib/runjs-context/snippets/index.js +162 -40
  107. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
  108. package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
  109. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
  110. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
  111. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
  112. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
  113. package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
  114. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.d.ts +11 -0
  115. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.js +65 -0
  116. package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
  117. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
  118. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
  119. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
  120. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
  121. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
  122. package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
  123. package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
  124. package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
  125. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
  126. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
  127. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
  128. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
  129. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
  130. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
  131. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
  132. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
  133. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
  134. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
  135. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
  136. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
  137. package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
  138. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
  139. package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
  140. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
  141. package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
  142. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
  143. package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
  144. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
  145. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
  146. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
  147. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
  148. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
  149. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
  150. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
  151. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
  152. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
  153. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
  154. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
  155. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
  156. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
  157. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
  158. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
  159. package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
  160. package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
  161. package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
  162. package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
  163. package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
  164. package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
  165. package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
  166. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
  167. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
  168. package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
  169. package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
  170. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
  171. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
  172. package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
  173. package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
  174. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
  175. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
  176. package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
  177. package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
  178. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
  179. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
  180. package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
  181. package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
  182. package/lib/runjs-context/snippets/types.d.ts +9 -1
  183. package/lib/scheduler/ModelOperationScheduler.d.ts +51 -0
  184. package/lib/scheduler/ModelOperationScheduler.js +262 -0
  185. package/lib/types.d.ts +28 -3
  186. package/lib/types.js +4 -3
  187. package/lib/utils/associationObjectVariable.d.ts +32 -0
  188. package/lib/utils/associationObjectVariable.js +157 -0
  189. package/lib/utils/buildSettingsViewInputArgs.d.ts +19 -0
  190. package/lib/utils/buildSettingsViewInputArgs.js +75 -0
  191. package/lib/utils/createEphemeralContext.d.ts +13 -0
  192. package/lib/utils/createEphemeralContext.js +140 -0
  193. package/lib/utils/flows.d.ts +10 -0
  194. package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → utils/flows.js} +21 -15
  195. package/lib/utils/index.d.ts +7 -2
  196. package/lib/utils/index.js +21 -2
  197. package/lib/utils/jsxTransform.d.ts +15 -0
  198. package/lib/utils/jsxTransform.js +68 -0
  199. package/lib/utils/params-resolvers.js +3 -3
  200. package/lib/utils/parsePathnameToViewParams.d.ts +1 -1
  201. package/lib/utils/parsePathnameToViewParams.js +41 -5
  202. package/lib/utils/pruneFilter.d.ts +21 -0
  203. package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
  204. package/lib/utils/safeGlobals.d.ts +5 -3
  205. package/lib/utils/safeGlobals.js +40 -0
  206. package/lib/utils/schema-utils.js +2 -2
  207. package/lib/utils/serverContextParams.d.ts +1 -0
  208. package/lib/utils/translation.d.ts +4 -1
  209. package/lib/utils/translation.js +6 -2
  210. package/lib/utils/variablesParams.d.ts +22 -5
  211. package/lib/utils/variablesParams.js +132 -36
  212. package/lib/views/DrawerComponent.js +21 -2
  213. package/lib/views/PageComponent.js +2 -1
  214. package/lib/views/ViewNavigation.d.ts +3 -9
  215. package/lib/views/ViewNavigation.js +16 -2
  216. package/lib/views/createViewMeta.d.ts +29 -1
  217. package/lib/views/createViewMeta.js +338 -72
  218. package/lib/views/index.d.ts +1 -0
  219. package/lib/views/index.js +3 -0
  220. package/lib/views/useDialog.d.ts +9 -8
  221. package/lib/views/useDialog.js +19 -14
  222. package/lib/views/useDrawer.d.ts +9 -8
  223. package/lib/views/useDrawer.js +54 -35
  224. package/lib/views/usePage.d.ts +9 -8
  225. package/lib/views/usePage.js +20 -14
  226. package/package.json +6 -3
  227. package/src/BlockScopedFlowEngine.ts +86 -0
  228. package/src/FlowContextProvider.tsx +4 -2
  229. package/src/JSRunner.ts +3 -0
  230. package/src/ViewScopedFlowEngine.ts +15 -1
  231. package/src/__tests__/JSRunner.test.ts +62 -53
  232. package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
  233. package/src/__tests__/createViewMeta.popup.test.ts +132 -0
  234. package/src/__tests__/flow-engine.test.ts +3 -0
  235. package/src/__tests__/flowContext.test.ts +52 -0
  236. package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
  237. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  238. package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
  239. package/src/__tests__/flowSettings.open.test.tsx +2 -0
  240. package/src/__tests__/flowSettings.test.ts +2 -0
  241. package/src/__tests__/globalFlowRegistry.test.ts +1 -1
  242. package/src/__tests__/modelOperationScheduler.test.ts +346 -0
  243. package/src/__tests__/objectVariable.test.ts +464 -0
  244. package/src/__tests__/runjsContext.test.ts +216 -35
  245. package/src/__tests__/runjsContextImplementations.test.ts +217 -0
  246. package/src/__tests__/runjsContextRuntime.test.ts +269 -0
  247. package/src/__tests__/runjsEdgeCases.test.ts +281 -0
  248. package/src/__tests__/runjsLocales.test.ts +36 -0
  249. package/src/__tests__/runjsRuntimeFeatures.test.ts +461 -0
  250. package/src/__tests__/runjsSnippets.test.ts +140 -0
  251. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  252. package/src/acl/Acl.tsx +14 -0
  253. package/src/acl/__tests__/Acl.test.tsx +43 -0
  254. package/src/components/DynamicFlowsEditor.tsx +3 -4
  255. package/src/components/FieldModelRenderer.tsx +20 -7
  256. package/src/components/FieldSkeleton.tsx +27 -0
  257. package/src/components/FlowContextSelector.tsx +6 -2
  258. package/src/components/FlowModelRenderer.tsx +33 -81
  259. package/src/components/FormItem.tsx +8 -1
  260. package/src/components/MobilePopup.style.ts +220 -0
  261. package/src/components/MobilePopup.tsx +86 -0
  262. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
  263. package/src/components/index.ts +1 -0
  264. package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +1 -1
  265. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +17 -4
  266. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +1 -1
  267. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +39 -10
  268. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
  269. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +425 -0
  270. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
  271. package/src/components/variables/VariableInput.tsx +25 -5
  272. package/src/components/variables/VariableTag.tsx +54 -2
  273. package/src/components/variables/types.ts +2 -0
  274. package/src/data-source/__tests__/sortCollectionsByInherits.test.ts +125 -0
  275. package/src/data-source/index.ts +42 -11
  276. package/src/data-source/sortCollectionsByInherits.ts +61 -0
  277. package/src/emitter.ts +14 -0
  278. package/src/executor/FlowExecutor.ts +213 -119
  279. package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
  280. package/src/executor/__tests__/flowExecutor.test.ts +151 -5
  281. package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
  282. package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
  283. package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
  284. package/src/flowContext.ts +303 -104
  285. package/src/flowEngine.ts +96 -6
  286. package/src/flowSettings.ts +12 -4
  287. package/src/hooks/useApplyAutoFlows.ts +3 -1
  288. package/src/index.ts +12 -1
  289. package/src/locale/en-US.json +4 -2
  290. package/src/locale/zh-CN.json +4 -2
  291. package/src/models/CollectionFieldModel.tsx +60 -8
  292. package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
  293. package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
  294. package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
  295. package/src/models/__tests__/flowModel.test.ts +234 -111
  296. package/src/models/__tests__/forkFlowModel.test.ts +40 -7
  297. package/src/models/flowModel.tsx +197 -132
  298. package/src/models/forkFlowModel.ts +48 -8
  299. package/src/provider.tsx +10 -7
  300. package/src/resources/multiRecordResource.ts +28 -1
  301. package/src/resources/singleRecordResource.ts +19 -1
  302. package/src/resources/sqlResource.ts +20 -25
  303. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +28 -21
  304. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +46 -34
  305. package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
  306. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
  307. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
  308. package/src/runjs-context/contexts/JSItemRunJSContext.ts +25 -21
  309. package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
  310. package/src/runjs-context/contexts/base.ts +196 -0
  311. package/src/runjs-context/helpers.ts +32 -30
  312. package/src/runjs-context/registry.ts +16 -47
  313. package/src/runjs-context/setup.ts +51 -0
  314. package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
  315. package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
  316. package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
  317. package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
  318. package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
  319. package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
  320. package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
  321. package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
  322. package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
  323. package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
  324. package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
  325. package/src/runjs-context/snippets/index.ts +178 -39
  326. package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
  327. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
  328. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
  329. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
  330. package/src/runjs-context/snippets/scene/block/render-antd-icons.snippet.ts +46 -0
  331. package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
  332. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
  333. package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
  334. package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
  335. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
  336. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
  337. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
  338. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
  339. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
  340. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
  341. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
  342. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
  343. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
  344. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
  345. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
  346. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
  347. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
  348. package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
  349. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
  350. package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
  351. package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
  352. package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
  353. package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
  354. package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
  355. package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
  356. package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
  357. package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
  358. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
  359. package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
  360. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
  361. package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
  362. package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
  363. package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
  364. package/src/runjs-context/snippets/types.ts +5 -1
  365. package/src/scheduler/ModelOperationScheduler.ts +304 -0
  366. package/src/types.ts +34 -0
  367. package/src/utils/__tests__/flows.test.ts +65 -0
  368. package/src/utils/__tests__/jsxTransform.test.ts +38 -0
  369. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
  370. package/src/utils/__tests__/pruneFilter.test.ts +38 -0
  371. package/src/utils/__tests__/safeGlobals.test.ts +22 -1
  372. package/src/utils/__tests__/variablesParams.test.ts +63 -0
  373. package/src/utils/associationObjectVariable.ts +180 -0
  374. package/src/utils/buildSettingsViewInputArgs.ts +72 -0
  375. package/src/utils/createEphemeralContext.ts +142 -0
  376. package/src/utils/flows.ts +23 -0
  377. package/src/utils/index.ts +11 -2
  378. package/src/utils/jsxTransform.ts +39 -0
  379. package/src/utils/params-resolvers.ts +2 -2
  380. package/src/utils/parsePathnameToViewParams.ts +50 -6
  381. package/src/utils/pruneFilter.ts +41 -0
  382. package/src/utils/safeGlobals.ts +49 -3
  383. package/src/utils/schema-utils.ts +1 -1
  384. package/src/utils/serverContextParams.ts +1 -0
  385. package/src/utils/translation.ts +7 -2
  386. package/src/utils/variablesParams.ts +156 -38
  387. package/src/views/DrawerComponent.tsx +21 -2
  388. package/src/views/PageComponent.tsx +1 -1
  389. package/src/views/ViewNavigation.ts +20 -12
  390. package/src/views/__tests__/ViewNavigation.test.ts +10 -0
  391. package/src/views/createViewMeta.ts +393 -70
  392. package/src/views/index.tsx +1 -0
  393. package/src/views/useDialog.tsx +24 -18
  394. package/src/views/useDrawer.tsx +74 -45
  395. package/src/views/usePage.tsx +26 -17
  396. package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
  397. package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
  398. package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
  399. package/lib/runjs-context/index.d.ts +0 -19
  400. package/lib/runjs-context/index.js +0 -57
  401. package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
  402. package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
  403. package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
  404. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
  405. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.js +0 -41
  406. package/lib/runjs-context/snippets/global/requireAsync.snippet.d.ts +0 -16
  407. package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
  408. package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
  409. package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
  410. package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
  411. package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
  412. package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
  413. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
  414. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
  415. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
  416. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
  417. package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
  418. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
  419. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
  420. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
  421. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js +0 -46
  422. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
  423. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
  424. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
  425. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
  426. package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
  427. package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
  428. package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
  429. package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
  430. package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
  431. package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
  432. package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
  433. package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
  434. package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
  435. package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
  436. package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
  437. package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
  438. package/src/runjs-context/index.ts +0 -20
  439. package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
  440. package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
  441. package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
  442. package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
  443. package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
  444. package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
  445. package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
  446. package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
  447. package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
  448. package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
  449. package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
  450. package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
  451. package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
  452. package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
  453. package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
  454. package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
  455. package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
  456. package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
  457. package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
  458. package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
  459. package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
  460. package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
  461. package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
  462. /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
  463. /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
  464. /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
  465. /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
  466. /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
@@ -11,14 +11,49 @@ import _ from 'lodash';
11
11
  import { FlowRuntimeContext } from '../flowContext';
12
12
  import { FlowEngine } from '../flowEngine';
13
13
  import type { FlowModel } from '../models';
14
+ import type { DispatchEventOptions } from '../types';
14
15
  import type { ActionDefinition, ApplyFlowCacheEntry, StepDefinition } from '../types';
15
16
  import { FlowExitException, resolveDefaultParams } from '../utils';
16
17
  import { FlowExitAllException } from '../utils/exceptions';
17
18
  import { setupRuntimeContextSteps } from '../utils/setupRuntimeContextSteps';
19
+ import { createEphemeralContext } from '../utils/createEphemeralContext';
18
20
 
19
21
  export class FlowExecutor {
20
22
  constructor(private readonly engine: FlowEngine) {}
21
23
 
24
+ /** Cache wrapper for applyFlow cache lifecycle */
25
+ private async withApplyFlowCache<T>(cacheKey: string | null, executor: () => Promise<T>): Promise<T> {
26
+ if (!cacheKey || !this.engine) return await executor();
27
+
28
+ const cachedEntry = this.engine.applyFlowCache.get(cacheKey);
29
+ if (cachedEntry) {
30
+ if (cachedEntry.status === 'resolved') return cachedEntry.data as T;
31
+ if (cachedEntry.status === 'rejected') throw cachedEntry.error;
32
+ if (cachedEntry.status === 'pending') return await cachedEntry.promise;
33
+ }
34
+
35
+ const promise = executor()
36
+ .then((result) => {
37
+ this.engine.applyFlowCache.set(cacheKey, {
38
+ status: 'resolved',
39
+ data: result,
40
+ promise: Promise.resolve(result),
41
+ } as ApplyFlowCacheEntry);
42
+ return result;
43
+ })
44
+ .catch((err) => {
45
+ this.engine.applyFlowCache.set(cacheKey, {
46
+ status: 'rejected',
47
+ error: err,
48
+ promise: Promise.reject(err),
49
+ } as ApplyFlowCacheEntry);
50
+ throw err;
51
+ });
52
+
53
+ this.engine.applyFlowCache.set(cacheKey, { status: 'pending', promise } as ApplyFlowCacheEntry);
54
+ return await promise;
55
+ }
56
+
22
57
  /**
23
58
  * Execute a single flow on model.
24
59
  */
@@ -46,43 +81,55 @@ export class FlowExecutor {
46
81
 
47
82
  let lastResult: any;
48
83
  const stepResults: Record<string, any> = flowContext.stepResults;
84
+
85
+ // Build steps with optional eventStep injection (when flow.on is configured)
49
86
  let eventStep = model.getEvent(typeof flow.on === 'string' ? flow.on : (flow.on as any)?.eventName);
50
87
  if (eventStep) {
51
- eventStep = { ...eventStep }; // clone to avoid side effects
52
- eventStep.defaultParams = { ..._.get(flow, 'on.defaultParams', {}), ...eventStep.defaultParams };
88
+ eventStep = { ...eventStep } as any; // clone to avoid side effects
89
+ (eventStep as any).defaultParams = {
90
+ ..._.get(flow, 'on.defaultParams', {}),
91
+ ...(eventStep as any).defaultParams,
92
+ };
53
93
  }
54
- // Execute the event step first since it's usually the trigger condition - if the condition is not met, subsequent steps don't need to execute
55
- const stepDefs = eventStep ? { eventStep, ...flow.steps } : flow.steps; // Record<string, StepDefinition>
94
+ const stepDefs: Record<string, StepDefinition> = eventStep
95
+ ? { eventStep: eventStep as any, ...flow.steps }
96
+ : flow.steps;
56
97
 
57
98
  // Setup steps meta and runtime mapping
58
99
  setupRuntimeContextSteps(flowContext, stepDefs, model, flowKey);
59
100
  const stepsRuntime = flowContext.steps as Record<string, { params: any; uiSchema?: any; result?: any }>;
60
101
 
61
- for (const stepKey in stepDefs) {
62
- if (!Object.prototype.hasOwnProperty.call(stepDefs, stepKey)) continue;
63
- const step: StepDefinition = stepDefs[stepKey];
64
- let handler: ActionDefinition['handler'] | undefined;
102
+ for (const [stepKey, step] of Object.entries(stepDefs) as [string, StepDefinition][]) {
103
+ // Resolve handler and params
104
+ let handler: ActionDefinition<FlowModel, FlowRuntimeContext>['handler'] | undefined;
65
105
  let combinedParams: Record<string, any> = {};
66
- let actionDefinition: ActionDefinition | undefined;
67
106
  let useRawParams: StepDefinition['useRawParams'] = step.useRawParams;
68
-
107
+ let runtimeCtx: FlowRuntimeContext;
69
108
  if (step.use) {
70
- // Step references a registered action
71
- actionDefinition = model.getAction(step.use);
109
+ const actionDefinition = model.getAction(step.use);
72
110
  if (!actionDefinition) {
73
111
  flowContext.logger.error(
74
112
  `BaseModel.applyFlow: Action '${step.use}' not found for step '${stepKey}' in flow '${flowKey}'. Skipping.`,
75
113
  );
76
114
  continue;
77
115
  }
116
+
117
+ // 为当前 step 创建“临时上下文”,并直接注入 action 与 step 提供的定义(step 覆盖 action)
118
+ runtimeCtx = await createEphemeralContext<FlowRuntimeContext>(flowContext, {
119
+ ...actionDefinition,
120
+ ...step,
121
+ });
122
+
78
123
  handler = step.handler || actionDefinition.handler;
79
124
  useRawParams = useRawParams ?? actionDefinition.useRawParams;
80
- const actionDefaultParams = await resolveDefaultParams(actionDefinition.defaultParams, flowContext);
81
- const stepDefaultParams = await resolveDefaultParams(step.defaultParams, flowContext);
125
+ const actionDefaultParams = await resolveDefaultParams(actionDefinition.defaultParams, runtimeCtx);
126
+ const stepDefaultParams = await resolveDefaultParams(step.defaultParams, runtimeCtx);
82
127
  combinedParams = { ...actionDefaultParams, ...stepDefaultParams };
83
128
  } else if (step.handler) {
129
+ // 对于内联 handler,为该步创建临时上下文并注入 step 定义
130
+ runtimeCtx = await createEphemeralContext<FlowRuntimeContext>(flowContext, step);
84
131
  handler = step.handler;
85
- const stepDefaultParams = await resolveDefaultParams(step.defaultParams, flowContext);
132
+ const stepDefaultParams = await resolveDefaultParams(step.defaultParams, runtimeCtx);
86
133
  combinedParams = { ...stepDefaultParams };
87
134
  } else {
88
135
  flowContext.logger.error(
@@ -97,12 +144,11 @@ export class FlowExecutor {
97
144
  }
98
145
  if (typeof useRawParams === 'function') {
99
146
  // eslint-disable-next-line react-hooks/rules-of-hooks
100
- useRawParams = await useRawParams(flowContext);
147
+ useRawParams = await useRawParams(runtimeCtx);
101
148
  }
102
149
  if (!useRawParams) {
103
- combinedParams = await flowContext.resolveJsonTemplate(combinedParams);
150
+ combinedParams = await runtimeCtx.resolveJsonTemplate(combinedParams);
104
151
  }
105
-
106
152
  try {
107
153
  if (!handler) {
108
154
  flowContext.logger.error(
@@ -110,13 +156,12 @@ export class FlowExecutor {
110
156
  );
111
157
  continue;
112
158
  }
113
- const currentStepResult = handler(flowContext, combinedParams);
159
+ const currentStepResult = handler(runtimeCtx, combinedParams);
114
160
  const isAwait = step.isAwait !== false;
115
161
  lastResult = isAwait ? await currentStepResult : currentStepResult;
116
162
 
117
- // Store step result
163
+ // Store step result and update context
118
164
  stepResults[stepKey] = lastResult;
119
- // update the context
120
165
  stepsRuntime[stepKey].result = stepResults[stepKey];
121
166
  } catch (error) {
122
167
  if (error instanceof FlowExitException) {
@@ -125,7 +170,6 @@ export class FlowExecutor {
125
170
  }
126
171
  if (error instanceof FlowExitAllException) {
127
172
  flowContext.logger.info(`[FlowEngine] ${error.message}`);
128
- // 传递特殊控制信号,让上层可中止后续流程
129
173
  return Promise.resolve(error);
130
174
  }
131
175
  flowContext.logger.error(
@@ -138,119 +182,169 @@ export class FlowExecutor {
138
182
  return Promise.resolve(stepResults);
139
183
  }
140
184
 
185
+ // runAutoFlows 已移除:统一通过 dispatchEvent('beforeRender') + useCache 控制
186
+
141
187
  /**
142
- * Execute all auto-apply flows for model.
188
+ * Dispatch an event to flows bound via flow.on and execute them.
143
189
  */
144
- async runAutoFlows(model: FlowModel, inputArgs?: Record<string, any>, useCache = true): Promise<any[]> {
145
- const autoApplyFlows = model.getAutoFlows();
146
-
147
- if (autoApplyFlows.length === 0) {
148
- model.context.logger.warn(`FlowModel: No auto-apply flows found for model '${model.uid}'`);
149
- return [];
150
- }
190
+ async dispatchEvent(
191
+ model: FlowModel,
192
+ eventName: string,
193
+ inputArgs?: Record<string, any>,
194
+ options?: DispatchEventOptions,
195
+ ): Promise<any> {
196
+ const isBeforeRender = eventName === 'beforeRender';
197
+ // 由模型层决定缺省值;执行器仅按显式 options 执行
198
+ const sequential = !!options?.sequential;
199
+ const useCache = !!options?.useCache;
200
+ // beforeRender 特殊处理:出错时一律抛出(用于错误边界捕获)
201
+ const throwOnError = isBeforeRender;
151
202
 
152
- const cacheKey = useCache
153
- ? FlowEngine.generateApplyFlowCacheKey(model.getAutoFlowCacheScope(), 'all', model.uid)
154
- : null;
203
+ const runId = `${model.uid}-${eventName}-${Date.now()}`;
204
+ const logger = model.context.logger;
155
205
 
156
- if (cacheKey && this.engine) {
157
- const cachedEntry = this.engine.applyFlowCache.get(cacheKey);
158
- if (cachedEntry) {
159
- if (cachedEntry.status === 'resolved') {
160
- model.context.logger.debug(`[FlowEngine.applyAutoFlows] Using cached result for model: ${model.uid}`);
161
- return cachedEntry.data;
162
- }
163
- if (cachedEntry.status === 'rejected') throw cachedEntry.error;
164
- if (cachedEntry.status === 'pending') return await cachedEntry.promise;
206
+ try {
207
+ await this.engine.emitter.emitAsync(`model:event:${eventName}:start`, {
208
+ uid: model.uid,
209
+ model,
210
+ runId,
211
+ inputArgs,
212
+ });
213
+ await model.onDispatchEventStart?.(eventName, options, inputArgs);
214
+ } catch (err) {
215
+ if (isBeforeRender && err instanceof FlowExitException) {
216
+ logger.debug(`[FlowModel.dispatchEvent] ${err.message}`);
217
+ return [];
165
218
  }
219
+ // 进入错误钩子并记录
220
+ try {
221
+ await model.onDispatchEventError?.(eventName, options, inputArgs, err as Error);
222
+ } finally {
223
+ logger.error({ err }, `BaseModel.dispatchEvent: Start hook error for event '${eventName}'`);
224
+ }
225
+ if (throwOnError) throw err;
226
+ return;
166
227
  }
167
228
 
168
- const executeAutoFlows = async (): Promise<any[]> => {
169
- const results: any[] = [];
170
- const runId = `${model.uid}-autoFlow-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
171
- const logger = model.context.logger.child({ module: 'flow-engine', component: 'FlowExecutor', runId });
172
- logger.debug(
173
- `[FlowExecutor] runAutoFlows: uid=${model.uid}, isFork=${
174
- (model as any)?.isFork === true
175
- }, useCache=${useCache}, runId=${runId}, flows=${autoApplyFlows.map((f) => f.key).join(',')}`,
176
- );
177
- try {
178
- if (autoApplyFlows.length === 0) {
179
- logger.warn(`FlowModel: No auto-apply flows found for model '${model.uid}'`);
180
- } else {
181
- for (const flow of autoApplyFlows) {
182
- try {
183
- logger.debug(`[FlowExecutor] runFlow: uid=${model.uid}, flowKey=${flow.key}`);
184
- const result = await this.runFlow(model, flow.key, inputArgs, runId);
185
- if (result instanceof FlowExitAllException) {
186
- logger.debug(`[FlowEngine.applyAutoFlows] ${result.message}`);
187
- break; // 终止后续流程执行
188
- }
189
- results.push(result);
190
- } catch (error) {
191
- logger.error({ err: error }, `FlowModel.applyAutoFlows: Error executing auto-apply flow '${flow.key}':`);
192
- throw error;
229
+ // 构造待执行的 flows 列表
230
+ const flows = isBeforeRender
231
+ ? model.getEventFlows('beforeRender') // beforeRender 事件集合(兼容未声明 on 且非 manual 的定义)
232
+ : Array.from(model.getFlows().values()).filter((flow) => {
233
+ const on = flow.on;
234
+ if (!on) return false;
235
+ if (typeof on === 'string') return on === eventName;
236
+ if (typeof on === 'object') return on.eventName === eventName;
237
+ return false;
238
+ });
239
+
240
+ // 组装执行函数(返回值用于缓存;beforeRender 返回 results:any[],其它返回 true)
241
+ const execute = async () => {
242
+ if (sequential) {
243
+ // 顺序执行:动态流(实例级)优先,其次静态流;各自组内再按 sort 升序,最后保持原始顺序稳定
244
+ const flowsWithIndex = flows.map((f, i) => ({ f, i }));
245
+ const ordered = flowsWithIndex
246
+ .slice()
247
+ .sort((a, b) => {
248
+ const regA = a.f['flowRegistry'] as any;
249
+ const regB = b.f['flowRegistry'] as any;
250
+ const typeA = regA?.constructor?._type as 'instance' | 'global' | undefined;
251
+ const typeB = regB?.constructor?._type as 'instance' | 'global' | undefined;
252
+ const groupA = typeA === 'instance' ? 0 : 1; // 实例=0,静态=1
253
+ const groupB = typeB === 'instance' ? 0 : 1;
254
+ if (groupA !== groupB) return groupA - groupB;
255
+ const sa = a.f.sort ?? 0;
256
+ const sb = b.f.sort ?? 0;
257
+ if (sa !== sb) return sa - sb;
258
+ return a.i - b.i; // 稳定排序:保持原有相对顺序
259
+ })
260
+ .map((x) => x.f);
261
+ const results: any[] = [];
262
+ for (const flow of ordered) {
263
+ try {
264
+ logger.debug(
265
+ `BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}' (sequential).`,
266
+ );
267
+ const result = await this.runFlow(model, flow.key, inputArgs, runId);
268
+ if (result instanceof FlowExitAllException) {
269
+ logger.debug(`[FlowEngine.dispatchEvent] ${result.message}`);
270
+ break; // 终止后续
193
271
  }
272
+ results.push(result);
273
+ } catch (error) {
274
+ logger.error(
275
+ { err: error },
276
+ `BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}' (sequential):`,
277
+ );
278
+ throw error;
194
279
  }
195
280
  }
196
281
  return results;
197
- } catch (error) {
198
- if (error instanceof FlowExitException) {
199
- logger.debug(`[FlowEngine.applyAutoFlows] ${error.message}`);
200
- return results;
201
- }
202
- throw error;
203
282
  }
283
+
284
+ // 并行
285
+ const results = await Promise.all(
286
+ flows.map(async (flow) => {
287
+ logger.debug(`BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}'.`);
288
+ try {
289
+ return await this.runFlow(model, flow.key, inputArgs, runId);
290
+ } catch (error) {
291
+ logger.error(
292
+ { err: error },
293
+ `BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}':`,
294
+ );
295
+ if (throwOnError) throw error;
296
+ return undefined;
297
+ }
298
+ }),
299
+ );
300
+ return results.filter((x) => x !== undefined);
204
301
  };
205
302
 
206
- if (!cacheKey || !this.engine) {
207
- return await executeAutoFlows();
208
- }
303
+ // 缓存键:按事件+scope 统一管理(beforeRender 也使用事件名 beforeRender)
304
+ const argsKey = useCache ? JSON.stringify(inputArgs ?? {}) : '';
305
+ const cacheKey = useCache
306
+ ? FlowEngine.generateApplyFlowCacheKey(
307
+ `event:${model.getFlowCacheScope(eventName)}:${argsKey}`,
308
+ eventName,
309
+ model.uid,
310
+ )
311
+ : null;
209
312
 
210
- const promise = executeAutoFlows()
211
- .then((result) => {
212
- this.engine.applyFlowCache.set(cacheKey, {
213
- status: 'resolved',
214
- data: result,
215
- promise: Promise.resolve(result),
216
- } as ApplyFlowCacheEntry);
217
- return result;
218
- })
219
- .catch((err) => {
220
- this.engine.applyFlowCache.set(cacheKey, {
221
- status: 'rejected',
222
- error: err,
223
- promise: Promise.reject(err),
224
- } as ApplyFlowCacheEntry);
225
- throw err;
313
+ try {
314
+ const result = await this.withApplyFlowCache(cacheKey, execute);
315
+ // 事件结束钩子
316
+ try {
317
+ await model.onDispatchEventEnd?.(eventName, options, inputArgs, result);
318
+ } catch (hookErr) {
319
+ logger.error({ err: hookErr }, `BaseModel.dispatchEvent: End hook error for event '${eventName}'`);
320
+ }
321
+ await this.engine.emitter.emitAsync(`model:event:${eventName}:end`, {
322
+ uid: model.uid,
323
+ model,
324
+ runId,
325
+ inputArgs,
326
+ result,
226
327
  });
227
-
228
- this.engine.applyFlowCache.set(cacheKey, { status: 'pending', promise } as ApplyFlowCacheEntry);
229
- return await promise;
230
- }
231
-
232
- /**
233
- * Dispatch an event to flows bound via flow.on and execute them.
234
- */
235
- async dispatchEvent(model: FlowModel, eventName: string, inputArgs?: Record<string, any>): Promise<void> {
236
- const flows = Array.from(model.getFlows().values()).filter((flow) => {
237
- const on = flow.on;
238
- if (!on) return false;
239
- if (typeof on === 'string') return on === eventName;
240
- if (typeof on === 'object') return on.eventName === eventName;
241
- return false;
242
- });
243
- const runId = `${model.uid}-${eventName}-${Date.now()}`;
244
- const logger = model.context.logger;
245
- const promises = flows.map((flow) => {
246
- logger.debug(`BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}'.`);
247
- return this.runFlow(model, flow.key, inputArgs, runId).catch((error) => {
248
- logger.error(
249
- { err: error },
250
- `BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}':`,
251
- );
328
+ return result;
329
+ } catch (error) {
330
+ // 进入错误钩子并记录
331
+ try {
332
+ await model.onDispatchEventError?.(eventName, options, inputArgs, error as Error);
333
+ } catch (_) {
334
+ // swallow secondary hook error
335
+ }
336
+ model.context.logger.error(
337
+ { err: error },
338
+ `BaseModel.dispatchEvent: Error executing event '${eventName}' for model '${model.uid}':`,
339
+ );
340
+ await this.engine.emitter.emitAsync(`model:event:${eventName}:error`, {
341
+ uid: model.uid,
342
+ model,
343
+ runId,
344
+ inputArgs,
345
+ error,
252
346
  });
253
- });
254
- await Promise.all(promises);
347
+ if (throwOnError) throw error;
348
+ }
255
349
  }
256
350
  }
@@ -0,0 +1,197 @@
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, vi, beforeEach } from 'vitest';
11
+ import { FlowEngine } from '../../flowEngine';
12
+ import { FlowModel } from '../../models';
13
+ import type { FlowDefinitionOptions, FlowModelOptions } from '../../types';
14
+
15
+ describe('defineProperties/defineMethods injection', () => {
16
+ let engine: FlowEngine;
17
+
18
+ beforeEach(() => {
19
+ engine = new FlowEngine();
20
+ vi.restoreAllMocks();
21
+ vi.clearAllMocks();
22
+ });
23
+
24
+ it('engine.context.runAction applies defs before defaultParams and handler', async () => {
25
+ const handler = vi.fn((_ctx, params) => params);
26
+ engine.registerActions({
27
+ test: {
28
+ name: 'test',
29
+ defineProperties: { foo: { value: 5 } },
30
+ defineMethods: { add: (x: number) => x + 1 },
31
+ defaultParams: (ctx) => ({ a: (ctx as any).foo, b: (ctx as any).add(2) }),
32
+ handler,
33
+ },
34
+ });
35
+
36
+ const result = await (engine.context as any).runAction('test');
37
+ expect(handler).toHaveBeenCalledTimes(1);
38
+ expect(result).toEqual({ a: 5, b: 3 });
39
+ });
40
+
41
+ it('model.context.runAction applies defs before defaultParams and handler', async () => {
42
+ class M extends FlowModel {}
43
+ engine.registerModels({ M });
44
+ const h = vi.fn((_ctx, params) => ({ ...params, y: (_ctx as any).mul(3) }));
45
+ M.registerAction({
46
+ name: 'mAction',
47
+ defineProperties: async () => ({ al: { value: 7 } }),
48
+ defineMethods: async () => ({ mul: (x: number) => x * 2 }),
49
+ defaultParams: (ctx) => ({ a: (ctx as any).al }),
50
+ handler: h,
51
+ });
52
+ const m = engine.createModel<M>({ use: 'M' });
53
+ const res = await (m.context as any).runAction('mAction', { x: 3 });
54
+ expect(h).toHaveBeenCalledTimes(1);
55
+ expect(res).toEqual({ a: 7, x: 3, y: 6 });
56
+ });
57
+
58
+ function createModelWithFlows(uid: string, flows: Record<string, Omit<FlowDefinitionOptions, 'key'>>) {
59
+ const model = new FlowModel({
60
+ uid,
61
+ flowEngine: engine,
62
+ stepParams: {},
63
+ subModels: {},
64
+ flowRegistry: flows,
65
+ } as FlowModelOptions);
66
+ return model;
67
+ }
68
+
69
+ it('FlowExecutor: action-based step injects defs before defaultParams/handler', async () => {
70
+ const stepHandler = vi.fn((_ctx, params) => params);
71
+ engine.registerActions({
72
+ act: {
73
+ name: 'act',
74
+ defineProperties: { base: { value: 10 } },
75
+ defineMethods: { inc: (x: number) => x + 1 },
76
+ handler: stepHandler,
77
+ },
78
+ });
79
+
80
+ const flows = {
81
+ f: {
82
+ steps: {
83
+ s: {
84
+ use: 'act',
85
+ defaultParams: (ctx) => ({ p: (ctx as any).base, q: (ctx as any).inc(4) }),
86
+ },
87
+ },
88
+ },
89
+ } satisfies Record<string, Omit<FlowDefinitionOptions, 'key'>>;
90
+
91
+ const m = createModelWithFlows('m1', flows);
92
+ const res = await engine.executor.runFlow(m, 'f');
93
+ expect(stepHandler).toHaveBeenCalledTimes(1);
94
+ const [, received] = stepHandler.mock.calls[0];
95
+ expect(received).toEqual({ p: 10, q: 5 });
96
+ expect(res.s).toEqual({ p: 10, q: 5 });
97
+ });
98
+
99
+ it('FlowExecutor: inline step injects defs before defaultParams/handler', async () => {
100
+ const stepHandler = vi.fn((_ctx, params) => ({ ...params, v: (_ctx as any).twice(5) }));
101
+ const flows = {
102
+ f: {
103
+ steps: {
104
+ s: {
105
+ defineProperties: { k: { value: 8 } },
106
+ defineMethods: { twice: (x: number) => 2 * x },
107
+ defaultParams: (ctx) => ({ m: (ctx as any).k }),
108
+ handler: stepHandler,
109
+ },
110
+ },
111
+ },
112
+ } satisfies Record<string, Omit<FlowDefinitionOptions, 'key'>>;
113
+
114
+ const m = createModelWithFlows('m2', flows);
115
+ const res = await engine.executor.runFlow(m, 'f');
116
+ expect(stepHandler).toHaveBeenCalledTimes(1);
117
+ const [, received] = stepHandler.mock.calls[0];
118
+ // params 仅包含 defaultParams 合并后的值;v 是 handler 计算的返回值
119
+ expect(received).toEqual({ m: 8 });
120
+ expect(res.s).toEqual({ m: 8, v: 10 });
121
+ });
122
+
123
+ it('FlowExecutor: step defs are isolated per step', async () => {
124
+ const flows = {
125
+ f: {
126
+ steps: {
127
+ a: {
128
+ defineMethods: { only: (x: number) => x + 1 },
129
+ handler: (_ctx, _params) => ({ ok: true }),
130
+ },
131
+ b: {
132
+ // 这里期望无法访问到上一步注入的方法,从而抛出错误
133
+ defaultParams: (ctx) => ({ v: (ctx as any).only(1) }),
134
+ handler: (_ctx, params) => params,
135
+ },
136
+ },
137
+ },
138
+ } satisfies Record<string, Omit<FlowDefinitionOptions, 'key'>>;
139
+ const m = createModelWithFlows('m-isolation', flows);
140
+ const res = await engine.executor.runFlow(m, 'f');
141
+ // a 正常执行,b 无法访问到 a 注入的方法,defaultParams 出错被 resolveDefaultParams 捕获后为空对象
142
+ expect(res).toEqual({ a: { ok: true }, b: {} });
143
+ });
144
+
145
+ it('FlowExecutor: step defs override action defs when same key', async () => {
146
+ const stepHandler = vi.fn((_ctx, params) => params);
147
+ engine.registerActions({
148
+ act: {
149
+ name: 'act',
150
+ defineProperties: { foo: { value: 1 } },
151
+ defineMethods: { id: (x: number) => x },
152
+ handler: stepHandler,
153
+ },
154
+ });
155
+ const flows = {
156
+ f: {
157
+ steps: {
158
+ s: {
159
+ use: 'act',
160
+ defineProperties: { foo: { value: 2 } },
161
+ defineMethods: { id: (x: number) => x + 100 },
162
+ defaultParams: (ctx) => ({ a: (ctx as any).foo, b: (ctx as any).id(1) }),
163
+ },
164
+ },
165
+ },
166
+ } satisfies Record<string, Omit<FlowDefinitionOptions, 'key'>>;
167
+ const m = createModelWithFlows('m3', flows);
168
+ await engine.executor.runFlow(m, 'f');
169
+ const [, received] = stepHandler.mock.calls[0];
170
+ expect(received).toEqual({ a: 2, b: 101 });
171
+ });
172
+
173
+ it('FlowExecutor: subsequent defineProperty/defineMethod write to parent context (not scoped)', async () => {
174
+ const flows = {
175
+ f: {
176
+ steps: {
177
+ a: {
178
+ handler: (ctx) => {
179
+ (ctx as any).defineProperty('mark', { value: 7 });
180
+ (ctx as any).defineMethod('inc', (x: number) => x + 1);
181
+ return { ok: true };
182
+ },
183
+ },
184
+ b: {
185
+ // 无注入,直接读取上一步通过 defineProperty/defineMethod 挂在到父级的能力
186
+ defaultParams: (ctx) => ({ v: (ctx as any).mark, w: (ctx as any).inc(2) }),
187
+ handler: (_ctx, params) => params,
188
+ },
189
+ },
190
+ },
191
+ } satisfies Record<string, Omit<FlowDefinitionOptions, 'key'>>;
192
+
193
+ const m = engine.createModel<FlowModel>({ use: FlowModel.name, flowRegistry: flows });
194
+ const res = await engine.executor.runFlow(m, 'f');
195
+ expect(res).toEqual({ a: { ok: true }, b: { v: 7, w: 3 } });
196
+ });
197
+ });