@nocobase/flow-engine 2.1.0-alpha.1 → 2.1.0-alpha.11

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 (312) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/BlockScopedFlowEngine.js +0 -1
  4. package/lib/FlowDefinition.d.ts +6 -0
  5. package/lib/FlowSchemaRegistry.d.ts +154 -0
  6. package/lib/FlowSchemaRegistry.js +1427 -0
  7. package/lib/JSRunner.d.ts +15 -0
  8. package/lib/JSRunner.js +82 -7
  9. package/lib/ViewScopedFlowEngine.js +8 -1
  10. package/lib/acl/Acl.js +13 -3
  11. package/lib/components/FlowContextSelector.js +155 -10
  12. package/lib/components/MobilePopup.js +6 -5
  13. package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
  14. package/lib/components/dnd/gridDragPlanner.js +59 -3
  15. package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
  16. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
  17. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
  18. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +21 -3
  19. package/lib/components/subModel/AddSubModelButton.js +27 -1
  20. package/lib/components/subModel/utils.js +2 -2
  21. package/lib/components/variables/VariableInput.js +9 -4
  22. package/lib/components/variables/VariableTag.js +46 -39
  23. package/lib/components/variables/utils.d.ts +7 -0
  24. package/lib/components/variables/utils.js +42 -2
  25. package/lib/data-source/index.d.ts +7 -27
  26. package/lib/data-source/index.js +84 -51
  27. package/lib/executor/FlowExecutor.d.ts +2 -1
  28. package/lib/executor/FlowExecutor.js +190 -26
  29. package/lib/flow-schema-registry/fieldBinding.d.ts +32 -0
  30. package/lib/flow-schema-registry/fieldBinding.js +165 -0
  31. package/lib/flow-schema-registry/modelPatches.d.ts +16 -0
  32. package/lib/flow-schema-registry/modelPatches.js +235 -0
  33. package/lib/flow-schema-registry/schemaInference.d.ts +17 -0
  34. package/lib/flow-schema-registry/schemaInference.js +207 -0
  35. package/lib/flow-schema-registry/utils.d.ts +25 -0
  36. package/lib/flow-schema-registry/utils.js +293 -0
  37. package/lib/flowContext.d.ts +230 -7
  38. package/lib/flowContext.js +2270 -148
  39. package/lib/flowEngine.d.ts +160 -1
  40. package/lib/flowEngine.js +387 -27
  41. package/lib/flowI18n.js +6 -4
  42. package/lib/flowSettings.d.ts +14 -6
  43. package/lib/flowSettings.js +51 -17
  44. package/lib/index.d.ts +8 -1
  45. package/lib/index.js +24 -1
  46. package/lib/lazy-helper.d.ts +14 -0
  47. package/lib/lazy-helper.js +71 -0
  48. package/lib/locale/en-US.json +9 -2
  49. package/lib/locale/index.d.ts +14 -0
  50. package/lib/locale/zh-CN.json +8 -1
  51. package/lib/models/CollectionFieldModel.d.ts +1 -0
  52. package/lib/models/CollectionFieldModel.js +3 -2
  53. package/lib/models/DisplayItemModel.d.ts +1 -1
  54. package/lib/models/EditableItemModel.d.ts +1 -1
  55. package/lib/models/FilterableItemModel.d.ts +1 -1
  56. package/lib/models/flowModel.d.ts +7 -0
  57. package/lib/models/flowModel.js +83 -8
  58. package/lib/provider.js +7 -6
  59. package/lib/resources/baseRecordResource.d.ts +5 -0
  60. package/lib/resources/baseRecordResource.js +24 -0
  61. package/lib/resources/multiRecordResource.d.ts +1 -0
  62. package/lib/resources/multiRecordResource.js +11 -4
  63. package/lib/resources/singleRecordResource.js +2 -0
  64. package/lib/resources/sqlResource.d.ts +4 -3
  65. package/lib/resources/sqlResource.js +8 -3
  66. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
  67. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
  68. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
  69. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
  70. package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
  71. package/lib/runjs-context/contexts/base.js +706 -41
  72. package/lib/runjs-context/contributions.d.ts +33 -0
  73. package/lib/runjs-context/contributions.js +88 -0
  74. package/lib/runjs-context/helpers.js +12 -1
  75. package/lib/runjs-context/registry.d.ts +1 -1
  76. package/lib/runjs-context/setup.js +23 -9
  77. package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
  78. package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
  79. package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
  80. package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
  81. package/lib/runjs-context/snippets/index.d.ts +11 -1
  82. package/lib/runjs-context/snippets/index.js +61 -40
  83. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
  84. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
  85. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
  86. package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
  87. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
  88. package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
  89. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
  90. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
  91. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
  92. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
  93. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
  94. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
  95. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
  96. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
  97. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
  98. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
  99. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
  100. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
  101. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
  102. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
  103. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
  104. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
  105. package/lib/runjsLibs.d.ts +28 -0
  106. package/lib/runjsLibs.js +532 -0
  107. package/lib/scheduler/ModelOperationScheduler.d.ts +7 -1
  108. package/lib/scheduler/ModelOperationScheduler.js +28 -23
  109. package/lib/server.d.ts +10 -0
  110. package/lib/server.js +32 -0
  111. package/lib/types.d.ts +296 -1
  112. package/lib/utils/associationObjectVariable.d.ts +2 -2
  113. package/lib/utils/createCollectionContextMeta.js +1 -0
  114. package/lib/utils/createEphemeralContext.js +2 -2
  115. package/lib/utils/dateVariable.d.ts +16 -0
  116. package/lib/utils/dateVariable.js +380 -0
  117. package/lib/utils/exceptions.d.ts +7 -0
  118. package/lib/utils/exceptions.js +10 -0
  119. package/lib/utils/index.d.ts +8 -3
  120. package/lib/utils/index.js +49 -0
  121. package/lib/utils/params-resolvers.js +16 -9
  122. package/lib/utils/parsePathnameToViewParams.js +1 -1
  123. package/lib/utils/resolveModuleUrl.d.ts +58 -0
  124. package/lib/utils/resolveModuleUrl.js +65 -0
  125. package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
  126. package/lib/utils/resolveRunJSObjectValues.js +61 -0
  127. package/lib/utils/runjsModuleLoader.d.ts +58 -0
  128. package/lib/utils/runjsModuleLoader.js +422 -0
  129. package/lib/utils/runjsTemplateCompat.d.ts +35 -0
  130. package/lib/utils/runjsTemplateCompat.js +743 -0
  131. package/lib/utils/runjsValue.d.ts +29 -0
  132. package/lib/utils/runjsValue.js +275 -0
  133. package/lib/utils/safeGlobals.d.ts +18 -8
  134. package/lib/utils/safeGlobals.js +164 -17
  135. package/lib/utils/schema-utils.d.ts +17 -1
  136. package/lib/utils/schema-utils.js +80 -0
  137. package/lib/views/FlowView.d.ts +7 -1
  138. package/lib/views/createViewMeta.d.ts +0 -7
  139. package/lib/views/createViewMeta.js +19 -70
  140. package/lib/views/index.d.ts +1 -2
  141. package/lib/views/index.js +4 -3
  142. package/lib/views/runViewBeforeClose.d.ts +10 -0
  143. package/lib/views/runViewBeforeClose.js +45 -0
  144. package/lib/views/useDialog.d.ts +2 -1
  145. package/lib/views/useDialog.js +28 -6
  146. package/lib/views/useDrawer.d.ts +2 -1
  147. package/lib/views/useDrawer.js +27 -5
  148. package/lib/views/usePage.d.ts +6 -1
  149. package/lib/views/usePage.js +53 -9
  150. package/lib/views/usePopover.js +4 -1
  151. package/lib/views/viewEvents.d.ts +17 -0
  152. package/lib/views/viewEvents.js +90 -0
  153. package/package.json +5 -5
  154. package/server.d.ts +1 -0
  155. package/server.js +1 -0
  156. package/src/BlockScopedFlowEngine.ts +2 -5
  157. package/src/FlowSchemaRegistry.ts +1799 -0
  158. package/src/JSRunner.ts +111 -5
  159. package/src/ViewScopedFlowEngine.ts +8 -0
  160. package/src/__tests__/FlowSchemaRegistry.test.ts +1951 -0
  161. package/src/__tests__/JSRunner.test.ts +91 -1
  162. package/src/__tests__/createViewMeta.popup.test.ts +62 -1
  163. package/src/__tests__/flow-engine.test.ts +48 -0
  164. package/src/__tests__/flowContext.test.ts +693 -1
  165. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
  166. package/src/__tests__/flowEngine.modelLoaders.test.ts +249 -0
  167. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  168. package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
  169. package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
  170. package/src/__tests__/flowRuntimeContext.test.ts +2 -1
  171. package/src/__tests__/flowSettings.open.test.tsx +123 -19
  172. package/src/__tests__/flowSettings.test.ts +94 -15
  173. package/src/__tests__/provider.test.tsx +0 -5
  174. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  175. package/src/__tests__/runjsContext.test.ts +26 -7
  176. package/src/__tests__/runjsContextImplementations.test.ts +34 -3
  177. package/src/__tests__/runjsContextRuntime.test.ts +5 -3
  178. package/src/__tests__/runjsContributions.test.ts +89 -0
  179. package/src/__tests__/runjsExternalLibs.test.ts +242 -0
  180. package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
  181. package/src/__tests__/runjsLocales.test.ts +4 -1
  182. package/src/__tests__/runjsPreprocessDefault.test.ts +72 -0
  183. package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
  184. package/src/__tests__/runjsSnippets.test.ts +40 -3
  185. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  186. package/src/acl/Acl.tsx +3 -3
  187. package/src/components/FlowContextSelector.tsx +208 -12
  188. package/src/components/MobilePopup.tsx +4 -2
  189. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
  190. package/src/components/__tests__/gridDragPlanner.test.ts +229 -1
  191. package/src/components/dnd/gridDragPlanner.ts +68 -2
  192. package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
  193. package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
  194. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +109 -16
  195. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
  196. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +31 -4
  197. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
  198. package/src/components/subModel/AddSubModelButton.tsx +32 -2
  199. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +143 -32
  200. package/src/components/subModel/utils.ts +1 -1
  201. package/src/components/variables/VariableInput.tsx +12 -4
  202. package/src/components/variables/VariableTag.tsx +54 -45
  203. package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
  204. package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
  205. package/src/components/variables/__tests__/utils.test.ts +81 -3
  206. package/src/components/variables/utils.ts +67 -6
  207. package/src/data-source/index.ts +88 -110
  208. package/src/executor/FlowExecutor.ts +230 -28
  209. package/src/executor/__tests__/flowExecutor.test.ts +123 -0
  210. package/src/flow-schema-registry/fieldBinding.ts +171 -0
  211. package/src/flow-schema-registry/modelPatches.ts +260 -0
  212. package/src/flow-schema-registry/schemaInference.ts +210 -0
  213. package/src/flow-schema-registry/utils.ts +268 -0
  214. package/src/flowContext.ts +2989 -212
  215. package/src/flowEngine.ts +434 -23
  216. package/src/flowI18n.ts +7 -5
  217. package/src/flowSettings.ts +58 -18
  218. package/src/index.ts +15 -1
  219. package/src/lazy-helper.tsx +57 -0
  220. package/src/locale/en-US.json +9 -2
  221. package/src/locale/zh-CN.json +8 -1
  222. package/src/models/CollectionFieldModel.tsx +3 -1
  223. package/src/models/DisplayItemModel.tsx +1 -1
  224. package/src/models/EditableItemModel.tsx +1 -1
  225. package/src/models/FilterableItemModel.tsx +1 -1
  226. package/src/models/__tests__/dispatchEvent.when.test.ts +768 -0
  227. package/src/models/__tests__/flowModel.clone.test.ts +416 -0
  228. package/src/models/__tests__/flowModel.test.ts +20 -4
  229. package/src/models/flowModel.tsx +112 -7
  230. package/src/provider.tsx +9 -7
  231. package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
  232. package/src/resources/__tests__/sqlResource.test.ts +60 -0
  233. package/src/resources/baseRecordResource.ts +31 -0
  234. package/src/resources/multiRecordResource.ts +11 -4
  235. package/src/resources/singleRecordResource.ts +3 -0
  236. package/src/resources/sqlResource.ts +11 -6
  237. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
  238. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
  239. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
  240. package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
  241. package/src/runjs-context/contexts/base.ts +715 -44
  242. package/src/runjs-context/contributions.ts +88 -0
  243. package/src/runjs-context/helpers.ts +11 -1
  244. package/src/runjs-context/registry.ts +1 -1
  245. package/src/runjs-context/setup.ts +25 -9
  246. package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
  247. package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
  248. package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
  249. package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
  250. package/src/runjs-context/snippets/index.ts +75 -41
  251. package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
  252. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
  253. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
  254. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
  255. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
  256. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
  257. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
  258. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
  259. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
  260. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
  261. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
  262. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
  263. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
  264. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
  265. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
  266. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
  267. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
  268. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
  269. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
  270. package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
  271. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
  272. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
  273. package/src/runjsLibs.ts +622 -0
  274. package/src/scheduler/ModelOperationScheduler.ts +41 -24
  275. package/src/server.ts +11 -0
  276. package/src/types.ts +359 -1
  277. package/src/utils/__tests__/dateVariable.test.ts +101 -0
  278. package/src/utils/__tests__/params-resolvers.test.ts +40 -0
  279. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
  280. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
  281. package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
  282. package/src/utils/__tests__/runjsValue.test.ts +44 -0
  283. package/src/utils/__tests__/safeGlobals.test.ts +57 -2
  284. package/src/utils/__tests__/utils.test.ts +157 -0
  285. package/src/utils/associationObjectVariable.ts +2 -2
  286. package/src/utils/createCollectionContextMeta.ts +1 -0
  287. package/src/utils/createEphemeralContext.ts +5 -4
  288. package/src/utils/dateVariable.ts +397 -0
  289. package/src/utils/exceptions.ts +11 -0
  290. package/src/utils/index.ts +38 -3
  291. package/src/utils/params-resolvers.ts +23 -9
  292. package/src/utils/parsePathnameToViewParams.ts +2 -2
  293. package/src/utils/resolveModuleUrl.ts +91 -0
  294. package/src/utils/resolveRunJSObjectValues.ts +46 -0
  295. package/src/utils/runjsModuleLoader.ts +553 -0
  296. package/src/utils/runjsTemplateCompat.ts +828 -0
  297. package/src/utils/runjsValue.ts +287 -0
  298. package/src/utils/safeGlobals.ts +188 -17
  299. package/src/utils/schema-utils.ts +109 -1
  300. package/src/views/FlowView.tsx +11 -1
  301. package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
  302. package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
  303. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +44 -16
  304. package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
  305. package/src/views/createViewMeta.ts +22 -75
  306. package/src/views/index.tsx +1 -2
  307. package/src/views/runViewBeforeClose.ts +19 -0
  308. package/src/views/useDialog.tsx +34 -5
  309. package/src/views/useDrawer.tsx +33 -4
  310. package/src/views/usePage.tsx +63 -8
  311. package/src/views/usePopover.tsx +4 -1
  312. package/src/views/viewEvents.ts +55 -0
@@ -8,6 +8,10 @@
8
8
  */
9
9
 
10
10
  import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
11
+ import React from 'react';
12
+ import { createForm } from '@formily/core';
13
+ import { createSchemaField, FormProvider } from '@formily/react';
14
+ import { render, screen } from '@testing-library/react';
11
15
  import { FlowSettings } from '../flowSettings';
12
16
  import { DefaultSettingsIcon } from '../components/settings/wrappers/contextual/DefaultSettingsIcon';
13
17
  import { FlowModel } from '../models';
@@ -142,10 +146,10 @@ describe('FlowSettings', () => {
142
146
  expect(settingsItem?.sort).toBe(0);
143
147
  });
144
148
 
145
- test('should set up observable properties', () => {
149
+ test('should set up observable properties', async () => {
146
150
  // Test that enabled property is reactive
147
151
  const initialEnabled = flowSettings.enabled;
148
- flowSettings.enable();
152
+ await flowSettings.enable();
149
153
  expect(flowSettings.enabled).not.toBe(initialEnabled);
150
154
  expect(flowSettings.enabled).toBe(true);
151
155
  });
@@ -186,6 +190,43 @@ describe('FlowSettings', () => {
186
190
  flowSettings.registerComponents({});
187
191
  expect(Object.keys(flowSettings.components)).toHaveLength(0);
188
192
  });
193
+
194
+ test('should register component loaders and load component on render', async () => {
195
+ const loader = vi.fn(async () => ({
196
+ default: () => React.createElement('div', null, 'Lazy Flow Settings Component'),
197
+ }));
198
+
199
+ flowSettings.registerComponentLoaders({
200
+ DemoFlowSettingsLazyField: loader,
201
+ });
202
+
203
+ expect(loader).not.toHaveBeenCalled();
204
+
205
+ const SchemaField = createSchemaField();
206
+ const form = createForm();
207
+
208
+ render(
209
+ React.createElement(
210
+ FormProvider,
211
+ { form },
212
+ React.createElement(SchemaField, {
213
+ schema: {
214
+ type: 'object',
215
+ properties: {
216
+ demo: {
217
+ type: 'void',
218
+ 'x-component': 'DemoFlowSettingsLazyField',
219
+ },
220
+ },
221
+ },
222
+ components: flowSettings.components,
223
+ }),
224
+ ),
225
+ );
226
+
227
+ expect(await screen.findByText('Lazy Flow Settings Component')).toBeInTheDocument();
228
+ expect(loader).toHaveBeenCalledTimes(1);
229
+ });
189
230
  });
190
231
 
191
232
  describe('Scope Registration', () => {
@@ -228,30 +269,68 @@ describe('FlowSettings', () => {
228
269
  });
229
270
 
230
271
  describe('Enable/Disable Functionality', () => {
231
- test('should enable flow settings', () => {
272
+ test('should enable flow settings', async () => {
232
273
  expect(flowSettings.enabled).toBe(false);
233
274
 
234
- flowSettings.enable();
275
+ await flowSettings.enable();
235
276
 
236
277
  expect(flowSettings.enabled).toBe(true);
237
278
  });
238
279
 
239
- test('should disable flow settings', () => {
240
- flowSettings.enable();
280
+ test('should preload model loaders before enabling flow settings', async () => {
281
+ const preloadSpy = vi.spyOn(engine, 'preloadModelLoaders').mockResolvedValue({
282
+ requested: [],
283
+ loaded: [],
284
+ failed: [],
285
+ });
286
+
287
+ await flowSettings.enable();
288
+
289
+ expect(preloadSpy).toHaveBeenCalledTimes(1);
241
290
  expect(flowSettings.enabled).toBe(true);
291
+ });
242
292
 
243
- flowSettings.disable();
293
+ test('should preload model loaders before force enabling flow settings', async () => {
294
+ const preloadSpy = vi.spyOn(engine, 'preloadModelLoaders').mockResolvedValue({
295
+ requested: [],
296
+ loaded: [],
297
+ failed: [],
298
+ });
299
+
300
+ await flowSettings.forceEnable();
301
+
302
+ expect(preloadSpy).toHaveBeenCalledTimes(1);
303
+ expect(flowSettings.enabled).toBe(true);
304
+ });
305
+
306
+ test('should disable flow settings', async () => {
307
+ await flowSettings.enable();
308
+ expect(flowSettings.enabled).toBe(true);
309
+
310
+ await flowSettings.disable();
244
311
 
245
312
  expect(flowSettings.enabled).toBe(false);
246
313
  });
247
314
 
248
- test('should handle multiple enable/disable calls', () => {
249
- flowSettings.enable();
250
- flowSettings.enable();
315
+ test('should handle multiple enable/disable calls', async () => {
316
+ await flowSettings.enable();
317
+ await flowSettings.enable();
251
318
  expect(flowSettings.enabled).toBe(true);
252
319
 
253
- flowSettings.disable();
254
- flowSettings.disable();
320
+ await flowSettings.disable();
321
+ await flowSettings.disable();
322
+ expect(flowSettings.enabled).toBe(false);
323
+ });
324
+
325
+ test('forceDisable should clear force-enabled state and disable flow settings', async () => {
326
+ await flowSettings.forceEnable();
327
+ expect(flowSettings.enabled).toBe(true);
328
+
329
+ await flowSettings.forceDisable();
330
+
331
+ expect(flowSettings.enabled).toBe(false);
332
+
333
+ await flowSettings.disable();
255
334
  expect(flowSettings.enabled).toBe(false);
256
335
  });
257
336
  });
@@ -512,7 +591,7 @@ describe('FlowSettings', () => {
512
591
  });
513
592
 
514
593
  describe('Complex Integration Scenarios', () => {
515
- test('should maintain state consistency during multiple operations', () => {
594
+ test('should maintain state consistency during multiple operations', async () => {
516
595
  // Initialize with components and scopes
517
596
  const TestComponent = () => 'TestComponent';
518
597
  const testScope = () => 'testScope';
@@ -528,7 +607,7 @@ describe('FlowSettings', () => {
528
607
  });
529
608
 
530
609
  // Enable/disable
531
- flowSettings.enable();
610
+ await flowSettings.enable();
532
611
  expect(flowSettings.enabled).toBe(true);
533
612
 
534
613
  // Verify all state is maintained
@@ -536,7 +615,7 @@ describe('FlowSettings', () => {
536
615
  expect(flowSettings.scopes.testScope).toBe(testScope);
537
616
  expect(flowSettings.getToolbarItems().find((item) => item.key === 'integration-test')).toBeDefined();
538
617
 
539
- flowSettings.disable();
618
+ await flowSettings.disable();
540
619
  expect(flowSettings.enabled).toBe(false);
541
620
 
542
621
  // State should still be maintained after disable
@@ -14,11 +14,6 @@ import { FlowEngine } from '../flowEngine';
14
14
  import { FlowEngineProvider, useFlowEngine } from '../provider';
15
15
 
16
16
  describe('FlowEngineProvider/useFlowEngine', () => {
17
- it('throws without provider', () => {
18
- const run = () => renderHook(() => useFlowEngine());
19
- expect(run).toThrow(/FlowEngineProvider/);
20
- });
21
-
22
17
  it('returns engine within provider', () => {
23
18
  const engine = new FlowEngine();
24
19
  const wrapper = ({ children }: any) => <FlowEngineProvider engine={engine}>{children}</FlowEngineProvider>;
@@ -14,7 +14,7 @@ import { FlowEngine } from '../flowEngine';
14
14
  import { FlowModel, ModelRenderMode } from '../models/flowModel';
15
15
 
16
16
  describe('FlowModel.renderHiddenInConfig', () => {
17
- it('renders via renderHiddenInConfig when hidden and config enabled (React element mode, mounted)', () => {
17
+ it('renders via renderHiddenInConfig when hidden and config enabled (React element mode, mounted)', async () => {
18
18
  class ElemModel extends FlowModel {
19
19
  render() {
20
20
  return <div data-testid="content">Content</div>;
@@ -28,14 +28,14 @@ describe('FlowModel.renderHiddenInConfig', () => {
28
28
  const model = new ElemModel({ uid: 'elem-1', flowEngine: engine });
29
29
 
30
30
  // runtime hidden => mounted result should be empty (no content/hidden)
31
- engine.flowSettings.disable();
31
+ await engine.flowSettings.disable();
32
32
  model.setHidden(true);
33
33
  const { container, unmount, rerender } = render(model.render() as React.ReactElement);
34
34
  expect(screen.queryByTestId('content')).toBeNull();
35
35
  expect(screen.queryByTestId('hidden')).toBeNull();
36
36
 
37
37
  // config enabled + hidden => should show renderHiddenInConfig result
38
- engine.flowSettings.enable();
38
+ await engine.flowSettings.enable();
39
39
  rerender(model.render() as React.ReactElement);
40
40
  expect(screen.getByTestId('hidden').textContent).toBe('HiddenViaAPI');
41
41
 
@@ -46,7 +46,7 @@ describe('FlowModel.renderHiddenInConfig', () => {
46
46
  unmount();
47
47
  cleanup();
48
48
  });
49
- it('returns a render function when hidden and config enabled (RenderFunction mode)', () => {
49
+ it('returns a render function when hidden and config enabled (RenderFunction mode)', async () => {
50
50
  class FuncModel extends FlowModel {
51
51
  static override renderMode = ModelRenderMode.RenderFunction;
52
52
  render() {
@@ -63,13 +63,13 @@ describe('FlowModel.renderHiddenInConfig', () => {
63
63
  const model = engine.createModel({ use: 'FuncModel' }) as FuncModel;
64
64
 
65
65
  // runtime hidden => null
66
- engine.flowSettings.disable();
66
+ await engine.flowSettings.disable();
67
67
  model.setHidden(true);
68
68
  const runtimeHidden = model.render();
69
69
  expect(runtimeHidden).toBeNull();
70
70
 
71
71
  // config enabled + hidden => renderHiddenInConfig (function)
72
- engine.flowSettings.enable();
72
+ await engine.flowSettings.enable();
73
73
  const cfgHidden = model.render();
74
74
  expect(typeof cfgHidden).toBe('function');
75
75
  const cellNode = (cfgHidden as any)();
@@ -7,17 +7,17 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { describe, it, expect, beforeAll } from 'vitest';
10
+ import { beforeAll, describe, expect, it } from 'vitest';
11
11
  import {
12
12
  RunJSContextRegistry,
13
- getRunJSDocFor,
14
13
  createJSRunnerWithVersion,
15
- getRunJSScenesForModel,
14
+ getRunJSDocFor,
16
15
  getRunJSScenesForContext,
16
+ getRunJSScenesForModel,
17
17
  } from '..';
18
- import { setupRunJSContexts } from '../runjs-context/setup';
19
18
  import { FlowContext } from '../flowContext';
20
19
  import { JSRunner } from '../JSRunner';
20
+ import { setupRunJSContexts } from '../runjs-context/setup';
21
21
 
22
22
  describe('flowRunJSContext registry and doc', () => {
23
23
  beforeAll(async () => {
@@ -29,11 +29,16 @@ describe('flowRunJSContext registry and doc', () => {
29
29
  expect(RunJSContextRegistry['resolve']('v1' as any, '*')).toBeTruthy();
30
30
  });
31
31
 
32
+ it('should register v2 mapping', () => {
33
+ expect(RunJSContextRegistry['resolve']('v2' as any, '*')).toBeTruthy();
34
+ });
35
+
32
36
  it('should register all context types', () => {
33
37
  const contextTypes = [
34
38
  'JSBlockModel',
35
39
  'JSFieldModel',
36
40
  'JSItemModel',
41
+ 'JSItemActionModel',
37
42
  'JSColumnModel',
38
43
  'FormJSFieldItemModel',
39
44
  'JSRecordActionModel',
@@ -44,12 +49,22 @@ describe('flowRunJSContext registry and doc', () => {
44
49
  const ctor = RunJSContextRegistry['resolve']('v1' as any, modelClass);
45
50
  expect(ctor).toBeTruthy();
46
51
  });
52
+
53
+ contextTypes.forEach((modelClass) => {
54
+ const ctor = RunJSContextRegistry['resolve']('v2' as any, modelClass);
55
+ expect(ctor).toBeTruthy();
56
+ });
47
57
  });
48
58
 
49
59
  it('should expose scene metadata for contexts', () => {
50
60
  expect(getRunJSScenesForModel('JSBlockModel', 'v1')).toEqual(['block']);
51
61
  expect(getRunJSScenesForModel('JSFieldModel', 'v1')).toEqual(['detail']);
62
+ expect(getRunJSScenesForModel('JSItemActionModel', 'v1')).toEqual(['table']);
63
+ expect(getRunJSScenesForModel('JSBlockModel', 'v2')).toEqual(['block']);
64
+ expect(getRunJSScenesForModel('JSFieldModel', 'v2')).toEqual(['detail']);
65
+ expect(getRunJSScenesForModel('JSItemActionModel', 'v2')).toEqual(['table']);
52
66
  expect(getRunJSScenesForModel('UnknownModel', 'v1')).toEqual([]);
67
+ expect(getRunJSScenesForModel('UnknownModel', 'v2')).toEqual([]);
53
68
  });
54
69
 
55
70
  it('should only execute once (idempotent)', async () => {
@@ -80,7 +95,10 @@ describe('flowRunJSContext registry and doc', () => {
80
95
  (ctx as any).defineProperty('model', { value: { constructor: { name: 'JSFieldModel' } } });
81
96
  (ctx as any).defineProperty('api', { value: { auth: { locale: 'zh-CN' } } });
82
97
  const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
83
- expect(doc?.properties?.message).toMatch(/Ant Design 全局消息/);
98
+ const message = doc?.properties?.message;
99
+ const messageText =
100
+ typeof message === 'string' ? message : (message as any)?.description ?? (message as any)?.detail ?? '';
101
+ expect(String(messageText)).toMatch(/Ant Design 全局消息/);
84
102
  });
85
103
 
86
104
  it('should fallback to English when locale is not found', () => {
@@ -172,6 +190,7 @@ describe('flowRunJSContext registry and doc', () => {
172
190
  const ctx = new FlowContext();
173
191
  ctx.defineProperty('model', { value: { constructor: { name: 'JSColumnModel' } } });
174
192
  expect(getRunJSScenesForContext(ctx as any, { version: 'v1' })).toEqual(['table']);
193
+ expect(getRunJSScenesForContext(ctx as any, { version: 'v2' })).toEqual(['table']);
175
194
  });
176
195
 
177
196
  it('JSBlockModel context should have element property in doc', () => {
@@ -208,10 +227,10 @@ describe('flowRunJSContext registry and doc', () => {
208
227
  expect(doc?.properties?.message).toBeTruthy();
209
228
  });
210
229
 
211
- it('should have api property in base context', () => {
230
+ it('should have request method in base context', () => {
212
231
  const ctx: any = { model: { constructor: { name: '*' } } };
213
232
  const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
214
- expect(doc?.properties?.api).toBeTruthy();
233
+ expect(doc?.methods?.request).toBeTruthy();
215
234
  });
216
235
 
217
236
  it('should have t method in base context', () => {
@@ -7,10 +7,10 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { describe, it, expect, beforeAll } from 'vitest';
10
+ import { beforeAll, describe, expect, it } from 'vitest';
11
11
  import { RunJSContextRegistry, getRunJSDocFor } from '..';
12
- import { setupRunJSContexts } from '../runjs-context/setup';
13
12
  import { FlowContext } from '../flowContext';
13
+ import { setupRunJSContexts } from '../runjs-context/setup';
14
14
 
15
15
  describe('Specific RunJSContext implementations', () => {
16
16
  beforeAll(async () => {
@@ -34,7 +34,8 @@ describe('Specific RunJSContext implementations', () => {
34
34
  const ctx: any = { model: { constructor: { name: 'JSColumnModel' } } };
35
35
  const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
36
36
  expect(doc?.properties?.record).toBeTruthy();
37
- expect(doc?.properties?.record).toContain('row record');
37
+ const recordDoc: any = doc?.properties?.record;
38
+ expect(String(recordDoc?.description ?? recordDoc ?? '')).toContain('row record');
38
39
  });
39
40
 
40
41
  it('should have recordIndex property in doc', () => {
@@ -86,6 +87,14 @@ describe('Specific RunJSContext implementations', () => {
86
87
  expect(doc?.properties?.antd).toBeTruthy();
87
88
  });
88
89
 
90
+ it('should have ctx.auth.locale / ctx.viewer.drawer in doc', () => {
91
+ const ctx: any = { model: { constructor: { name: 'JSBlockModel' } } };
92
+ const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
93
+
94
+ expect(doc?.properties?.auth?.properties?.locale).toBeTruthy();
95
+ expect(doc?.properties?.viewer?.properties?.drawer).toBeTruthy();
96
+ });
97
+
89
98
  it('should have element property', () => {
90
99
  const ctx: any = { model: { constructor: { name: 'JSBlockModel' } } };
91
100
  const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
@@ -214,4 +223,26 @@ describe('Specific RunJSContext implementations', () => {
214
223
  expect(doc?.label).toMatch(/表单 JS 字段项/);
215
224
  });
216
225
  });
226
+
227
+ describe('JSEditableFieldRunJSContext', () => {
228
+ it('should be registered for JSEditableFieldModel', () => {
229
+ const ctor = RunJSContextRegistry['resolve']('v1' as any, 'JSEditableFieldModel');
230
+ expect(ctor).toBeTruthy();
231
+ });
232
+
233
+ it('should have getValue/setValue methods in doc', () => {
234
+ const ctx: any = { model: { constructor: { name: 'JSEditableFieldModel' } } };
235
+ const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
236
+ expect(doc?.methods?.getValue).toBeTruthy();
237
+ expect(doc?.methods?.setValue).toBeTruthy();
238
+ });
239
+
240
+ it('should support zh-CN locale', () => {
241
+ const ctx = new FlowContext();
242
+ (ctx as any).defineProperty('model', { value: { constructor: { name: 'JSEditableFieldModel' } } });
243
+ (ctx as any).defineProperty('api', { value: { auth: { locale: 'zh-CN' } } });
244
+ const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
245
+ expect(doc?.label).toMatch(/可编辑字段/);
246
+ });
247
+ });
217
248
  });
@@ -7,9 +7,9 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { describe, it, expect, beforeAll } from 'vitest';
11
- import { FlowContext } from '../flowContext';
10
+ import { beforeAll, describe, expect, it } from 'vitest';
12
11
  import { createJSRunnerWithVersion, getRunJSDocFor } from '..';
12
+ import { FlowContext } from '../flowContext';
13
13
  import { setupRunJSContexts } from '../runjs-context/setup';
14
14
 
15
15
  describe('RunJS Context Runtime Behavior', () => {
@@ -186,6 +186,7 @@ describe('RunJS Context Runtime Behavior', () => {
186
186
  'JSBlockModel',
187
187
  'JSFieldModel',
188
188
  'JSItemModel',
189
+ 'JSItemActionModel',
189
190
  'JSColumnModel',
190
191
  'FormJSFieldItemModel',
191
192
  'JSRecordActionModel',
@@ -224,7 +225,7 @@ describe('RunJS Context Runtime Behavior', () => {
224
225
  // Base properties from FlowRunJSContext
225
226
  expect(doc?.properties?.logger).toBeTruthy();
226
227
  expect(doc?.properties?.message).toBeTruthy();
227
- expect(doc?.properties?.api).toBeTruthy();
228
+ expect(doc?.methods?.request).toBeTruthy();
228
229
  expect(doc?.methods?.t).toBeTruthy();
229
230
  expect(doc?.methods?.requireAsync).toBeTruthy();
230
231
  }
@@ -237,6 +238,7 @@ describe('RunJS Context Runtime Behavior', () => {
237
238
  'JSBlockModel',
238
239
  'JSFieldModel',
239
240
  'JSItemModel',
241
+ 'JSItemActionModel',
240
242
  'JSColumnModel',
241
243
  'FormJSFieldItemModel',
242
244
  'JSRecordActionModel',
@@ -0,0 +1,89 @@
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
+
12
+ describe('RunJS context contributions', () => {
13
+ it('should apply contribution during setupRunJSContexts()', async () => {
14
+ vi.resetModules();
15
+ const mod: any = await import('..');
16
+ const {
17
+ registerRunJSContextContribution,
18
+ setupRunJSContexts,
19
+ RunJSContextRegistry,
20
+ getRunJSDocFor,
21
+ FlowRunJSContext,
22
+ } = mod;
23
+
24
+ registerRunJSContextContribution(({ version, RunJSContextRegistry: Registry, FlowRunJSContext: BaseCtx }: any) => {
25
+ if (version !== 'v1') return;
26
+ class PluginTestRunJSContext extends BaseCtx {}
27
+ PluginTestRunJSContext.define({
28
+ properties: {
29
+ plugin: { description: 'plugin namespace', detail: 'object' },
30
+ },
31
+ });
32
+ Registry.register('v1', 'PluginTestModel', PluginTestRunJSContext, { scenes: ['block'] });
33
+ });
34
+
35
+ await setupRunJSContexts();
36
+
37
+ const ctor = RunJSContextRegistry.resolve('v1', 'PluginTestModel');
38
+ expect(ctor).toBeTruthy();
39
+ expect((ctor as any).name).toBe('PluginTestRunJSContext');
40
+
41
+ const ctx: any = { model: { constructor: { name: 'PluginTestModel' } } };
42
+ const doc = getRunJSDocFor(ctx, { version: 'v1' });
43
+ expect(doc?.properties?.plugin).toBeTruthy();
44
+
45
+ // Ensure FlowRunJSContext stays usable
46
+ expect(typeof FlowRunJSContext.getDoc).toBe('function');
47
+ });
48
+
49
+ it('should apply late contribution immediately after setup', async () => {
50
+ vi.resetModules();
51
+ const mod: any = await import('..');
52
+ const { registerRunJSContextContribution, setupRunJSContexts, getRunJSDocFor, FlowContext } = mod;
53
+
54
+ await setupRunJSContexts();
55
+
56
+ registerRunJSContextContribution(({ version, FlowRunJSContext }: any) => {
57
+ if (version !== 'v1') return;
58
+ FlowRunJSContext.define({
59
+ properties: {
60
+ pluginLate: { description: 'late-added', detail: 'string' },
61
+ },
62
+ });
63
+ });
64
+
65
+ const ctx = new FlowContext();
66
+ const doc = getRunJSDocFor(ctx as any, { version: 'v1' });
67
+ expect(doc?.properties?.pluginLate).toBeTruthy();
68
+ });
69
+
70
+ it('should run each contribution at most once per version', async () => {
71
+ vi.resetModules();
72
+ const mod: any = await import('..');
73
+ const { registerRunJSContextContribution, setupRunJSContexts } = mod;
74
+
75
+ let count = 0;
76
+ const fn = ({ version }: any) => {
77
+ if (version !== 'v1') return;
78
+ count += 1;
79
+ };
80
+
81
+ registerRunJSContextContribution(fn);
82
+ registerRunJSContextContribution(fn); // duplicate registration should be ignored
83
+
84
+ await setupRunJSContexts();
85
+ await setupRunJSContexts();
86
+
87
+ expect(count).toBe(1);
88
+ });
89
+ });