@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
@@ -0,0 +1,154 @@
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, expect, it, vi } from 'vitest';
11
+ import { APIClient as SDKApiClient } from '@nocobase/sdk';
12
+ import { FlowEngine } from '../flowEngine';
13
+ import { createBlockScopedEngine } from '../BlockScopedFlowEngine';
14
+ import { FlowModel } from '../models';
15
+
16
+ describe('BlockScopedFlowEngine', () => {
17
+ it('shares global actions/events and model classes with parent', async () => {
18
+ const parent = new FlowEngine();
19
+ // 在运行时 host app 会注入 api,这里补一个最小 mock,避免偶发访问 ctx.auth 抛错
20
+ const api = new SDKApiClient({ storageType: 'memory' });
21
+ api.auth.role = 'guest';
22
+ api.auth.locale = 'en-US';
23
+ api.auth.token = 't';
24
+ parent.context.defineProperty('api', { value: api });
25
+ parent.registerActions({
26
+ ping: {
27
+ name: 'ping',
28
+ handler: () => 'pong',
29
+ },
30
+ });
31
+ const child = createBlockScopedEngine(parent);
32
+
33
+ // child can read parent's action
34
+ expect(child.getAction('ping')).toBeDefined();
35
+
36
+ // registering via child should affect parent
37
+ child.registerActions({ pong: { name: 'pong', handler: () => 'ok' } });
38
+ expect(parent.getAction('pong')).toBeDefined();
39
+
40
+ // model class resolution is proxied
41
+ class TestModel extends FlowModel {}
42
+ parent.registerModels({ TestModel });
43
+ expect(child.getModelClass('TestModel')).toBeDefined();
44
+ });
45
+
46
+ it('isolates model instances map and beforeRender cache across engines with identical model uid', async () => {
47
+ const parent = new FlowEngine();
48
+ // 提供最小 api mock,满足 ctx.auth 相关 getter
49
+ const api = new SDKApiClient({ storageType: 'memory' });
50
+ api.auth.role = 'guest';
51
+ api.auth.locale = 'en-US';
52
+ api.auth.token = 't';
53
+ parent.context.defineProperty('api', { value: api });
54
+ const child = createBlockScopedEngine(parent);
55
+
56
+ let count = 0;
57
+ class CounterModel extends FlowModel {}
58
+ CounterModel.registerFlow({
59
+ key: 'autoCounter',
60
+ steps: {
61
+ s1: {
62
+ handler: async () => {
63
+ count += 1;
64
+ return count;
65
+ },
66
+ },
67
+ },
68
+ });
69
+
70
+ const uid = 'model-uid-cache';
71
+ const pm = parent.createModel<CounterModel>({ use: CounterModel, uid });
72
+ const cm = child.createModel<CounterModel>({ use: CounterModel, uid });
73
+
74
+ await pm.dispatchEvent('beforeRender');
75
+ expect(count).toBe(1);
76
+
77
+ // If cache was shared, the next call would hit cache and not increment.
78
+ await cm.dispatchEvent('beforeRender');
79
+ expect(count).toBe(2);
80
+ });
81
+
82
+ it('stacks engines and repairs chain when unlinking a middle block-scoped engine', () => {
83
+ const root = new FlowEngine();
84
+ // 提供最小 api mock,满足 ctx.auth 相关 getter
85
+ const api = new SDKApiClient({ storageType: 'memory' });
86
+ api.auth.role = 'guest';
87
+ api.auth.locale = 'en-US';
88
+ api.auth.token = 't';
89
+ root.context.defineProperty('api', { value: api });
90
+ const c1 = createBlockScopedEngine(root);
91
+ const c2 = createBlockScopedEngine(root);
92
+ const c3 = createBlockScopedEngine(root);
93
+
94
+ // chain root -> c1 -> c2 -> c3
95
+ expect(root.nextEngine).toBeTruthy();
96
+ expect(root.nextEngine?.nextEngine).toBeTruthy();
97
+ expect(root.nextEngine?.nextEngine?.nextEngine).toBeTruthy();
98
+
99
+ // unlink middle (c2) should link c1 <-> c3 and keep the tail intact
100
+ c2.unlinkFromStack();
101
+
102
+ // root.nextEngine 指向的是被代理的本地 FlowEngine 实例,不与 c1 的 Proxy 引用相等
103
+ // 这里通过前后指针关系来校验链修复是否正确
104
+ const first = root.nextEngine;
105
+ const second = first?.nextEngine;
106
+ expect(first?.previousEngine).toBe(root);
107
+ expect(second?.previousEngine).toBe(first);
108
+ expect(second?.nextEngine).toBeUndefined();
109
+ });
110
+
111
+ it('global model lookup traverses from top to root across stack', () => {
112
+ const root = new FlowEngine();
113
+ // 提供最小 api mock,满足 ctx.auth 相关 getter
114
+ const api = new SDKApiClient({ storageType: 'memory' });
115
+ api.auth.role = 'guest';
116
+ api.auth.locale = 'en-US';
117
+ api.auth.token = 't';
118
+ root.context.defineProperty('api', { value: api });
119
+ const c1 = createBlockScopedEngine(root);
120
+ const c2 = createBlockScopedEngine(root);
121
+
122
+ class TM extends FlowModel {}
123
+ const uid = 'same-uid-global-lookup';
124
+ const mRoot = root.createModel<TM>({ use: TM, uid });
125
+ const m1 = c1.createModel<TM>({ use: TM, uid });
126
+ const m2 = c2.createModel<TM>({ use: TM, uid });
127
+
128
+ // sanity: each scope returns its own when not global
129
+ expect(root.getModel(uid)).toBe(mRoot);
130
+ expect(c1.getModel(uid)).toBe(m1);
131
+ expect(c2.getModel(uid)).toBe(m2);
132
+
133
+ // global search from any engine hits the top-most engine's instance first
134
+ expect(root.getModel(uid, true)).toBe(m2);
135
+ expect(c1.getModel(uid, true)).toBe(m2);
136
+ expect(c2.getModel(uid, true)).toBe(m2);
137
+ });
138
+
139
+ it('delegates saveModel to parent (concurrency gate sharing)', async () => {
140
+ const parent = new FlowEngine();
141
+ // 提供最小 api mock,满足 ctx.auth 相关 getter
142
+ const api = new SDKApiClient({ storageType: 'memory' });
143
+ api.auth.role = 'guest';
144
+ api.auth.locale = 'en-US';
145
+ api.auth.token = 't';
146
+ parent.context.defineProperty('api', { value: api });
147
+ const child = createBlockScopedEngine(parent);
148
+ const spy = vi.spyOn(parent, 'saveModel').mockResolvedValueOnce(true);
149
+ class T extends FlowModel {}
150
+ const m = child.createModel<T>({ use: T });
151
+ await child.saveModel(m);
152
+ expect(spy).toHaveBeenCalledOnce();
153
+ });
154
+ });
@@ -0,0 +1,132 @@
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 } from 'vitest';
11
+ import { FlowContext } from '../flowContext';
12
+ import { FlowEngine } from '../flowEngine';
13
+ import type { FlowView } from '../views/FlowView';
14
+ import { createPopupMeta } from '../views/createViewMeta';
15
+
16
+ describe('createViewMeta - popup variables', () => {
17
+ function makeCtx() {
18
+ const engine = new FlowEngine();
19
+ const ctx = new FlowContext();
20
+ ctx.defineProperty('engine', { value: engine });
21
+ // 简化 i18n:直接返回 key
22
+ ctx.defineProperty('t', { value: (s: string) => s });
23
+ return { engine, ctx };
24
+ }
25
+
26
+ it('buildVariablesParams(record) uses anchor view instead of ctx.view', async () => {
27
+ const { engine, ctx } = makeCtx();
28
+
29
+ // 模拟锚定弹窗视图(被测试目标)
30
+ const anchorView: FlowView = {
31
+ type: 'embed',
32
+ inputArgs: {},
33
+ Header: null,
34
+ Footer: null,
35
+ close: () => void 0,
36
+ update: () => void 0,
37
+ navigation: {
38
+ viewStack: [
39
+ {
40
+ viewUid: 'popup-uid',
41
+ filterByTk: 111,
42
+ sourceId: 42,
43
+ },
44
+ ],
45
+ } as any,
46
+ } as any;
47
+
48
+ // ctx.view 指向另一个(例如设置弹窗)
49
+ ctx.defineProperty('view', {
50
+ value: {
51
+ type: 'dialog',
52
+ inputArgs: {},
53
+ navigation: {
54
+ viewStack: [
55
+ {
56
+ viewUid: 'settings-uid',
57
+ filterByTk: 9999,
58
+ },
59
+ ],
60
+ },
61
+ },
62
+ });
63
+
64
+ // 为两个视图 uid 注入不同的 openView 配置,确保只取锚定视图
65
+ const popupModel = {
66
+ getStepParams: vi.fn((_fk: string, sk: string) =>
67
+ sk === 'openView'
68
+ ? {
69
+ collectionName: 'posts',
70
+ dataSourceKey: 'main',
71
+ }
72
+ : undefined,
73
+ ),
74
+ } as any;
75
+ const settingsModel = {
76
+ getStepParams: vi.fn((_fk: string, sk: string) =>
77
+ sk === 'openView'
78
+ ? {
79
+ collectionName: 'comments',
80
+ dataSourceKey: 'main',
81
+ }
82
+ : undefined,
83
+ ),
84
+ } as any;
85
+
86
+ const getModelSpy = vi.spyOn(engine as any, 'getModel');
87
+ getModelSpy.mockImplementation((uid: string) => (uid === 'popup-uid' ? popupModel : settingsModel));
88
+
89
+ const metaFactory = createPopupMeta(ctx, anchorView);
90
+ const meta = (await metaFactory())!;
91
+
92
+ // record 应来自锚定视图的 openView 配置
93
+ const vars = (await meta.buildVariablesParams!(ctx)) as any;
94
+ expect(vars).toBeTruthy();
95
+ expect(vars.record).toEqual({ collection: 'posts', dataSourceKey: 'main', filterByTk: 111 });
96
+
97
+ // 确认没有误用 ctx.view(settings-uid)的集合
98
+ expect(vars.record.collection).not.toBe('comments');
99
+ });
100
+
101
+ it('properties() provides a record factory node (lazy) with title', async () => {
102
+ const { engine, ctx } = makeCtx();
103
+ // 只要能通过 anchorView 推断到集合名和主键即可;集合详情在懒加载时再取
104
+ const anchorView: FlowView = {
105
+ type: 'embed',
106
+ inputArgs: {},
107
+ Header: null,
108
+ Footer: null,
109
+ close: () => void 0,
110
+ update: () => void 0,
111
+ navigation: { viewStack: [{ viewUid: 'popup-uid', filterByTk: 1 }] } as any,
112
+ } as any;
113
+ const popupModel = {
114
+ getStepParams: vi.fn((_fk: string, sk: string) =>
115
+ sk === 'openView'
116
+ ? {
117
+ collectionName: 'tasks',
118
+ dataSourceKey: 'main',
119
+ }
120
+ : undefined,
121
+ ),
122
+ } as any;
123
+ vi.spyOn(engine as any, 'getModel').mockImplementation(() => popupModel);
124
+
125
+ const meta = (await createPopupMeta(ctx, anchorView)())!;
126
+ const props = typeof meta.properties === 'function' ? await (meta.properties as any)() : meta.properties || {};
127
+ // 断言存在 record 节点工厂,且有标题(懒加载,不触发集合访问)
128
+ expect(typeof props.record).toBe('function');
129
+ expect((props.record as any).title).toBe('Current popup record');
130
+ expect((props.record as any).hasChildren).toBe(true);
131
+ });
132
+ });
@@ -98,6 +98,9 @@ describe('FlowEngine', () => {
98
98
  return true;
99
99
  }
100
100
  async move() {}
101
+ async duplicate() {
102
+ return null;
103
+ }
101
104
  }
102
105
 
103
106
  let repo: MockFlowModelRepository;
@@ -1282,6 +1282,58 @@ describe('FlowContext resolveOnServer selective server resolution', () => {
1282
1282
  // server was called
1283
1283
  expect(api.request).toHaveBeenCalledTimes(1);
1284
1284
  });
1285
+
1286
+ it('skips server call when all vars require contextParams but none provided', async () => {
1287
+ const engine = new FlowEngine();
1288
+ const api = { request: vi.fn() } as any;
1289
+ engine.context.defineProperty('api', { value: api });
1290
+
1291
+ engine.context.defineProperty('foo', {
1292
+ value: { a: 1 },
1293
+ resolveOnServer: true,
1294
+ serverOnlyWhenContextParams: true,
1295
+ meta: async () => ({
1296
+ type: 'object',
1297
+ title: 'Foo',
1298
+ // no buildVariablesParams -> empty contextParams
1299
+ }),
1300
+ });
1301
+
1302
+ const tpl = { a: '{{ ctx.foo.a }}' } as any;
1303
+ const out = await (engine.context as any).resolveJsonTemplate(tpl);
1304
+ expect(out).toEqual({ a: 1 });
1305
+ // skipped server, because all server vars (only foo) require contextParams but none present
1306
+ expect(api.request).not.toHaveBeenCalled();
1307
+ });
1308
+
1309
+ it('still calls server when at least one var has contextParams (even if others require contextParams but none)', async () => {
1310
+ const engine = new FlowEngine();
1311
+ const api = { request: vi.fn(async () => ({ data: { ok: true } })) } as any;
1312
+ engine.context.defineProperty('api', { value: api });
1313
+
1314
+ // foo: requires contextParams but none will be provided
1315
+ engine.context.defineProperty('foo', {
1316
+ value: { a: 1 },
1317
+ resolveOnServer: true,
1318
+ serverOnlyWhenContextParams: true,
1319
+ meta: async () => ({ type: 'object', title: 'Foo' }),
1320
+ });
1321
+
1322
+ // user: provides contextParams via builder
1323
+ engine.context.defineProperty('user', {
1324
+ value: { id: 9 },
1325
+ resolveOnServer: true,
1326
+ meta: async () => ({
1327
+ type: 'object',
1328
+ title: 'User',
1329
+ buildVariablesParams: () => ({ collection: 'users', filterByTk: 9, dataSourceKey: 'main' }),
1330
+ }),
1331
+ });
1332
+
1333
+ const tpl = { a: '{{ ctx.foo.a }}', u: '{{ ctx.user.id }}' } as any;
1334
+ await (engine.context as any).resolveJsonTemplate(tpl);
1335
+ expect(api.request).toHaveBeenCalledTimes(1);
1336
+ });
1285
1337
  });
1286
1338
 
1287
1339
  describe('FlowContext.getPropertyOptions()', () => {
@@ -0,0 +1,163 @@
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, beforeAll } from 'vitest';
11
+ import { FlowEngineContext } from '../flowContext';
12
+ import { FlowEngine } from '../flowEngine';
13
+ import { JSRunner } from '../JSRunner';
14
+
15
+ describe('FlowContext async createJSRunner', () => {
16
+ let engine: FlowEngine;
17
+ let engineCtx: FlowEngineContext;
18
+
19
+ beforeAll(() => {
20
+ // Create minimal engine and context for testing
21
+ engine = new FlowEngine();
22
+ engineCtx = new FlowEngineContext(engine);
23
+ });
24
+
25
+ describe('createJSRunner method', () => {
26
+ it('should be async and return JSRunner instance', async () => {
27
+ const runner = await engineCtx.createJSRunner();
28
+ expect(runner).toBeInstanceOf(JSRunner);
29
+ });
30
+
31
+ it('should pass globals to JSRunner', async () => {
32
+ const customGlobals = { customVar: 'test' };
33
+ const runner = await engineCtx.createJSRunner({ globals: customGlobals });
34
+ const result = await runner.run('return customVar');
35
+ expect(result?.success).toBe(true);
36
+ expect(result?.value).toBe('test');
37
+ });
38
+
39
+ it('should always provide ctx in globals', async () => {
40
+ const runner = await engineCtx.createJSRunner();
41
+ const result = await runner.run('return typeof ctx !== "undefined"');
42
+ expect(result?.success).toBe(true);
43
+ expect(result?.value).toBe(true);
44
+ });
45
+
46
+ it('should support timeout option', async () => {
47
+ const runner = await engineCtx.createJSRunner({ timeoutMs: 100 });
48
+ expect(runner).toBeInstanceOf(JSRunner);
49
+ });
50
+
51
+ it('should select appropriate context based on model class', async () => {
52
+ // Test with JSBlockModel
53
+ const ctx = new FlowEngineContext(engine);
54
+ (ctx as any).defineProperty('model', {
55
+ value: { constructor: { name: 'JSBlockModel' } },
56
+ });
57
+
58
+ const runner = await ctx.createJSRunner();
59
+ expect(runner).toBeInstanceOf(JSRunner);
60
+
61
+ // Check if ctx.element is accessible (specific to JSBlockModel)
62
+ const result = await runner.run('return typeof ctx !== "undefined"');
63
+ expect(result?.success).toBe(true);
64
+ });
65
+
66
+ it('should select JSFieldModel context correctly', async () => {
67
+ const ctx = new FlowEngineContext(engine);
68
+ (ctx as any).defineProperty('model', {
69
+ value: { constructor: { name: 'JSFieldModel' } },
70
+ });
71
+
72
+ const runner = await ctx.createJSRunner();
73
+ expect(runner).toBeInstanceOf(JSRunner);
74
+ });
75
+
76
+ it('should select JSColumnModel context correctly', async () => {
77
+ const ctx = new FlowEngineContext(engine);
78
+ (ctx as any).defineProperty('model', {
79
+ value: { constructor: { name: 'JSColumnModel' } },
80
+ });
81
+
82
+ const runner = await ctx.createJSRunner();
83
+ expect(runner).toBeInstanceOf(JSRunner);
84
+ });
85
+
86
+ it('should fallback to base context for unknown model', async () => {
87
+ const ctx = new FlowEngineContext(engine);
88
+ (ctx as any).defineProperty('model', {
89
+ value: { constructor: { name: 'UnknownModel' } },
90
+ });
91
+
92
+ const runner = await ctx.createJSRunner();
93
+ expect(runner).toBeInstanceOf(JSRunner);
94
+
95
+ // Should still have ctx
96
+ const result = await runner.run('return typeof ctx !== "undefined"');
97
+ expect(result?.success).toBe(true);
98
+ expect(result?.value).toBe(true);
99
+ });
100
+
101
+ it('should support version option', async () => {
102
+ const runner = await engineCtx.createJSRunner({ version: 'v1' } as any);
103
+ expect(runner).toBeInstanceOf(JSRunner);
104
+ });
105
+
106
+ it('should merge custom globals with ctx', async () => {
107
+ const runner = await engineCtx.createJSRunner({
108
+ globals: { foo: 'bar', baz: 123 },
109
+ });
110
+
111
+ const result = await runner.run('return { hasCtx: typeof ctx !== "undefined", foo, baz }');
112
+ expect(result?.success).toBe(true);
113
+ expect(result?.value).toEqual({ hasCtx: true, foo: 'bar', baz: 123 });
114
+ });
115
+
116
+ it('should execute async code successfully', async () => {
117
+ const runner = await engineCtx.createJSRunner();
118
+ const result = await runner.run(`
119
+ return new Promise(resolve => {
120
+ setTimeout(() => resolve('async result'), 10);
121
+ });
122
+ `);
123
+ expect(result?.success).toBe(true);
124
+ expect(result?.value).toBe('async result');
125
+ });
126
+
127
+ it('should handle errors gracefully', async () => {
128
+ const runner = await engineCtx.createJSRunner();
129
+ const result = await runner.run('throw new Error("test error")');
130
+ expect(result?.success).toBe(false);
131
+ expect(result?.error).toBeTruthy();
132
+ });
133
+
134
+ it('should respect timeout setting', async () => {
135
+ const runner = await engineCtx.createJSRunner({ timeoutMs: 50 });
136
+ const result = await runner.run(`
137
+ return new Promise(resolve => {
138
+ setTimeout(() => resolve('done'), 1000);
139
+ });
140
+ `);
141
+ // Should timeout before completing
142
+ expect(result?.success).toBe(false);
143
+ });
144
+ });
145
+
146
+ describe('setupRunJSContexts integration', () => {
147
+ it('should auto-setup contexts when createJSRunner is called', async () => {
148
+ // Even if setup wasn't called manually, createJSRunner should trigger it
149
+ const ctx = new FlowEngineContext(engine);
150
+ (ctx as any).defineProperty('model', {
151
+ value: { constructor: { name: 'JSBlockModel' } },
152
+ });
153
+
154
+ const runner = await ctx.createJSRunner();
155
+ expect(runner).toBeInstanceOf(JSRunner);
156
+
157
+ // Verify context is properly initialized
158
+ const result = await runner.run('return typeof ctx !== "undefined"');
159
+ expect(result?.success).toBe(true);
160
+ expect(result?.value).toBe(true);
161
+ });
162
+ });
163
+ });
@@ -47,6 +47,10 @@ class MockFlowModelRepository implements IFlowModelRepository {
47
47
  async move(sourceId: string, targetId: string, position: 'before' | 'after'): Promise<void> {
48
48
  // Mock implementation
49
49
  }
50
+
51
+ async duplicate(uid: string): Promise<Record<string, any> | null> {
52
+ return null;
53
+ }
50
54
  }
51
55
 
52
56
  describe('FlowEngine.saveModel concurrent save prevention', () => {