@nocobase/flow-engine 2.0.0-alpha.3 → 2.0.0-alpha.30

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 (418) 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/components/FieldModelRenderer.js +10 -4
  9. package/lib/components/FieldSkeleton.d.ts +10 -0
  10. package/lib/components/FieldSkeleton.js +64 -0
  11. package/lib/components/FlowContextSelector.js +7 -2
  12. package/lib/components/FlowModelRenderer.d.ts +2 -5
  13. package/lib/components/FlowModelRenderer.js +16 -47
  14. package/lib/components/FormItem.js +5 -1
  15. package/lib/{runjs-context/snippets/global/requireAsync.snippet.d.ts → components/dnd/findModelUidPosition.d.ts} +4 -7
  16. package/lib/{runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js → components/dnd/findModelUidPosition.js} +23 -19
  17. package/lib/components/dnd/gridDragPlanner.d.ts +130 -0
  18. package/lib/components/dnd/gridDragPlanner.js +497 -0
  19. package/lib/components/dnd/index.d.ts +2 -2
  20. package/lib/components/dnd/index.js +5 -5
  21. package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +2 -2
  22. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +17 -5
  23. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +2 -2
  24. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
  25. package/lib/components/variables/VariableInput.js +16 -2
  26. package/lib/components/variables/VariableTag.js +43 -2
  27. package/lib/components/variables/types.d.ts +2 -0
  28. package/lib/data-source/index.d.ts +12 -4
  29. package/lib/data-source/index.js +19 -13
  30. package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
  31. package/lib/data-source/sortCollectionsByInherits.js +71 -0
  32. package/lib/executor/FlowExecutor.d.ts +4 -5
  33. package/lib/executor/FlowExecutor.js +135 -100
  34. package/lib/flowContext.d.ts +33 -5
  35. package/lib/flowContext.js +193 -76
  36. package/lib/flowEngine.d.ts +8 -1
  37. package/lib/flowEngine.js +18 -6
  38. package/lib/flowSettings.d.ts +2 -1
  39. package/lib/flowSettings.js +12 -8
  40. package/lib/hooks/useApplyAutoFlows.js +2 -1
  41. package/lib/index.d.ts +7 -1
  42. package/lib/index.js +32 -3
  43. package/lib/locale/en-US.json +4 -2
  44. package/lib/locale/index.d.ts +4 -0
  45. package/lib/locale/zh-CN.json +4 -2
  46. package/lib/models/CollectionFieldModel.d.ts +2 -0
  47. package/lib/models/CollectionFieldModel.js +43 -3
  48. package/lib/models/flowModel.d.ts +28 -29
  49. package/lib/models/flowModel.js +114 -92
  50. package/lib/models/forkFlowModel.d.ts +4 -4
  51. package/lib/models/forkFlowModel.js +32 -8
  52. package/lib/provider.d.ts +3 -1
  53. package/lib/provider.js +7 -5
  54. package/lib/resources/multiRecordResource.js +2 -0
  55. package/lib/resources/singleRecordResource.js +1 -0
  56. package/lib/resources/sqlResource.d.ts +1 -0
  57. package/lib/resources/sqlResource.js +20 -24
  58. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
  59. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +27 -20
  60. package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
  61. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
  62. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
  63. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
  64. package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
  65. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
  66. package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
  67. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
  68. package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
  69. package/lib/runjs-context/contexts/JSItemRunJSContext.js +24 -20
  70. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
  71. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
  72. package/lib/runjs-context/contexts/base.d.ts +9 -0
  73. package/lib/runjs-context/contexts/base.js +183 -0
  74. package/lib/runjs-context/helpers.d.ts +5 -2
  75. package/lib/runjs-context/helpers.js +36 -27
  76. package/lib/runjs-context/registry.d.ts +7 -4
  77. package/lib/runjs-context/registry.js +10 -42
  78. package/lib/runjs-context/setup.d.ts +9 -0
  79. package/lib/runjs-context/setup.js +82 -0
  80. package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
  81. package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
  82. package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
  83. package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
  84. package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
  85. package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
  86. package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
  87. package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
  88. package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
  89. package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
  90. package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
  91. package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
  92. package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
  93. package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
  94. package/lib/runjs-context/snippets/index.d.ts +14 -3
  95. package/lib/runjs-context/snippets/index.js +161 -40
  96. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
  97. package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
  98. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
  99. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
  100. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
  101. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
  102. package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
  103. package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
  104. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
  105. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
  106. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
  107. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
  108. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
  109. package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
  110. package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
  111. package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
  112. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
  113. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
  114. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
  115. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
  116. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
  117. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
  118. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
  119. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
  120. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
  121. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
  122. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
  123. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
  124. package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
  125. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
  126. package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
  127. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
  128. package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
  129. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
  130. package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
  131. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
  132. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
  133. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
  134. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
  135. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
  136. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
  137. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
  138. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
  139. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
  140. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
  141. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
  142. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
  143. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
  144. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
  145. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
  146. package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
  147. package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
  148. package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
  149. package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
  150. package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
  151. package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
  152. package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
  153. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
  154. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
  155. package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
  156. package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
  157. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
  158. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
  159. package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
  160. package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
  161. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
  162. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
  163. package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
  164. package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
  165. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
  166. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
  167. package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
  168. package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
  169. package/lib/runjs-context/snippets/types.d.ts +9 -1
  170. package/lib/types.d.ts +28 -3
  171. package/lib/types.js +4 -3
  172. package/lib/utils/buildSettingsViewInputArgs.d.ts +19 -0
  173. package/lib/utils/buildSettingsViewInputArgs.js +75 -0
  174. package/lib/utils/createEphemeralContext.d.ts +13 -0
  175. package/lib/utils/createEphemeralContext.js +140 -0
  176. package/lib/utils/index.d.ts +3 -2
  177. package/lib/utils/index.js +5 -2
  178. package/lib/utils/jsxTransform.d.ts +15 -0
  179. package/lib/utils/jsxTransform.js +68 -0
  180. package/lib/utils/params-resolvers.js +3 -3
  181. package/lib/utils/safeGlobals.d.ts +5 -3
  182. package/lib/utils/safeGlobals.js +40 -0
  183. package/lib/utils/schema-utils.js +2 -2
  184. package/lib/utils/serverContextParams.d.ts +1 -0
  185. package/lib/utils/variablesParams.d.ts +9 -5
  186. package/lib/utils/variablesParams.js +47 -36
  187. package/lib/views/PageComponent.js +2 -1
  188. package/lib/views/createViewMeta.d.ts +29 -1
  189. package/lib/views/createViewMeta.js +338 -72
  190. package/lib/views/index.d.ts +1 -0
  191. package/lib/views/index.js +3 -0
  192. package/lib/views/useDialog.d.ts +8 -8
  193. package/lib/views/useDialog.js +8 -7
  194. package/lib/views/useDrawer.d.ts +8 -8
  195. package/lib/views/useDrawer.js +40 -26
  196. package/lib/views/usePage.d.ts +8 -8
  197. package/lib/views/usePage.js +8 -7
  198. package/package.json +5 -3
  199. package/src/BlockScopedFlowEngine.ts +86 -0
  200. package/src/FlowContextProvider.tsx +4 -2
  201. package/src/JSRunner.ts +3 -0
  202. package/src/ViewScopedFlowEngine.ts +1 -1
  203. package/src/__tests__/JSRunner.test.ts +62 -53
  204. package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
  205. package/src/__tests__/createViewMeta.popup.test.ts +132 -0
  206. package/src/__tests__/flow-engine.test.ts +3 -0
  207. package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
  208. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  209. package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
  210. package/src/__tests__/globalFlowRegistry.test.ts +1 -1
  211. package/src/__tests__/runjsContext.test.ts +216 -35
  212. package/src/__tests__/runjsContextImplementations.test.ts +217 -0
  213. package/src/__tests__/runjsContextRuntime.test.ts +269 -0
  214. package/src/__tests__/runjsEdgeCases.test.ts +281 -0
  215. package/src/__tests__/runjsLocales.test.ts +36 -0
  216. package/src/__tests__/runjsRuntimeFeatures.test.ts +449 -0
  217. package/src/__tests__/runjsSnippets.test.ts +140 -0
  218. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  219. package/src/components/DynamicFlowsEditor.tsx +3 -4
  220. package/src/components/FieldModelRenderer.tsx +16 -5
  221. package/src/components/FieldSkeleton.tsx +27 -0
  222. package/src/components/FlowContextSelector.tsx +6 -2
  223. package/src/components/FlowModelRenderer.tsx +30 -78
  224. package/src/components/FormItem.tsx +8 -1
  225. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +5 -5
  226. package/src/components/__tests__/gridDragPlanner.test.ts +494 -0
  227. package/src/components/dnd/README.md +149 -0
  228. package/src/components/dnd/findModelUidPosition.ts +26 -0
  229. package/src/components/dnd/gridDragPlanner.ts +659 -0
  230. package/src/components/dnd/index.tsx +3 -3
  231. package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +1 -1
  232. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +17 -4
  233. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +1 -1
  234. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
  235. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +424 -0
  236. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
  237. package/src/components/variables/VariableInput.tsx +22 -2
  238. package/src/components/variables/VariableTag.tsx +54 -2
  239. package/src/components/variables/types.ts +2 -0
  240. package/src/data-source/__tests__/sortCollectionsByInherits.test.ts +125 -0
  241. package/src/data-source/index.ts +17 -11
  242. package/src/data-source/sortCollectionsByInherits.ts +61 -0
  243. package/src/executor/FlowExecutor.ts +178 -121
  244. package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
  245. package/src/executor/__tests__/flowExecutor.test.ts +151 -5
  246. package/src/flowContext.ts +266 -97
  247. package/src/flowEngine.ts +21 -6
  248. package/src/flowSettings.ts +9 -4
  249. package/src/hooks/useApplyAutoFlows.ts +3 -1
  250. package/src/index.ts +12 -1
  251. package/src/locale/en-US.json +4 -2
  252. package/src/locale/zh-CN.json +4 -2
  253. package/src/models/CollectionFieldModel.tsx +43 -4
  254. package/src/models/__tests__/flowModel.getFlows.sort.test.ts +4 -4
  255. package/src/models/__tests__/flowModel.test.ts +234 -111
  256. package/src/models/__tests__/forkFlowModel.test.ts +22 -7
  257. package/src/models/flowModel.tsx +149 -125
  258. package/src/models/forkFlowModel.ts +41 -8
  259. package/src/provider.tsx +10 -7
  260. package/src/resources/multiRecordResource.ts +2 -0
  261. package/src/resources/singleRecordResource.ts +1 -0
  262. package/src/resources/sqlResource.ts +20 -25
  263. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +28 -21
  264. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +46 -34
  265. package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
  266. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
  267. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
  268. package/src/runjs-context/contexts/JSItemRunJSContext.ts +25 -21
  269. package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
  270. package/src/runjs-context/contexts/base.ts +171 -0
  271. package/src/runjs-context/helpers.ts +32 -30
  272. package/src/runjs-context/registry.ts +16 -47
  273. package/src/runjs-context/setup.ts +51 -0
  274. package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
  275. package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
  276. package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
  277. package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
  278. package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
  279. package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
  280. package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
  281. package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
  282. package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
  283. package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
  284. package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
  285. package/src/runjs-context/snippets/index.ts +177 -39
  286. package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
  287. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
  288. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
  289. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
  290. package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
  291. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
  292. package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
  293. package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
  294. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
  295. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
  296. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
  297. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
  298. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
  299. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
  300. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
  301. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
  302. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
  303. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
  304. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
  305. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
  306. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
  307. package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
  308. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
  309. package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
  310. package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
  311. package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
  312. package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
  313. package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
  314. package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
  315. package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
  316. package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
  317. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
  318. package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
  319. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
  320. package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
  321. package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
  322. package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
  323. package/src/runjs-context/snippets/types.ts +5 -1
  324. package/src/types.ts +34 -0
  325. package/src/utils/__tests__/jsxTransform.test.ts +38 -0
  326. package/src/utils/__tests__/safeGlobals.test.ts +22 -1
  327. package/src/utils/buildSettingsViewInputArgs.ts +72 -0
  328. package/src/utils/createEphemeralContext.ts +142 -0
  329. package/src/utils/index.ts +2 -2
  330. package/src/utils/jsxTransform.ts +39 -0
  331. package/src/utils/params-resolvers.ts +2 -2
  332. package/src/utils/safeGlobals.ts +49 -3
  333. package/src/utils/schema-utils.ts +1 -1
  334. package/src/utils/serverContextParams.ts +1 -0
  335. package/src/utils/variablesParams.ts +50 -38
  336. package/src/views/PageComponent.tsx +1 -1
  337. package/src/views/createViewMeta.ts +393 -70
  338. package/src/views/index.tsx +1 -0
  339. package/src/views/useDialog.tsx +12 -10
  340. package/src/views/useDrawer.tsx +60 -36
  341. package/src/views/usePage.tsx +13 -10
  342. package/lib/components/dnd/getMousePositionOnElement.d.ts +0 -50
  343. package/lib/components/dnd/getMousePositionOnElement.js +0 -95
  344. package/lib/components/dnd/moveBlock.d.ts +0 -33
  345. package/lib/components/dnd/moveBlock.js +0 -302
  346. package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
  347. package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
  348. package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
  349. package/lib/runjs-context/index.d.ts +0 -19
  350. package/lib/runjs-context/index.js +0 -57
  351. package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
  352. package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
  353. package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
  354. package/lib/runjs-context/snippets/global/api-request-post.snippet.js +0 -42
  355. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
  356. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.js +0 -41
  357. package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
  358. package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
  359. package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
  360. package/lib/runjs-context/snippets/global/try-catch-async.snippet.js +0 -44
  361. package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
  362. package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
  363. package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
  364. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
  365. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
  366. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
  367. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
  368. package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
  369. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
  370. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
  371. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
  372. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
  373. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
  374. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
  375. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
  376. package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
  377. package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
  378. package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
  379. package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
  380. package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
  381. package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
  382. package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
  383. package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
  384. package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
  385. package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
  386. package/src/components/dnd/getMousePositionOnElement.ts +0 -115
  387. package/src/components/dnd/moveBlock.ts +0 -379
  388. package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
  389. package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
  390. package/src/runjs-context/index.ts +0 -20
  391. package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
  392. package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
  393. package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
  394. package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
  395. package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
  396. package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
  397. package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
  398. package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
  399. package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
  400. package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
  401. package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
  402. package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
  403. package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
  404. package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
  405. package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
  406. package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
  407. package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
  408. package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
  409. package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
  410. package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
  411. package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
  412. package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
  413. package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
  414. /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
  415. /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
  416. /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
  417. /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
  418. /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,132 @@ 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();
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;
146
202
 
147
- if (autoApplyFlows.length === 0) {
148
- model.context.logger.warn(`FlowModel: No auto-apply flows found for model '${model.uid}'`);
149
- return [];
150
- }
151
-
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 model.onDispatchEventStart?.(eventName, options, inputArgs);
208
+ } catch (err) {
209
+ if (isBeforeRender && err instanceof FlowExitException) {
210
+ logger.debug(`[FlowModel.dispatchEvent] ${err.message}`);
211
+ return [];
212
+ }
213
+ // 进入错误钩子并记录
214
+ try {
215
+ await model.onDispatchEventError?.(eventName, options, inputArgs, err as Error);
216
+ } finally {
217
+ logger.error({ err }, `BaseModel.dispatchEvent: Start hook error for event '${eventName}'`);
165
218
  }
219
+ if (throwOnError) throw err;
220
+ return;
166
221
  }
167
222
 
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;
223
+ // 构造待执行的 flows 列表
224
+ const flows = isBeforeRender
225
+ ? model.getEventFlows('beforeRender') // beforeRender 事件集合(兼容未声明 on 且非 manual 的定义)
226
+ : Array.from(model.getFlows().values()).filter((flow) => {
227
+ const on = flow.on;
228
+ if (!on) return false;
229
+ if (typeof on === 'string') return on === eventName;
230
+ if (typeof on === 'object') return on.eventName === eventName;
231
+ return false;
232
+ });
233
+
234
+ // 组装执行函数(返回值用于缓存;beforeRender 返回 results:any[],其它返回 true)
235
+ const execute = async () => {
236
+ if (sequential) {
237
+ const ordered = flows.slice().sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
238
+ const results: any[] = [];
239
+ for (const flow of ordered) {
240
+ try {
241
+ logger.debug(
242
+ `BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}' (sequential).`,
243
+ );
244
+ const result = await this.runFlow(model, flow.key, inputArgs, runId);
245
+ if (result instanceof FlowExitAllException) {
246
+ logger.debug(`[FlowEngine.dispatchEvent] ${result.message}`);
247
+ break; // 终止后续
193
248
  }
249
+ results.push(result);
250
+ } catch (error) {
251
+ logger.error(
252
+ { err: error },
253
+ `BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}' (sequential):`,
254
+ );
255
+ throw error;
194
256
  }
195
257
  }
196
258
  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
259
  }
204
- };
205
-
206
- if (!cacheKey || !this.engine) {
207
- return await executeAutoFlows();
208
- }
209
260
 
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;
226
- });
261
+ // 并行
262
+ const results = await Promise.all(
263
+ flows.map(async (flow) => {
264
+ logger.debug(`BaseModel '${model.uid}' dispatching event '${eventName}' to flow '${flow.key}'.`);
265
+ try {
266
+ return await this.runFlow(model, flow.key, inputArgs, runId);
267
+ } catch (error) {
268
+ logger.error(
269
+ { err: error },
270
+ `BaseModel.dispatchEvent: Error executing event-triggered flow '${flow.key}' for event '${eventName}':`,
271
+ );
272
+ if (throwOnError) throw error;
273
+ return undefined;
274
+ }
275
+ }),
276
+ );
277
+ return results.filter((x) => x !== undefined);
278
+ };
227
279
 
228
- this.engine.applyFlowCache.set(cacheKey, { status: 'pending', promise } as ApplyFlowCacheEntry);
229
- return await promise;
230
- }
280
+ // 缓存键:按事件+scope 统一管理(beforeRender 也使用事件名 beforeRender)
281
+ const argsKey = useCache ? JSON.stringify(inputArgs ?? {}) : '';
282
+ const cacheKey = useCache
283
+ ? FlowEngine.generateApplyFlowCacheKey(
284
+ `event:${model.getFlowCacheScope(eventName)}:${argsKey}`,
285
+ eventName,
286
+ model.uid,
287
+ )
288
+ : null;
231
289
 
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
- );
252
- });
253
- });
254
- await Promise.all(promises);
290
+ try {
291
+ const result = await this.withApplyFlowCache(cacheKey, execute);
292
+ // 事件结束钩子
293
+ try {
294
+ await model.onDispatchEventEnd?.(eventName, options, inputArgs, result);
295
+ } catch (hookErr) {
296
+ logger.error({ err: hookErr }, `BaseModel.dispatchEvent: End hook error for event '${eventName}'`);
297
+ }
298
+ return result;
299
+ } catch (error) {
300
+ // 进入错误钩子并记录
301
+ try {
302
+ await model.onDispatchEventError?.(eventName, options, inputArgs, error as Error);
303
+ } catch (_) {
304
+ // swallow secondary hook error
305
+ }
306
+ model.context.logger.error(
307
+ { err: error },
308
+ `BaseModel.dispatchEvent: Error executing event '${eventName}' for model '${model.uid}':`,
309
+ );
310
+ if (throwOnError) throw error;
311
+ }
255
312
  }
256
313
  }
@@ -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
+ });