@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,464 @@
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 { FlowContext } from '../flowContext';
12
+ import { FlowEngine } from '../flowEngine';
13
+ import {
14
+ createAssociationAwareObjectMetaFactory,
15
+ createAssociationSubpathResolver,
16
+ } from '../utils/associationObjectVariable';
17
+
18
+ function setupEngineWithCollections() {
19
+ const engine = new FlowEngine();
20
+ const ds = engine.context.dataSourceManager.getDataSource('main');
21
+
22
+ // 真实集合定义,确保 targetCollection / filterTargetKey 等行为一致
23
+ ds.addCollection({
24
+ name: 'users',
25
+ filterTargetKey: 'id',
26
+ fields: [
27
+ { name: 'id', type: 'integer', interface: 'number' },
28
+ { name: 'name', type: 'string', interface: 'text' },
29
+ ],
30
+ });
31
+ ds.addCollection({
32
+ name: 'tags',
33
+ filterTargetKey: 'id',
34
+ fields: [
35
+ { name: 'id', type: 'integer', interface: 'number' },
36
+ { name: 'name', type: 'string', interface: 'text' },
37
+ ],
38
+ });
39
+ ds.addCollection({
40
+ name: 'posts',
41
+ filterTargetKey: 'id',
42
+ fields: [
43
+ { name: 'title', type: 'string', interface: 'text' },
44
+ { name: 'author', type: 'belongsTo', target: 'users', interface: 'm2o' },
45
+ { name: 'tags', type: 'belongsToMany', target: 'tags', interface: 'm2m' },
46
+ ],
47
+ });
48
+
49
+ return { engine, ds, collection: ds.getCollection('posts') } as const;
50
+ }
51
+
52
+ describe('objectVariable utilities', () => {
53
+ it('createAssociationSubpathResolver should detect association subpaths', () => {
54
+ const { collection } = setupEngineWithCollections();
55
+ const resolver = createAssociationSubpathResolver(() => collection);
56
+ expect(resolver('author.name')).toBe(true);
57
+ expect(resolver('tags[0].name')).toBe(true);
58
+ expect(resolver('title')).toBe(false);
59
+ expect(resolver('title.length')).toBe(false);
60
+ expect(resolver('unknown.name')).toBe(false);
61
+ });
62
+
63
+ it('createAssociationAwareObjectMetaFactory should build params for toOne/toMany', async () => {
64
+ const { collection } = setupEngineWithCollections();
65
+
66
+ const obj1 = { title: 'hello', author: 1 };
67
+ const metaFactory1 = createAssociationAwareObjectMetaFactory(
68
+ () => collection,
69
+ 'Current object',
70
+ () => obj1,
71
+ );
72
+ const meta1 = await metaFactory1();
73
+ const ctx1 = new FlowContext();
74
+ const params1 = await (meta1 as any).buildVariablesParams(ctx1);
75
+ expect(params1).toEqual({
76
+ author: { collection: 'users', dataSourceKey: 'main', filterByTk: 1 },
77
+ });
78
+
79
+ const obj2 = { author: { id: 2 }, tags: [{ id: 11 }, { id: 12 }] };
80
+ const metaFactory2 = createAssociationAwareObjectMetaFactory(
81
+ () => collection,
82
+ 'Current object',
83
+ () => obj2,
84
+ );
85
+ const meta2 = await metaFactory2();
86
+ const ctx2 = new FlowContext();
87
+ const params2 = await (meta2 as any).buildVariablesParams(ctx2);
88
+ expect(params2).toEqual({
89
+ author: { collection: 'users', dataSourceKey: 'main', filterByTk: 2 },
90
+ tags: { collection: 'tags', dataSourceKey: 'main', filterByTk: [11, 12] },
91
+ });
92
+ });
93
+
94
+ it('integrates with FlowContext.resolveJsonTemplate to call variables:resolve with flattened contextParams', async () => {
95
+ const { engine, collection } = setupEngineWithCollections();
96
+ const obj = { author: 1 };
97
+ const ctx = engine.context as any;
98
+
99
+ // Provide API stub to intercept variables:resolve
100
+ const calls: any[] = [];
101
+ (ctx as any).api = {
102
+ request: vi.fn(async ({ url, data, method }) => {
103
+ calls.push({ url, data, method });
104
+ const batch = (data?.values?.batch as any[]) || [];
105
+ const results = batch.map((it) => ({ id: it.id, data: it.template }));
106
+ return { data: { data: { results } } };
107
+ }),
108
+ };
109
+
110
+ // Define object-like variable
111
+ const metaFactory = createAssociationAwareObjectMetaFactory(
112
+ () => collection,
113
+ 'Current object',
114
+ () => obj,
115
+ );
116
+ ctx.defineProperty('obj', {
117
+ get: () => obj,
118
+ cache: false,
119
+ meta: metaFactory,
120
+ resolveOnServer: createAssociationSubpathResolver(() => collection),
121
+ serverOnlyWhenContextParams: true,
122
+ });
123
+
124
+ const template = { x: '{{ ctx.obj.author.name }}' } as any;
125
+ await (ctx as any).resolveJsonTemplate(template);
126
+
127
+ // Assert variables:resolve was called with proper flattened contextParams
128
+ expect((ctx as any).api.request).toHaveBeenCalled();
129
+ const call = calls.find((c) => c.url === 'variables:resolve');
130
+ expect(call).toBeTruthy();
131
+ const batch0 = call.data?.values?.batch?.[0];
132
+ expect(batch0?.contextParams).toBeTruthy();
133
+ // Flattened key should be 'obj.author'
134
+ const cp = batch0.contextParams as Record<string, any>;
135
+ const keys = Object.keys(cp || {});
136
+ expect(keys).toContain('obj.author');
137
+ expect(cp['obj.author']).toMatchObject({ collection: 'users', filterByTk: 1 });
138
+ });
139
+
140
+ it('does not call server when only non-association fields are referenced', async () => {
141
+ const { engine, collection } = setupEngineWithCollections();
142
+ const obj = { title: 'hello', author: 1 };
143
+ const ctx = engine.context as any;
144
+
145
+ (ctx as any).api = { request: vi.fn() };
146
+
147
+ const metaFactory = createAssociationAwareObjectMetaFactory(
148
+ () => collection,
149
+ 'Current object',
150
+ () => obj,
151
+ );
152
+ ctx.defineProperty('obj', {
153
+ get: () => obj,
154
+ cache: false,
155
+ meta: metaFactory,
156
+ resolveOnServer: createAssociationSubpathResolver(() => collection),
157
+ serverOnlyWhenContextParams: true,
158
+ });
159
+
160
+ const template = { x: '{{ ctx.obj.title }}' } as any;
161
+ const resolved = await (ctx as any).resolveJsonTemplate(template);
162
+ expect(resolved).toEqual({ x: 'hello' });
163
+ expect((ctx as any).api.request).not.toHaveBeenCalled();
164
+ });
165
+
166
+ it('buildVariablesParams merges ids and objects in toMany and filters falsy', async () => {
167
+ const { collection } = setupEngineWithCollections();
168
+ const obj = { tags: [11, { id: 12 }, {}, null, undefined, { id: null }] };
169
+ const mf = createAssociationAwareObjectMetaFactory(
170
+ () => collection,
171
+ 'Current object',
172
+ () => obj,
173
+ );
174
+ const meta = await mf();
175
+ const ctx = new FlowContext();
176
+ const params = await (meta as any).buildVariablesParams(ctx);
177
+ expect(params).toMatchObject({ tags: { collection: 'tags', filterByTk: [11, 12] } });
178
+ });
179
+
180
+ it('supports custom primary key via filterTargetKey', async () => {
181
+ const engine = new FlowEngine();
182
+ const ds = engine.context.dataSourceManager.getDataSource('main');
183
+ ds.addCollection({
184
+ name: 'profiles',
185
+ filterTargetKey: 'uuid',
186
+ fields: [
187
+ { name: 'uuid', type: 'string', interface: 'text' },
188
+ { name: 'name', type: 'string', interface: 'text' },
189
+ ],
190
+ });
191
+ ds.addCollection({
192
+ name: 'articles',
193
+ filterTargetKey: 'id',
194
+ fields: [
195
+ { name: 'id', type: 'integer', interface: 'number' },
196
+ { name: 'editor', type: 'belongsTo', target: 'profiles', interface: 'm2o' },
197
+ ],
198
+ });
199
+ const collection = ds.getCollection('articles');
200
+ const obj = { editor: { uuid: 'p-1' } };
201
+ const mf = createAssociationAwareObjectMetaFactory(
202
+ () => collection,
203
+ 'Current object',
204
+ () => obj,
205
+ );
206
+ const meta = await mf();
207
+ const ctx = new FlowContext();
208
+ const params = await (meta as any).buildVariablesParams(ctx);
209
+ expect(params).toMatchObject({ editor: { collection: 'profiles', filterByTk: 'p-1' } });
210
+ });
211
+
212
+ it('supports composite primary key via filterTargetKey array (toOne/toMany)', async () => {
213
+ const engine = new FlowEngine();
214
+ const ds = engine.context.dataSourceManager.getDataSource('main');
215
+ ds.addCollection({
216
+ name: 'composites',
217
+ filterTargetKey: ['country', 'code'],
218
+ fields: [
219
+ { name: 'country', type: 'string', interface: 'text' },
220
+ { name: 'code', type: 'string', interface: 'text' },
221
+ { name: 'name', type: 'string', interface: 'text' },
222
+ ],
223
+ });
224
+ ds.addCollection({
225
+ name: 'items',
226
+ filterTargetKey: 'id',
227
+ fields: [
228
+ { name: 'id', type: 'integer', interface: 'number' },
229
+ { name: 'ref', type: 'belongsTo', target: 'composites', interface: 'm2o' },
230
+ { name: 'refs', type: 'belongsToMany', target: 'composites', interface: 'm2m' },
231
+ ],
232
+ });
233
+ const collection = ds.getCollection('items');
234
+
235
+ // toOne
236
+ const obj1 = { ref: { country: 'US', code: '001', name: 'United States' } };
237
+ const mf1 = createAssociationAwareObjectMetaFactory(
238
+ () => collection,
239
+ 'Current object',
240
+ () => obj1,
241
+ );
242
+ const meta1 = await mf1();
243
+ const ctx1 = new FlowContext();
244
+ const params1 = await (meta1 as any).buildVariablesParams(ctx1);
245
+ expect(params1).toMatchObject({
246
+ ref: { collection: 'composites', filterByTk: { country: 'US', code: '001' } },
247
+ });
248
+
249
+ // toMany with mixed valid/invalid entries
250
+ const obj2 = {
251
+ refs: [
252
+ { country: 'CN', code: '086' },
253
+ { country: 'JP' }, // missing code => ignored
254
+ 'raw', // unsupported raw value for composite => ignored
255
+ ],
256
+ };
257
+ const mf2 = createAssociationAwareObjectMetaFactory(
258
+ () => collection,
259
+ 'Current object',
260
+ () => obj2,
261
+ );
262
+ const meta2 = await mf2();
263
+ const ctx2 = new FlowContext();
264
+ const params2 = await (meta2 as any).buildVariablesParams(ctx2);
265
+ expect(params2).toMatchObject({
266
+ refs: { collection: 'composites', filterByTk: [{ country: 'CN', code: '086' }] },
267
+ });
268
+ });
269
+
270
+ it('ignores incomplete composite key values when building params', async () => {
271
+ const engine = new FlowEngine();
272
+ const ds = engine.context.dataSourceManager.getDataSource('main');
273
+ ds.addCollection({
274
+ name: 'dept',
275
+ filterTargetKey: ['org', 'id'],
276
+ fields: [
277
+ { name: 'org', type: 'string', interface: 'text' },
278
+ { name: 'id', type: 'string', interface: 'text' },
279
+ ],
280
+ });
281
+ ds.addCollection({
282
+ name: 'employees',
283
+ filterTargetKey: 'id',
284
+ fields: [
285
+ { name: 'id', type: 'integer', interface: 'number' },
286
+ { name: 'department', type: 'belongsTo', target: 'dept', interface: 'm2o' },
287
+ ],
288
+ });
289
+ const collection = ds.getCollection('employees');
290
+ const obj = { department: { org: 'HQ' } }; // missing id
291
+ const mf = createAssociationAwareObjectMetaFactory(
292
+ () => collection,
293
+ 'Current object',
294
+ () => obj,
295
+ );
296
+ const meta = await mf();
297
+ const ctx = new FlowContext();
298
+ const params = await (meta as any).buildVariablesParams(ctx);
299
+ expect(params).toEqual({});
300
+ });
301
+
302
+ it('contextParams only includes used association subpaths (filters out unused)', async () => {
303
+ const { engine, collection } = setupEngineWithCollections();
304
+ const obj = { author: 1, tags: [11, 12] };
305
+ const ctx = engine.context as any;
306
+ const calls: any[] = [];
307
+ (ctx as any).api = {
308
+ request: vi.fn(async ({ url, data, method }) => {
309
+ calls.push({ url, data, method });
310
+ const batch = (data?.values?.batch as any[]) || [];
311
+ const results = batch.map((it) => ({ id: it.id, data: it.template }));
312
+ return { data: { data: { results } } };
313
+ }),
314
+ };
315
+
316
+ const metaFactory = createAssociationAwareObjectMetaFactory(
317
+ () => collection,
318
+ 'Current object',
319
+ () => obj,
320
+ );
321
+ ctx.defineProperty('obj', {
322
+ get: () => obj,
323
+ cache: false,
324
+ meta: metaFactory,
325
+ resolveOnServer: createAssociationSubpathResolver(() => collection),
326
+ serverOnlyWhenContextParams: true,
327
+ });
328
+
329
+ // 仅引用 author 子属性,不使用 tags
330
+ const template = { a: '{{ ctx.obj.author.name }}' } as any;
331
+ await (ctx as any).resolveJsonTemplate(template);
332
+
333
+ const call = calls.find((c) => c.url === 'variables:resolve');
334
+ expect(call).toBeTruthy();
335
+ const cp = call.data?.values?.batch?.[0]?.contextParams as Record<string, any>;
336
+ const keys = Object.keys(cp || {});
337
+ expect(keys).toContain('obj.author');
338
+ expect(keys).not.toContain('obj.tags');
339
+ });
340
+
341
+ it('bracket without dot does not call server (tags[0])', async () => {
342
+ const { engine, collection } = setupEngineWithCollections();
343
+ const obj = { tags: [11, 12] };
344
+ const ctx = engine.context as any;
345
+ (ctx as any).api = { request: vi.fn() };
346
+
347
+ const metaFactory = createAssociationAwareObjectMetaFactory(
348
+ () => collection,
349
+ 'Current object',
350
+ () => obj,
351
+ );
352
+ ctx.defineProperty('obj', {
353
+ get: () => obj,
354
+ cache: false,
355
+ meta: metaFactory,
356
+ resolveOnServer: createAssociationSubpathResolver(() => collection),
357
+ serverOnlyWhenContextParams: true,
358
+ });
359
+
360
+ const template = { t0: '{{ ctx.obj.tags[0] }}' } as any;
361
+ const resolved = await (ctx as any).resolveJsonTemplate(template);
362
+ expect(resolved).toEqual({ t0: 11 });
363
+ expect((ctx as any).api.request).not.toHaveBeenCalled();
364
+ });
365
+
366
+ it('no association values => buildVariablesParams returns empty object', async () => {
367
+ const { collection } = setupEngineWithCollections();
368
+ const obj = { title: 'hello' };
369
+ const mf = createAssociationAwareObjectMetaFactory(
370
+ () => collection,
371
+ 'Current object',
372
+ () => obj,
373
+ );
374
+ const meta = await mf();
375
+ const ctx = new FlowContext();
376
+ const params = await (meta as any).buildVariablesParams(ctx);
377
+ expect(params).toEqual({});
378
+ });
379
+
380
+ it('top-level association value (no dot) does not call server', async () => {
381
+ const { engine, collection } = setupEngineWithCollections();
382
+ const obj = { author: 7 };
383
+ const ctx = engine.context as any;
384
+ (ctx as any).api = { request: vi.fn() };
385
+
386
+ const metaFactory = createAssociationAwareObjectMetaFactory(
387
+ () => collection,
388
+ 'Current object',
389
+ () => obj,
390
+ );
391
+ ctx.defineProperty('obj', {
392
+ get: () => obj,
393
+ cache: false,
394
+ meta: metaFactory,
395
+ resolveOnServer: createAssociationSubpathResolver(() => collection),
396
+ });
397
+
398
+ const template = { a: '{{ ctx.obj.author }}' } as any;
399
+ const resolved = await (ctx as any).resolveJsonTemplate(template);
400
+ expect(resolved).toEqual({ a: 7 });
401
+ expect((ctx as any).api.request).not.toHaveBeenCalled();
402
+ });
403
+
404
+ it('local-first: toOne association subpath uses local value and skips server', async () => {
405
+ const { engine, collection } = setupEngineWithCollections();
406
+ const obj = { author: { id: 7, name: 'LocalName' } };
407
+ const ctx = engine.context as any;
408
+
409
+ (ctx as any).api = { request: vi.fn() };
410
+
411
+ const metaFactory = createAssociationAwareObjectMetaFactory(
412
+ () => collection,
413
+ 'Current object',
414
+ () => obj,
415
+ );
416
+
417
+ ctx.defineProperty('obj', {
418
+ get: () => obj,
419
+ cache: false,
420
+ meta: metaFactory,
421
+ // 启用“本地优先”:传入 valueAccessor,以便当本地已有该子路径时跳过服务端
422
+ resolveOnServer: createAssociationSubpathResolver(
423
+ () => collection,
424
+ () => obj,
425
+ ),
426
+ serverOnlyWhenContextParams: true,
427
+ });
428
+
429
+ const template = { x: '{{ ctx.obj.author.name }}' } as any;
430
+ const resolved = await (ctx as any).resolveJsonTemplate(template);
431
+ expect(resolved).toEqual({ x: 'LocalName' });
432
+ expect((ctx as any).api.request).not.toHaveBeenCalled();
433
+ });
434
+
435
+ it('local-first: toMany association subpath (index + dot) uses local value and skips server', async () => {
436
+ const { engine, collection } = setupEngineWithCollections();
437
+ const obj = { tags: [{ id: 11, name: 'T1' }] };
438
+ const ctx = engine.context as any;
439
+
440
+ (ctx as any).api = { request: vi.fn() };
441
+
442
+ const metaFactory = createAssociationAwareObjectMetaFactory(
443
+ () => collection,
444
+ 'Current object',
445
+ () => obj,
446
+ );
447
+
448
+ ctx.defineProperty('obj', {
449
+ get: () => obj,
450
+ cache: false,
451
+ meta: metaFactory,
452
+ resolveOnServer: createAssociationSubpathResolver(
453
+ () => collection,
454
+ () => obj,
455
+ ),
456
+ serverOnlyWhenContextParams: true,
457
+ });
458
+
459
+ const template = { x: '{{ ctx.obj.tags[0].name }}' } as any;
460
+ const resolved = await (ctx as any).resolveJsonTemplate(template);
461
+ expect(resolved).toEqual({ x: 'T1' });
462
+ expect((ctx as any).api.request).not.toHaveBeenCalled();
463
+ });
464
+ });