@nocobase/client-v2 2.1.0-beta.21 → 2.1.0-beta.23

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 (1147) hide show
  1. package/es/Application.d.ts +27 -105
  2. package/es/BaseApplication.d.ts +176 -0
  3. package/es/PinnedPluginListContext.d.ts +22 -0
  4. package/es/Plugin.d.ts +37 -11
  5. package/es/PluginManager.d.ts +19 -20
  6. package/es/PluginSettingsManager.d.ts +168 -30
  7. package/es/RouteRepository.d.ts +124 -0
  8. package/es/RouterManager.d.ts +18 -13
  9. package/es/WebSocketClient.d.ts +3 -3
  10. package/es/acl/ACLProvider.d.ts +81 -0
  11. package/es/acl/createAclSnippetAllow.d.ts +16 -0
  12. package/es/acl/index.d.ts +11 -0
  13. package/es/acl/useAclSnippets.d.ts +16 -0
  14. package/es/ai/ai-manager.d.ts +15 -0
  15. package/es/ai/index.d.ts +12 -0
  16. package/{lib/components → es/ai/skills-manager}/index.d.ts +1 -2
  17. package/es/ai/skills-manager/types.d.ts +23 -0
  18. package/es/ai/tools-manager/hooks/context.d.ts +17 -0
  19. package/es/ai/tools-manager/hooks/index.d.ts +11 -0
  20. package/es/ai/tools-manager/hooks/provider.d.ts +16 -0
  21. package/es/ai/tools-manager/index.d.ts +20 -0
  22. package/es/ai/tools-manager/types.d.ts +90 -0
  23. package/es/ai/utils.d.ts +10 -0
  24. package/es/authRedirect.d.ts +63 -0
  25. package/es/collection-field-interface/CollectionFieldInterface.d.ts +71 -0
  26. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +109 -0
  27. package/es/collection-manager/interfaces/checkbox.d.ts +87 -0
  28. package/es/collection-manager/interfaces/checkboxGroup.d.ts +138 -0
  29. package/es/collection-manager/interfaces/collection.d.ts +145 -0
  30. package/es/collection-manager/interfaces/color.d.ts +62 -0
  31. package/es/collection-manager/interfaces/createdAt.d.ts +96 -0
  32. package/es/collection-manager/interfaces/createdBy.d.ts +63 -0
  33. package/es/collection-manager/interfaces/dateOnly.d.ts +103 -0
  34. package/es/collection-manager/interfaces/datetime.d.ts +121 -0
  35. package/es/collection-manager/interfaces/datetimeNoTz.d.ts +121 -0
  36. package/es/collection-manager/interfaces/email.d.ts +87 -0
  37. package/es/collection-manager/interfaces/icon.d.ts +44 -0
  38. package/es/collection-manager/interfaces/id.d.ts +71 -0
  39. package/es/collection-manager/interfaces/index.d.ts +49 -0
  40. package/es/collection-manager/interfaces/input.d.ts +187 -0
  41. package/es/collection-manager/interfaces/integer.d.ts +186 -0
  42. package/es/collection-manager/interfaces/json.d.ts +62 -0
  43. package/es/collection-manager/interfaces/linkTo.d.ts +90 -0
  44. package/es/collection-manager/interfaces/m2m.d.ts +268 -0
  45. package/es/collection-manager/interfaces/m2o.d.ts +215 -0
  46. package/es/collection-manager/interfaces/markdown.d.ts +108 -0
  47. package/es/collection-manager/interfaces/multipleSelect.d.ts +141 -0
  48. package/es/collection-manager/interfaces/nanoid.d.ts +131 -0
  49. package/es/collection-manager/interfaces/number.d.ts +187 -0
  50. package/es/collection-manager/interfaces/o2m.d.ts +243 -0
  51. package/es/collection-manager/interfaces/o2o.d.ts +621 -0
  52. package/es/collection-manager/interfaces/password.d.ts +112 -0
  53. package/es/collection-manager/interfaces/percent.d.ts +157 -0
  54. package/es/collection-manager/interfaces/phone.d.ts +85 -0
  55. package/es/collection-manager/interfaces/properties/index.d.ts +145 -0
  56. package/es/collection-manager/interfaces/properties/operators.d.ts +294 -0
  57. package/es/collection-manager/interfaces/radioGroup.d.ts +134 -0
  58. package/es/collection-manager/interfaces/richText.d.ts +110 -0
  59. package/es/collection-manager/interfaces/select.d.ts +139 -0
  60. package/es/collection-manager/interfaces/snowflake-id.d.ts +117 -0
  61. package/es/collection-manager/interfaces/subTable.d.ts +172 -0
  62. package/es/collection-manager/interfaces/tableoid.d.ts +59 -0
  63. package/es/collection-manager/interfaces/textarea.d.ts +107 -0
  64. package/es/collection-manager/interfaces/time.d.ts +64 -0
  65. package/es/collection-manager/interfaces/types.d.ts +38 -0
  66. package/es/collection-manager/interfaces/unixTimestamp.d.ts +124 -0
  67. package/es/collection-manager/interfaces/updatedAt.d.ts +96 -0
  68. package/es/collection-manager/interfaces/updatedBy.d.ts +63 -0
  69. package/es/collection-manager/interfaces/url.d.ts +72 -0
  70. package/es/collection-manager/interfaces/utils/index.d.ts +14 -0
  71. package/es/collection-manager/interfaces/uuid.d.ts +120 -0
  72. package/es/components/AppComponents.d.ts +27 -0
  73. package/es/components/Icon.d.ts +24 -0
  74. package/es/components/index.d.ts +2 -0
  75. package/es/css-variable/CSSVariableProvider.d.ts +15 -0
  76. package/es/flow/FlowModelRepository.d.ts +36 -0
  77. package/es/flow/FlowPage.d.ts +20 -0
  78. package/es/flow/actions/aclCheck.d.ts +9 -0
  79. package/es/flow/actions/aclCheckRefresh.d.ts +9 -0
  80. package/es/flow/actions/afterSuccess.d.ts +9 -0
  81. package/es/flow/actions/blockHeight.d.ts +9 -0
  82. package/es/flow/actions/columnFixed.d.ts +9 -0
  83. package/es/flow/actions/confirm.d.ts +9 -0
  84. package/es/flow/actions/customVariable.d.ts +9 -0
  85. package/es/flow/actions/dataLoadingMode.d.ts +9 -0
  86. package/es/flow/actions/dataScope.d.ts +9 -0
  87. package/es/flow/actions/dateTimeFormat.d.ts +9 -0
  88. package/es/flow/actions/displayFieldComponent.d.ts +19 -0
  89. package/es/flow/actions/fieldComponent.d.ts +9 -0
  90. package/es/flow/actions/filterFormDefaultValues.d.ts +9 -0
  91. package/es/flow/actions/formAssignRules.d.ts +9 -0
  92. package/es/flow/actions/index.d.ts +40 -0
  93. package/es/flow/actions/layout.d.ts +10 -0
  94. package/es/flow/actions/linkageRules.d.ts +23 -0
  95. package/es/flow/actions/linkageRulesRefresh.d.ts +9 -0
  96. package/es/flow/actions/navigateToURL.d.ts +9 -0
  97. package/es/flow/actions/numberFormat.d.ts +9 -0
  98. package/es/flow/actions/openView.d.ts +15 -0
  99. package/es/flow/actions/overflowMode.d.ts +9 -0
  100. package/es/flow/actions/pattern.d.ts +9 -0
  101. package/es/flow/actions/refreshTargetBlocks.d.ts +9 -0
  102. package/es/flow/actions/renderMode.d.ts +9 -0
  103. package/es/flow/actions/required.d.ts +9 -0
  104. package/es/flow/actions/runjs.d.ts +9 -0
  105. package/es/flow/actions/setTargetDataScope.d.ts +10 -0
  106. package/es/flow/actions/showMessage.d.ts +9 -0
  107. package/es/flow/actions/showNotification.d.ts +9 -0
  108. package/es/flow/actions/sortingRules.d.ts +9 -0
  109. package/es/flow/actions/titleField.d.ts +9 -0
  110. package/es/flow/actions/validation.d.ts +9 -0
  111. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +51 -0
  112. package/es/flow/admin-shell/admin-layout/AdminLayoutCompat.d.ts +112 -0
  113. package/es/flow/admin-shell/admin-layout/AdminLayoutComponent.d.ts +10 -0
  114. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +107 -0
  115. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +60 -0
  116. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +167 -0
  117. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +150 -0
  118. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +31 -0
  119. package/es/flow/admin-shell/admin-layout/HelpLite.d.ts +13 -0
  120. package/{lib/utils/requirejs.d.ts → es/flow/admin-shell/admin-layout/PinnedPluginListLite.d.ts} +5 -8
  121. package/es/flow/admin-shell/admin-layout/ResetThemeTokenAndKeepAlgorithm.d.ts +13 -0
  122. package/es/flow/admin-shell/admin-layout/TopbarActionsBar.d.ts +25 -0
  123. package/es/flow/admin-shell/admin-layout/constants.d.ts +9 -0
  124. package/es/flow/admin-shell/admin-layout/flowSettingsPreference.d.ts +23 -0
  125. package/es/flow/admin-shell/admin-layout/index.d.ts +19 -0
  126. package/es/flow/admin-shell/admin-layout/mobile-layout.d.ts +12 -0
  127. package/es/flow/admin-shell/admin-layout/mobileMenuNavigation.d.ts +15 -0
  128. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +42 -0
  129. package/{lib/context.d.ts → es/flow/admin-shell/admin-layout/useApplications.d.ts} +3 -2
  130. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +28 -0
  131. package/es/flow/common/Liquid.d.ts +33 -0
  132. package/es/flow/common/Markdown/Display.d.ts +9 -0
  133. package/es/flow/common/Markdown/Edit.d.ts +23 -0
  134. package/es/flow/common/Markdown/Markdown.d.ts +24 -0
  135. package/es/flow/common/Markdown/style.d.ts +14 -0
  136. package/es/flow/common/Markdown/useCDN.d.ts +9 -0
  137. package/es/flow/components/AdminLayout.d.ts +11 -0
  138. package/es/flow/components/BlockItemCard.d.ts +16 -0
  139. package/es/flow/components/ConditionBuilder.d.ts +17 -0
  140. package/es/flow/components/DefaultValue.d.ts +20 -0
  141. package/es/flow/components/DynamicFlowsIcon.d.ts +13 -0
  142. package/es/flow/components/EllipsisWithTooltip.d.ts +17 -0
  143. package/es/flow/components/ExpiresRadio/index.d.ts +12 -0
  144. package/es/flow/components/FieldAssignEditor.d.ts +32 -0
  145. package/es/flow/components/FieldAssignExactDatePicker.d.ts +23 -0
  146. package/es/flow/components/FieldAssignRulesEditor.d.ts +70 -0
  147. package/es/flow/components/FieldAssignValueInput.d.ts +88 -0
  148. package/es/flow/components/FlowRoute.d.ts +28 -0
  149. package/es/flow/components/Grid/index.d.ts +26 -0
  150. package/es/flow/components/JsonEditor.d.ts +19 -0
  151. package/es/flow/components/RunJSValueEditor.d.ts +19 -0
  152. package/{lib/hooks/useApp.d.ts → es/flow/components/SkeletonFallback.d.ts} +2 -2
  153. package/es/flow/components/TextAreaWithContextSelector.d.ts +30 -0
  154. package/es/flow/components/code-editor/core/EditorCore.d.ts +25 -0
  155. package/es/flow/components/code-editor/core/tooltipParent.d.ts +9 -0
  156. package/es/flow/components/code-editor/errorHelpers.d.ts +17 -0
  157. package/es/flow/components/code-editor/extension/CodeEditorExtension.d.ts +14 -0
  158. package/{lib/hooks → es/flow/components/code-editor/extension}/index.d.ts +1 -3
  159. package/es/flow/components/code-editor/formatDocInfo.d.ts +17 -0
  160. package/es/flow/components/code-editor/hooks/useCodeRunner.d.ts +23 -0
  161. package/es/flow/components/code-editor/hooks/useRunJSDocCompletions.d.ts +15 -0
  162. package/es/flow/components/code-editor/htmlCompletion.d.ts +11 -0
  163. package/es/flow/components/code-editor/index.d.ts +31 -0
  164. package/es/flow/components/code-editor/javascriptCompletion.d.ts +11 -0
  165. package/es/flow/components/code-editor/javascriptHtmlTemplate.d.ts +16 -0
  166. package/es/flow/components/code-editor/jsxCompletion.d.ts +10 -0
  167. package/es/flow/components/code-editor/linter.d.ts +19 -0
  168. package/es/flow/components/code-editor/panels/LogsPanel.d.ts +15 -0
  169. package/es/flow/components/code-editor/panels/RightExtra.d.ts +18 -0
  170. package/es/flow/components/code-editor/panels/SnippetsDrawer.d.ts +18 -0
  171. package/es/flow/components/code-editor/resolveScenes.d.ts +10 -0
  172. package/es/flow/components/code-editor/runjsCompletionSource.d.ts +20 -0
  173. package/es/flow/components/code-editor/runjsCompletions.d.ts +23 -0
  174. package/es/flow/components/code-editor/runjsDiagnostics.d.ts +46 -0
  175. package/es/flow/components/code-editor/types.d.ts +32 -0
  176. package/es/flow/components/drag-drop/Sortable.d.ts +12 -0
  177. package/es/flow/components/drag-drop/SortableItem.d.ts +12 -0
  178. package/es/flow/components/drag-drop/index.d.ts +10 -0
  179. package/es/flow/components/fieldAssignOptions.d.ts +23 -0
  180. package/es/flow/components/filter/FilterContainer.d.ts +67 -0
  181. package/es/flow/components/filter/FilterGroup.d.ts +66 -0
  182. package/es/flow/components/filter/FilterItem.d.ts +40 -0
  183. package/es/flow/components/filter/LinkageFilterItem.d.ts +51 -0
  184. package/es/flow/components/filter/VariableFilterItem.d.ts +37 -0
  185. package/es/flow/components/filter/fieldsToOptions.d.ts +9 -0
  186. package/es/flow/components/filter/index.d.ts +16 -0
  187. package/es/flow/components/index.d.ts +14 -0
  188. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +11 -0
  189. package/{lib/components/MainComponent.d.ts → es/flow/components/placeholders/FieldPlaceholder.d.ts} +1 -1
  190. package/es/flow/components/useAssociationTitleFieldSync.d.ts +13 -0
  191. package/es/flow/flows/editMarkdownFlow.d.ts +10 -0
  192. package/es/flow/flows/openViewFlow.d.ts +17 -0
  193. package/es/flow/formily/ReactiveField.d.ts +19 -0
  194. package/es/flow/formily/index.d.ts +8 -0
  195. package/es/flow/getViewDiffAndUpdateHidden.d.ts +21 -0
  196. package/es/flow/index.d.ts +30 -0
  197. package/es/flow/internal/components/Markdown/DisplayMarkdown.d.ts +10 -0
  198. package/{lib/utils/globalDeps.d.ts → es/flow/internal/components/Markdown/markdown-it-plugins/mermaidPlugin.d.ts} +3 -3
  199. package/es/flow/internal/components/Markdown/md.d.ts +11 -0
  200. package/es/flow/internal/components/Markdown/style.d.ts +9 -0
  201. package/es/flow/internal/components/Markdown/util.d.ts +15 -0
  202. package/{lib/components/BlankComponent.d.ts → es/flow/internal/constants/HeightMode.d.ts} +5 -4
  203. package/es/flow/internal/index.d.ts +11 -0
  204. package/es/flow/internal/utils/associationValueCoercion.d.ts +15 -0
  205. package/es/flow/internal/utils/dateTimeUtils.d.ts +10 -0
  206. package/es/flow/internal/utils/enumOptionsUtils.d.ts +23 -0
  207. package/es/flow/internal/utils/modelUtils.d.ts +27 -0
  208. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +15 -0
  209. package/es/flow/internal/utils/rebuildFieldSubModel.d.ts +33 -0
  210. package/es/flow/internal/utils/saveStepParamsWithSubModels.d.ts +11 -0
  211. package/es/flow/internal/utils/titleFieldQuickSync.d.ts +36 -0
  212. package/es/flow/models/actions/AddChildActionModel.d.ts +16 -0
  213. package/es/flow/models/actions/AddNewActionModel.d.ts +16 -0
  214. package/es/flow/models/actions/BulkDeleteActionModel.d.ts +16 -0
  215. package/es/flow/models/actions/DeleteActionModel.d.ts +15 -0
  216. package/es/flow/models/actions/EditActionModel.d.ts +16 -0
  217. package/es/flow/models/actions/ExpandCollapseActionModel.d.ts +19 -0
  218. package/es/flow/models/actions/FilterActionModel.d.ts +26 -0
  219. package/{lib/components/RouterBridge.d.ts → es/flow/models/actions/JSActionModel.d.ts} +5 -5
  220. package/es/flow/models/actions/JSCollectionActionModel.d.ts +14 -0
  221. package/es/flow/models/actions/JSItemActionModel.d.ts +19 -0
  222. package/es/flow/models/actions/JSRecordActionModel.d.ts +14 -0
  223. package/es/flow/models/actions/LinkActionModel.d.ts +15 -0
  224. package/es/flow/models/actions/LinkActionUtils.d.ts +34 -0
  225. package/es/flow/models/actions/PopupCollectionActionModel.d.ts +14 -0
  226. package/es/flow/models/actions/RefreshActionModel.d.ts +14 -0
  227. package/es/flow/models/actions/UpdateRecordActionModel.d.ts +27 -0
  228. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +12 -0
  229. package/es/flow/models/actions/ViewActionModel.d.ts +15 -0
  230. package/es/flow/models/actions/index.d.ts +24 -0
  231. package/es/flow/models/actions/joinUrlSearch.d.ts +12 -0
  232. package/es/flow/models/base/ActionGroupModel.d.ts +9 -0
  233. package/es/flow/models/base/ActionGroupModelCore.d.ts +29 -0
  234. package/es/flow/models/base/ActionModel.d.ts +9 -0
  235. package/es/flow/models/base/ActionModelCore.d.ts +45 -0
  236. package/es/flow/models/base/AssociationFieldGroupModel.d.ts +49 -0
  237. package/es/flow/models/base/BlockGridModel.d.ts +22 -0
  238. package/es/flow/models/base/BlockModel.d.ts +57 -0
  239. package/es/flow/models/base/CollectionActionModel.d.ts +12 -0
  240. package/es/flow/models/base/CollectionBlockModel.d.ts +71 -0
  241. package/es/flow/models/base/CommonItemModel.d.ts +11 -0
  242. package/es/flow/models/base/DataBlockModel.d.ts +12 -0
  243. package/es/flow/models/base/FieldModel.d.ts +17 -0
  244. package/es/flow/models/base/FilterBlockModel.d.ts +12 -0
  245. package/es/flow/models/base/GridModel.d.ts +99 -0
  246. package/es/flow/models/base/PageModel/ChildPageModel.d.ts +21 -0
  247. package/es/flow/models/base/PageModel/MainPageModel.d.ts +17 -0
  248. package/es/flow/models/base/PageModel/OldPageModel.d.ts +21 -0
  249. package/es/flow/models/base/PageModel/PageModel.d.ts +53 -0
  250. package/es/flow/models/base/PageModel/PageTabModel.d.ts +42 -0
  251. package/es/flow/models/base/PageModel/RootPageModel.d.ts +24 -0
  252. package/es/flow/models/base/PageModel/SubPageModel.d.ts +17 -0
  253. package/es/flow/models/base/PageModel/closeGuard.d.ts +31 -0
  254. package/{lib → es/flow/models/base/PageModel}/index.d.ts +6 -6
  255. package/es/flow/models/base/PopupActionModel.d.ts +13 -0
  256. package/es/flow/models/base/RecordActionModel.d.ts +12 -0
  257. package/es/flow/models/base/RouteModel.d.ts +11 -0
  258. package/es/flow/models/base/index.d.ts +24 -0
  259. package/es/flow/models/blocks/assign-form/AssignFormGridModel.d.ts +15 -0
  260. package/es/flow/models/blocks/assign-form/AssignFormItemModel.d.ts +22 -0
  261. package/es/flow/models/blocks/assign-form/AssignFormModel.d.ts +26 -0
  262. package/es/flow/models/blocks/assign-form/index.d.ts +11 -0
  263. package/es/flow/models/blocks/details/DetailsAssociationFieldGroupModel.d.ts +12 -0
  264. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +41 -0
  265. package/es/flow/models/blocks/details/DetailsCustomItemModel.d.ts +13 -0
  266. package/es/flow/models/blocks/details/DetailsGridModel.d.ts +33 -0
  267. package/es/flow/models/blocks/details/DetailsItemModel.d.ts +60 -0
  268. package/es/flow/models/blocks/details/DetailsJSFieldItemModel.d.ts +19 -0
  269. package/es/flow/models/blocks/details/index.d.ts +14 -0
  270. package/es/flow/models/blocks/details/utils.d.ts +16 -0
  271. package/es/flow/models/blocks/filter-form/FieldModelSelect.d.ts +10 -0
  272. package/es/flow/models/blocks/filter-form/FieldOperatorSelect.d.ts +10 -0
  273. package/es/flow/models/blocks/filter-form/FilterFormActionGroupModel.d.ts +13 -0
  274. package/es/flow/models/blocks/filter-form/FilterFormActionModel.d.ts +11 -0
  275. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +39 -0
  276. package/es/flow/models/blocks/filter-form/FilterFormCollapseActionModel.d.ts +18 -0
  277. package/es/flow/models/blocks/filter-form/FilterFormCustomItemModel.d.ts +13 -0
  278. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +43 -0
  279. package/es/flow/models/blocks/filter-form/FilterFormItemModel.d.ts +59 -0
  280. package/es/flow/models/blocks/filter-form/FilterFormJSActionModel.d.ts +11 -0
  281. package/es/flow/models/blocks/filter-form/FilterFormResetActionModel.d.ts +13 -0
  282. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +15 -0
  283. package/es/flow/models/blocks/filter-form/SourceCascader.d.ts +10 -0
  284. package/es/flow/models/blocks/filter-form/customFieldOperators.d.ts +34 -0
  285. package/es/flow/models/blocks/filter-form/fields/FieldComponentProps.d.ts +13 -0
  286. package/es/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.d.ts +52 -0
  287. package/es/flow/models/blocks/filter-form/fields/FilterFormCustomRecordSelectFieldModel.d.ts +20 -0
  288. package/{lib/components/RouterContextCleaner.d.ts → es/flow/models/blocks/filter-form/fields/FilterFormFieldModel.d.ts} +3 -4
  289. package/es/flow/models/blocks/filter-form/fields/FilterFormRecordSelectFieldModel.d.ts +12 -0
  290. package/es/flow/models/blocks/filter-form/fields/date-time/DateOnlyFilterFieldModel.d.ts +14 -0
  291. package/es/flow/models/blocks/filter-form/fields/date-time/DateTimeFilterFieldModel.d.ts +13 -0
  292. package/es/flow/models/blocks/filter-form/fields/date-time/DateTimeNoTzFilterFieldModel.d.ts +13 -0
  293. package/es/flow/models/blocks/filter-form/fields/date-time/DateTimeTzFilterFieldModel.d.ts +14 -0
  294. package/es/flow/models/blocks/filter-form/fields/date-time/components/DateFilterDynamicComponent.d.ts +10 -0
  295. package/es/flow/models/blocks/filter-form/fields/date-time/components/FilterDatePicker.d.ts +10 -0
  296. package/es/flow/models/blocks/filter-form/fields/date-time/components/FilterRangePicker.d.ts +10 -0
  297. package/es/flow/models/blocks/filter-form/fields/date-time/index.d.ts +12 -0
  298. package/es/flow/models/blocks/filter-form/fields/index.d.ts +13 -0
  299. package/es/flow/models/blocks/filter-form/index.d.ts +23 -0
  300. package/es/flow/models/blocks/filter-form/legacyDefaultValueMigration.d.ts +13 -0
  301. package/es/flow/models/blocks/filter-form/valueNormalization.d.ts +17 -0
  302. package/es/flow/models/blocks/filter-manager/FilterManager.d.ts +141 -0
  303. package/es/flow/models/blocks/filter-manager/flow-actions/connectFields.d.ts +10 -0
  304. package/es/flow/models/blocks/filter-manager/flow-actions/customizeFilterRender.d.ts +10 -0
  305. package/es/flow/models/blocks/filter-manager/flow-actions/defaultOperator.d.ts +9 -0
  306. package/es/flow/models/blocks/filter-manager/flow-actions/index.d.ts +12 -0
  307. package/es/flow/models/blocks/filter-manager/flow-actions/operatorComponentProps.d.ts +10 -0
  308. package/es/flow/models/blocks/filter-manager/index.d.ts +11 -0
  309. package/es/flow/models/blocks/filter-manager/utils.d.ts +21 -0
  310. package/es/flow/models/blocks/form/CreateFormModel.d.ts +22 -0
  311. package/es/flow/models/blocks/form/EditFormModel.d.ts +26 -0
  312. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +13 -0
  313. package/es/flow/models/blocks/form/FormActionModel.d.ts +21 -0
  314. package/es/flow/models/blocks/form/FormAssociationFieldGroupModel.d.ts +12 -0
  315. package/es/flow/models/blocks/form/FormAssociationItemModel.d.ts +39 -0
  316. package/es/flow/models/blocks/form/FormBlockModel.d.ts +96 -0
  317. package/es/flow/models/blocks/form/FormCustomItemModel.d.ts +13 -0
  318. package/es/flow/models/blocks/form/FormGridModel.d.ts +35 -0
  319. package/es/flow/models/blocks/form/FormItemModel.d.ts +39 -0
  320. package/es/flow/models/blocks/form/FormJSFieldItemModel.d.ts +18 -0
  321. package/es/flow/models/blocks/form/JSFormActionModel.d.ts +11 -0
  322. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +44 -0
  323. package/es/flow/models/blocks/form/assignRulesUpdateAssociationValues.d.ts +18 -0
  324. package/es/flow/models/blocks/form/dynamicNamePath.d.ts +19 -0
  325. package/es/flow/models/blocks/form/index.d.ts +21 -0
  326. package/es/flow/models/blocks/form/legacyDefaultValueMigration.d.ts +13 -0
  327. package/es/flow/models/blocks/form/submitHandler.d.ts +9 -0
  328. package/es/flow/models/blocks/form/submitValues.d.ts +40 -0
  329. package/es/flow/models/blocks/form/value-runtime/conditions.d.ts +9 -0
  330. package/es/flow/models/blocks/form/value-runtime/deps.d.ts +23 -0
  331. package/es/flow/models/blocks/form/value-runtime/form-patch.d.ts +19 -0
  332. package/es/flow/models/blocks/form/value-runtime/index.d.ts +11 -0
  333. package/es/flow/models/blocks/form/value-runtime/path.d.ts +25 -0
  334. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +137 -0
  335. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +88 -0
  336. package/es/flow/models/blocks/form/value-runtime/types.d.ts +62 -0
  337. package/es/flow/models/blocks/form/value-runtime/utils.d.ts +12 -0
  338. package/es/flow/models/blocks/index.d.ts +15 -0
  339. package/es/flow/models/blocks/js-block/JSBlock.d.ts +16 -0
  340. package/es/flow/models/blocks/js-block/index.d.ts +9 -0
  341. package/es/flow/models/blocks/shared/legacyDefaultValueMigrationBase.d.ts +20 -0
  342. package/es/flow/models/blocks/table/JSColumnModel.d.ts +21 -0
  343. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +18 -0
  344. package/es/flow/models/blocks/table/TableAssociationFieldGroupModel.d.ts +12 -0
  345. package/es/flow/models/blocks/table/TableBlockModel.d.ts +69 -0
  346. package/es/flow/models/blocks/table/TableColumnModel.d.ts +29 -0
  347. package/es/flow/models/blocks/table/TableCustomColumnModel.d.ts +12 -0
  348. package/es/flow/models/blocks/table/TableJSFieldItemModel.d.ts +19 -0
  349. package/{lib/hooks/usePlugin.d.ts → es/flow/models/blocks/table/TableSelectModel.d.ts} +6 -3
  350. package/es/flow/models/blocks/table/dragSort/dragSortComponents.d.ts +14 -0
  351. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +13 -0
  352. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +39 -0
  353. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +37 -0
  354. package/es/flow/models/blocks/table/dragSort/index.d.ts +12 -0
  355. package/es/flow/models/blocks/table/index.d.ts +16 -0
  356. package/es/flow/models/blocks/table/sortUtils.d.ts +32 -0
  357. package/es/flow/models/blocks/table/utils.d.ts +18 -0
  358. package/es/flow/models/blocks/utils/transformChildrenToJS.d.ts +42 -0
  359. package/es/flow/models/fields/AssociationFieldModel/AssociationFieldModel.d.ts +5 -0
  360. package/es/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.d.ts +45 -0
  361. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableActionGroupModel.d.ts +11 -0
  362. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableActionsColumnModel.d.ts +19 -0
  363. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.d.ts +30 -0
  364. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.d.ts +29 -0
  365. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableFormSubmitActionModel.d.ts +18 -0
  366. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableRemoveActionModel.d.ts +14 -0
  367. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/popupSubTableBeforeClose.d.ts +25 -0
  368. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/blocks/PopupSubTableFormModel.d.ts +36 -0
  369. package/es/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/index.d.ts +17 -0
  370. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +45 -0
  371. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +46 -0
  372. package/es/flow/models/fields/AssociationFieldModel/SubFormFieldModel.d.ts +32 -0
  373. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +32 -0
  374. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.d.ts +10 -0
  375. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +19 -0
  376. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/rowIdentity.d.ts +18 -0
  377. package/{lib/utils → es/flow/models/fields/AssociationFieldModel}/index.d.ts +7 -3
  378. package/es/flow/models/fields/AssociationFieldModel/itemChain.d.ts +99 -0
  379. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +48 -0
  380. package/es/flow/models/fields/CheckboxFieldModel.d.ts +13 -0
  381. package/es/flow/models/fields/CheckboxGroupFieldModel.d.ts +13 -0
  382. package/es/flow/models/fields/ClickableFieldModel.d.ts +25 -0
  383. package/es/flow/models/fields/CollectionSelectorFieldModel.d.ts +13 -0
  384. package/es/flow/models/fields/ColorFieldModel.d.ts +14 -0
  385. package/es/flow/models/fields/DateTimeFieldModel/DateOnlyFieldModel.d.ts +7 -0
  386. package/es/flow/models/fields/DateTimeFieldModel/DateTimeFieldModel.d.ts +3 -0
  387. package/es/flow/models/fields/DateTimeFieldModel/DateTimeNoTzFieldModel.d.ts +6 -0
  388. package/es/flow/models/fields/DateTimeFieldModel/DateTimeTzFieldModel.d.ts +7 -0
  389. package/es/flow/models/fields/DateTimeFieldModel/index.d.ts +11 -0
  390. package/es/flow/models/fields/DisplayAssociationField/DisplaySubItemFieldModel.d.ts +26 -0
  391. package/es/flow/models/fields/DisplayAssociationField/DisplaySubListFieldModel.d.ts +26 -0
  392. package/es/flow/models/fields/DisplayAssociationField/DisplaySubTableFieldModel.d.ts +20 -0
  393. package/es/flow/models/fields/DisplayAssociationField/displaySubListUtils.d.ts +14 -0
  394. package/es/flow/models/fields/DisplayAssociationField/index.d.ts +11 -0
  395. package/es/flow/models/fields/DisplayCheckboxFieldModel.d.ts +13 -0
  396. package/es/flow/models/fields/DisplayColorFieldModel.d.ts +13 -0
  397. package/es/flow/models/fields/DisplayDateTimeFieldModel.d.ts +13 -0
  398. package/es/flow/models/fields/DisplayEnumFieldModel.d.ts +14 -0
  399. package/es/flow/models/fields/DisplayHtmlFieldModel.d.ts +13 -0
  400. package/es/flow/models/fields/DisplayIconFieldModel.d.ts +13 -0
  401. package/es/flow/models/fields/DisplayJSONFieldModel.d.ts +13 -0
  402. package/es/flow/models/fields/DisplayNumberFieldModel.d.ts +33 -0
  403. package/es/flow/models/fields/DisplayPasswordFieldModel.d.ts +13 -0
  404. package/es/flow/models/fields/DisplayPercentFieldModel.d.ts +13 -0
  405. package/es/flow/models/fields/DisplayTextFieldModel.d.ts +14 -0
  406. package/es/flow/models/fields/DisplayTimeFieldModel.d.ts +13 -0
  407. package/es/flow/models/fields/DisplayTitleFieldModel.d.ts +18 -0
  408. package/es/flow/models/fields/DisplayURLFieldModel.d.ts +13 -0
  409. package/es/flow/models/fields/DividerItemModel.d.ts +13 -0
  410. package/es/flow/models/fields/IconFieldModel.d.ts +13 -0
  411. package/es/flow/models/fields/InputFieldModel.d.ts +13 -0
  412. package/es/flow/models/fields/JSEditableFieldModel.d.ts +27 -0
  413. package/es/flow/models/fields/JSFieldModel.d.ts +45 -0
  414. package/es/flow/models/fields/JSItemModel.d.ts +27 -0
  415. package/es/flow/models/fields/JsonFieldModel.d.ts +6 -0
  416. package/es/flow/models/fields/MarkdownItemModel.d.ts +14 -0
  417. package/es/flow/models/fields/NumberFieldModel.d.ts +15 -0
  418. package/es/flow/models/fields/PasswordFieldModel.d.ts +13 -0
  419. package/es/flow/models/fields/PercentFieldModel.d.ts +14 -0
  420. package/es/flow/models/fields/RadioGroupFieldModel.d.ts +13 -0
  421. package/es/flow/models/fields/RichTextFieldModel/index.d.ts +14 -0
  422. package/es/flow/models/fields/RichTextFieldModel/registerFontSize.d.ts +9 -0
  423. package/es/flow/models/fields/RichTextFieldModel/registerImageResize.d.ts +9 -0
  424. package/es/flow/models/fields/RichTextFieldModel/registerSmartBreak.d.ts +12 -0
  425. package/es/flow/models/fields/RichTextFieldModel/style.d.ts +9 -0
  426. package/es/flow/models/fields/SelectFieldModel.d.ts +13 -0
  427. package/es/flow/models/fields/TextareaFieldModel.d.ts +13 -0
  428. package/es/flow/models/fields/TimeFieldModel.d.ts +5 -0
  429. package/es/flow/models/fields/VariableFieldFormModel.d.ts +18 -0
  430. package/es/flow/models/fields/index.d.ts +47 -0
  431. package/es/flow/models/fields/mobile-components/MobileDatePicker.d.ts +10 -0
  432. package/es/flow/models/fields/mobile-components/MobileLazySelect.d.ts +11 -0
  433. package/es/flow/models/fields/mobile-components/MobileSelect.d.ts +10 -0
  434. package/es/flow/models/fields/mobile-components/MobileTimePicker.d.ts +10 -0
  435. package/es/flow/models/index.d.ts +13 -0
  436. package/es/flow/models/topbar/TopbarActionModel.d.ts +114 -0
  437. package/es/flow/models/topbar/UserCenterTopbarActionModel.d.ts +68 -0
  438. package/es/flow/models/topbar/index.d.ts +10 -0
  439. package/es/flow/models/utils/resolveRunJsParams.d.ts +19 -0
  440. package/es/flow/resolveViewParamsToViewList.d.ts +20 -0
  441. package/es/flow/system-settings/SystemSettingsSource.d.ts +97 -0
  442. package/es/flow/system-settings/index.d.ts +10 -0
  443. package/es/flow/system-settings/useSystemSettings.d.ts +30 -0
  444. package/es/flow/utils/actionCapability.d.ts +60 -0
  445. package/es/flow/utils/blockUtils.d.ts +16 -0
  446. package/es/flow/utils/dispatchEventDeep.d.ts +20 -0
  447. package/es/flow/utils/index.d.ts +11 -0
  448. package/es/flow/utils/pagination.d.ts +29 -0
  449. package/es/flow/utils/useJsonTemplateResolver.d.ts +9 -0
  450. package/es/flow-compat/ColorPicker.d.ts +15 -0
  451. package/es/flow-compat/FieldValidation.d.ts +31 -0
  452. package/es/flow-compat/HighPerformanceSpin.d.ts +11 -0
  453. package/es/flow-compat/Icon.d.ts +10 -0
  454. package/es/flow-compat/IconPicker.d.ts +19 -0
  455. package/es/flow-compat/Password.d.ts +16 -0
  456. package/es/flow-compat/PasswordStrength.d.ts +16 -0
  457. package/es/flow-compat/Popover.d.ts +12 -0
  458. package/es/flow-compat/data.d.ts +15 -0
  459. package/es/flow-compat/date.d.ts +114 -0
  460. package/es/flow-compat/fieldValidationConstants.d.ts +383 -0
  461. package/es/flow-compat/index.d.ts +27 -0
  462. package/es/flow-compat/lazy.d.ts +17 -0
  463. package/es/flow-compat/operators.d.ts +582 -0
  464. package/{lib/hooks/useRouter.d.ts → es/flow-compat/passwordUtils.d.ts} +1 -1
  465. package/es/flow-compat/routeTypes.d.ts +56 -0
  466. package/es/flow-compat/useFullscreenOverlay.d.ts +20 -0
  467. package/es/flow-compat/zIndexContext.d.ts +12 -0
  468. package/es/hooks/useApp.d.ts +4 -2
  469. package/es/hooks/usePlugin.d.ts +2 -2
  470. package/es/hooks/useRouter.d.ts +1 -1
  471. package/es/i18n.d.ts +22 -0
  472. package/es/index.d.ts +19 -0
  473. package/es/index.mjs +1019 -0
  474. package/es/index.mjs.LICENSE.txt +1 -0
  475. package/es/locale/languageCodes.d.ts +13 -0
  476. package/es/nocobase-buildin-plugin/index.d.ts +24 -0
  477. package/es/nocobase-buildin-plugin/plugins/LocalePlugin.d.ts +15 -0
  478. package/es/nocobase-buildin-plugin/plugins/dayjsLocale.d.ts +76 -0
  479. package/es/nocobase-buildin-plugin/plugins/loadConstrueLocale.d.ts +9 -0
  480. package/es/settings-center/AdminSettingsLayout.d.ts +24 -0
  481. package/es/settings-center/AdminSettingsLayoutModel.d.ts +28 -0
  482. package/es/settings-center/PluginManagerPage.d.ts +17 -0
  483. package/es/settings-center/SystemSettingsPage.d.ts +17 -0
  484. package/es/settings-center/index.d.ts +13 -0
  485. package/es/settings-center/utils.d.ts +94 -0
  486. package/es/theme/AntdAppProvider.d.ts +18 -0
  487. package/es/theme/compatOldTheme.d.ts +11 -0
  488. package/es/theme/customAlgorithm.d.ts +12 -0
  489. package/es/theme/defaultTheme.d.ts +11 -0
  490. package/es/theme/index.d.ts +35 -0
  491. package/es/theme/type.d.ts +41 -0
  492. package/es/utils/index.d.ts +1 -1
  493. package/es/utils/remotePlugins.d.ts +6 -6
  494. package/lib/index.js +1019 -40
  495. package/lib/index.js.LICENSE.txt +1 -0
  496. package/lib/locale/languageCodes.js +96 -0
  497. package/package.json +28 -5
  498. package/src/Application.tsx +76 -432
  499. package/src/BaseApplication.tsx +648 -0
  500. package/src/PinnedPluginListContext.tsx +33 -0
  501. package/src/Plugin.ts +24 -8
  502. package/src/PluginManager.ts +31 -19
  503. package/src/PluginSettingsManager.ts +504 -100
  504. package/src/RouteRepository.ts +247 -0
  505. package/src/RouterManager.tsx +15 -16
  506. package/src/WebSocketClient.ts +4 -4
  507. package/src/__tests__/PluginSettingsManager.test.ts +228 -0
  508. package/src/__tests__/app.test.tsx +216 -34
  509. package/src/__tests__/authRedirect.test.ts +181 -0
  510. package/src/__tests__/dataSourceRuntime.test.tsx +34 -0
  511. package/src/__tests__/globalDeps.test.ts +1 -0
  512. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +185 -0
  513. package/src/__tests__/settings-center.test.tsx +363 -0
  514. package/src/acl/ACLProvider.tsx +284 -0
  515. package/src/acl/__tests__/createAclSnippetAllow.test.ts +42 -0
  516. package/src/acl/createAclSnippetAllow.ts +33 -0
  517. package/src/acl/index.ts +12 -0
  518. package/src/acl/useAclSnippets.ts +25 -0
  519. package/src/ai/ai-manager.ts +19 -0
  520. package/src/ai/index.ts +13 -0
  521. package/src/ai/skills-manager/index.ts +10 -0
  522. package/src/ai/skills-manager/types.ts +26 -0
  523. package/src/ai/tools-manager/hooks/context.ts +20 -0
  524. package/src/ai/tools-manager/hooks/index.ts +22 -0
  525. package/src/ai/tools-manager/hooks/provider.tsx +53 -0
  526. package/src/ai/tools-manager/index.ts +42 -0
  527. package/src/ai/tools-manager/types.ts +106 -0
  528. package/src/ai/utils.ts +12 -0
  529. package/src/authRedirect.ts +297 -0
  530. package/src/collection-field-interface/CollectionFieldInterface.ts +200 -0
  531. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +177 -0
  532. package/src/collection-manager/interfaces/checkbox.ts +41 -0
  533. package/src/collection-manager/interfaces/checkboxGroup.ts +45 -0
  534. package/src/collection-manager/interfaces/collection.ts +53 -0
  535. package/src/collection-manager/interfaces/color.ts +34 -0
  536. package/src/collection-manager/interfaces/createdAt.ts +41 -0
  537. package/src/collection-manager/interfaces/createdBy.ts +80 -0
  538. package/src/collection-manager/interfaces/dateOnly.ts +48 -0
  539. package/src/collection-manager/interfaces/datetime.ts +66 -0
  540. package/src/collection-manager/interfaces/datetimeNoTz.ts +66 -0
  541. package/src/collection-manager/interfaces/email.ts +47 -0
  542. package/src/collection-manager/interfaces/icon.ts +31 -0
  543. package/src/collection-manager/interfaces/id.ts +61 -0
  544. package/src/collection-manager/interfaces/index.ts +51 -0
  545. package/src/collection-manager/interfaces/input.ts +224 -0
  546. package/src/collection-manager/interfaces/integer.ts +135 -0
  547. package/src/collection-manager/interfaces/json.tsx +74 -0
  548. package/src/collection-manager/interfaces/linkTo.ts +120 -0
  549. package/src/collection-manager/interfaces/m2m.tsx +262 -0
  550. package/src/collection-manager/interfaces/m2o.tsx +196 -0
  551. package/src/collection-manager/interfaces/markdown.ts +84 -0
  552. package/src/collection-manager/interfaces/multipleSelect.ts +52 -0
  553. package/src/collection-manager/interfaces/nanoid.ts +82 -0
  554. package/src/collection-manager/interfaces/number.ts +173 -0
  555. package/src/collection-manager/interfaces/o2m.tsx +217 -0
  556. package/src/collection-manager/interfaces/o2o.tsx +561 -0
  557. package/src/collection-manager/interfaces/password.ts +80 -0
  558. package/src/collection-manager/interfaces/percent.ts +178 -0
  559. package/src/collection-manager/interfaces/phone.ts +43 -0
  560. package/src/collection-manager/interfaces/properties/index.ts +582 -0
  561. package/src/collection-manager/interfaces/properties/operators.ts +352 -0
  562. package/src/collection-manager/interfaces/radioGroup.ts +37 -0
  563. package/src/collection-manager/interfaces/richText.ts +84 -0
  564. package/src/collection-manager/interfaces/select.ts +50 -0
  565. package/src/collection-manager/interfaces/snowflake-id.ts +61 -0
  566. package/src/collection-manager/interfaces/subTable.ts +218 -0
  567. package/src/collection-manager/interfaces/tableoid.ts +53 -0
  568. package/src/collection-manager/interfaces/textarea.ts +93 -0
  569. package/src/collection-manager/interfaces/time.ts +37 -0
  570. package/src/collection-manager/interfaces/types.ts +41 -0
  571. package/src/collection-manager/interfaces/unixTimestamp.tsx +68 -0
  572. package/src/collection-manager/interfaces/updatedAt.ts +41 -0
  573. package/src/collection-manager/interfaces/updatedBy.ts +79 -0
  574. package/src/collection-manager/interfaces/url.ts +48 -0
  575. package/src/collection-manager/interfaces/utils/index.ts +20 -0
  576. package/src/collection-manager/interfaces/uuid.ts +72 -0
  577. package/src/components/AppComponents.tsx +245 -0
  578. package/src/components/Icon.tsx +68 -0
  579. package/src/components/index.ts +2 -0
  580. package/src/css-variable/CSSVariableProvider.tsx +108 -0
  581. package/src/flow/FlowModelRepository.ts +222 -0
  582. package/src/flow/FlowPage.tsx +118 -0
  583. package/src/flow/__tests__/FlowModelRepository.test.ts +61 -0
  584. package/src/flow/__tests__/FlowRoute.test.tsx +474 -0
  585. package/src/flow/__tests__/getKey.test.ts +61 -0
  586. package/src/flow/__tests__/getViewDiff.test.ts +180 -0
  587. package/src/flow/__tests__/helpers/mockFlowApp.ts +49 -0
  588. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +278 -0
  589. package/src/flow/actions/__tests__/aclCheckRefresh.test.ts +129 -0
  590. package/src/flow/actions/__tests__/dataLoadingMode.test.tsx +69 -0
  591. package/src/flow/actions/__tests__/dataScope.leftMetaTree.test.tsx +192 -0
  592. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +538 -0
  593. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +470 -0
  594. package/src/flow/actions/__tests__/linkageRules.assignFieldOptions.test.tsx +56 -0
  595. package/src/flow/actions/__tests__/linkageRules.runjsTemplateResolution.test.ts +71 -0
  596. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +109 -0
  597. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +164 -0
  598. package/src/flow/actions/__tests__/openView.beforeClose.test.tsx +263 -0
  599. package/src/flow/actions/__tests__/openView.closeDestroy.test.tsx +95 -0
  600. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +152 -0
  601. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +95 -0
  602. package/src/flow/actions/__tests__/setTargetDataScope.leftMetaTree.test.tsx +225 -0
  603. package/src/flow/actions/__tests__/showMessageNotification.test.ts +79 -0
  604. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +173 -0
  605. package/src/flow/actions/aclCheck.tsx +46 -0
  606. package/src/flow/actions/aclCheckRefresh.tsx +168 -0
  607. package/src/flow/actions/afterSuccess.tsx +138 -0
  608. package/src/flow/actions/blockHeight.tsx +77 -0
  609. package/src/flow/actions/columnFixed.tsx +35 -0
  610. package/src/flow/actions/confirm.tsx +63 -0
  611. package/src/flow/actions/customVariable.tsx +399 -0
  612. package/src/flow/actions/dataLoadingMode.tsx +61 -0
  613. package/src/flow/actions/dataScope.tsx +61 -0
  614. package/src/flow/actions/dateTimeFormat.tsx +188 -0
  615. package/src/flow/actions/displayFieldComponent.tsx +103 -0
  616. package/src/flow/actions/fieldComponent.tsx +114 -0
  617. package/src/flow/actions/filterFormDefaultValues.tsx +134 -0
  618. package/src/flow/actions/formAssignRules.tsx +117 -0
  619. package/src/flow/actions/index.ts +55 -0
  620. package/src/flow/actions/layout.tsx +107 -0
  621. package/src/flow/actions/linkageRules.tsx +2584 -0
  622. package/src/flow/actions/linkageRulesRefresh.tsx +82 -0
  623. package/src/flow/actions/navigateToURL.tsx +142 -0
  624. package/src/flow/actions/numberFormat.tsx +145 -0
  625. package/src/flow/actions/openView.tsx +512 -0
  626. package/src/flow/actions/overflowMode.tsx +36 -0
  627. package/src/flow/actions/pattern.tsx +122 -0
  628. package/src/flow/actions/refreshTargetBlocks.tsx +91 -0
  629. package/src/flow/actions/renderMode.tsx +42 -0
  630. package/src/flow/actions/required.tsx +70 -0
  631. package/src/flow/actions/runjs.tsx +48 -0
  632. package/src/flow/actions/setTargetDataScope.tsx +111 -0
  633. package/src/flow/actions/showMessage.tsx +155 -0
  634. package/src/flow/actions/showNotification.tsx +179 -0
  635. package/src/flow/actions/sortingRules.tsx +125 -0
  636. package/src/flow/actions/titleField.tsx +141 -0
  637. package/src/flow/actions/validation.tsx +77 -0
  638. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +339 -0
  639. package/src/flow/admin-shell/admin-layout/AdminLayoutCompat.tsx +326 -0
  640. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +681 -0
  641. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +338 -0
  642. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +774 -0
  643. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +964 -0
  644. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +330 -0
  645. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +127 -0
  646. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +193 -0
  647. package/src/flow/admin-shell/admin-layout/PinnedPluginListLite.tsx +129 -0
  648. package/src/flow/admin-shell/admin-layout/ResetThemeTokenAndKeepAlgorithm.tsx +30 -0
  649. package/src/flow/admin-shell/admin-layout/TopbarActionsBar.tsx +211 -0
  650. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +2023 -0
  651. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +284 -0
  652. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +211 -0
  653. package/src/flow/admin-shell/admin-layout/__tests__/flowSettingsPreference.test.ts +44 -0
  654. package/src/flow/admin-shell/admin-layout/constants.ts +10 -0
  655. package/src/flow/admin-shell/admin-layout/flowSettingsPreference.ts +54 -0
  656. package/src/flow/admin-shell/admin-layout/index.ts +20 -0
  657. package/src/flow/admin-shell/admin-layout/mobile-layout.tsx +35 -0
  658. package/src/flow/admin-shell/admin-layout/mobileMenuNavigation.ts +30 -0
  659. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +214 -0
  660. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +260 -0
  661. package/src/flow/admin-shell/admin-layout/useApplications.tsx +15 -0
  662. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +56 -0
  663. package/src/flow/common/Liquid.tsx +148 -0
  664. package/src/flow/common/Markdown/Display.tsx +213 -0
  665. package/src/flow/common/Markdown/Edit.tsx +437 -0
  666. package/src/flow/common/Markdown/Markdown.tsx +44 -0
  667. package/src/flow/common/Markdown/style.ts +53 -0
  668. package/src/flow/common/Markdown/useCDN.ts +21 -0
  669. package/src/flow/components/AdminLayout.tsx +167 -0
  670. package/src/flow/components/BlockItemCard.tsx +204 -0
  671. package/src/flow/components/ConditionBuilder.tsx +40 -0
  672. package/src/flow/components/DefaultValue.tsx +732 -0
  673. package/src/flow/components/DynamicFlowsIcon.tsx +764 -0
  674. package/src/flow/components/EllipsisWithTooltip.tsx +106 -0
  675. package/src/flow/components/ExpiresRadio/index.tsx +139 -0
  676. package/src/flow/components/FieldAssignEditor.tsx +80 -0
  677. package/src/flow/components/FieldAssignExactDatePicker.tsx +225 -0
  678. package/src/flow/components/FieldAssignRulesEditor.tsx +1013 -0
  679. package/src/flow/components/FieldAssignValueInput.tsx +1490 -0
  680. package/src/flow/components/FlowRoute.tsx +173 -0
  681. package/src/flow/components/Grid/index.tsx +158 -0
  682. package/src/flow/components/JsonEditor.tsx +53 -0
  683. package/src/flow/components/RunJSValueEditor.tsx +50 -0
  684. package/src/flow/components/SkeletonFallback.tsx +19 -0
  685. package/src/flow/components/TextAreaWithContextSelector.tsx +139 -0
  686. package/src/flow/components/__tests__/DefaultValue.test.tsx +1289 -0
  687. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +193 -0
  688. package/src/flow/components/__tests__/FieldAssignExactDatePicker.test.tsx +121 -0
  689. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +758 -0
  690. package/src/flow/components/__tests__/FieldAssignValueInput.exactDate.test.tsx +180 -0
  691. package/src/flow/components/__tests__/FieldAssignValueInput.path.test.ts +298 -0
  692. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +97 -0
  693. package/src/flow/components/code-editor/__tests__/SnippetsDrawer.test.tsx +51 -0
  694. package/src/flow/components/code-editor/__tests__/errorHelpers.test.ts +37 -0
  695. package/src/flow/components/code-editor/__tests__/javascriptHtmlTemplate.test.ts +101 -0
  696. package/src/flow/components/code-editor/__tests__/linter.test.ts +106 -0
  697. package/src/flow/components/code-editor/__tests__/resolveScenes.test.ts +44 -0
  698. package/src/flow/components/code-editor/__tests__/runjsCompletionSource.test.ts +251 -0
  699. package/src/flow/components/code-editor/__tests__/runjsCompletions.test.ts +245 -0
  700. package/src/flow/components/code-editor/__tests__/runjsDiagnostics.test.ts +233 -0
  701. package/src/flow/components/code-editor/__tests__/runjsLocales.test.ts +24 -0
  702. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +273 -0
  703. package/src/flow/components/code-editor/core/EditorCore.tsx +216 -0
  704. package/src/flow/components/code-editor/core/tooltipParent.ts +28 -0
  705. package/src/flow/components/code-editor/errorHelpers.ts +92 -0
  706. package/src/flow/components/code-editor/extension/CodeEditorExtension.tsx +23 -0
  707. package/src/flow/components/code-editor/extension/index.ts +10 -0
  708. package/src/flow/components/code-editor/formatDocInfo.ts +98 -0
  709. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +333 -0
  710. package/src/flow/components/code-editor/hooks/useRunJSDocCompletions.ts +41 -0
  711. package/src/flow/components/code-editor/htmlCompletion.ts +155 -0
  712. package/src/flow/components/code-editor/index.tsx +286 -0
  713. package/src/flow/components/code-editor/javascriptCompletion.ts +121 -0
  714. package/src/flow/components/code-editor/javascriptHtmlTemplate.ts +68 -0
  715. package/src/flow/components/code-editor/jsxCompletion.ts +244 -0
  716. package/src/flow/components/code-editor/linter.ts +885 -0
  717. package/src/flow/components/code-editor/panels/LogsPanel.tsx +68 -0
  718. package/src/flow/components/code-editor/panels/RightExtra.tsx +71 -0
  719. package/src/flow/components/code-editor/panels/SnippetsDrawer.tsx +104 -0
  720. package/src/flow/components/code-editor/resolveScenes.ts +38 -0
  721. package/src/flow/components/code-editor/runjsCompletionSource.ts +456 -0
  722. package/src/flow/components/code-editor/runjsCompletions.ts +306 -0
  723. package/src/flow/components/code-editor/runjsDiagnostics.ts +1077 -0
  724. package/src/flow/components/code-editor/types.ts +36 -0
  725. package/src/flow/components/drag-drop/Sortable.tsx +48 -0
  726. package/src/flow/components/drag-drop/SortableItem.tsx +28 -0
  727. package/src/flow/components/drag-drop/index.ts +11 -0
  728. package/src/flow/components/fieldAssignOptions.ts +212 -0
  729. package/src/flow/components/filter/FilterContainer.tsx +123 -0
  730. package/src/flow/components/filter/FilterGroup.tsx +320 -0
  731. package/src/flow/components/filter/FilterItem.tsx +144 -0
  732. package/src/flow/components/filter/LinkageFilterItem.tsx +511 -0
  733. package/src/flow/components/filter/VariableFilterItem.tsx +684 -0
  734. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +143 -0
  735. package/src/flow/components/filter/__tests__/LinkageFilterItem.metaTree.test.ts +167 -0
  736. package/src/flow/components/filter/__tests__/LinkageFilterItem.test.tsx +301 -0
  737. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +196 -0
  738. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +406 -0
  739. package/src/flow/components/filter/fieldsToOptions.ts +71 -0
  740. package/src/flow/components/filter/index.ts +17 -0
  741. package/src/flow/components/index.tsx +16 -0
  742. package/src/flow/components/placeholders/BlockPlaceholder.tsx +68 -0
  743. package/src/flow/components/placeholders/FieldPlaceholder.tsx +32 -0
  744. package/src/flow/components/useAssociationTitleFieldSync.ts +49 -0
  745. package/src/flow/flows/editMarkdownFlow.tsx +143 -0
  746. package/src/flow/flows/openViewFlow.ts +89 -0
  747. package/src/flow/formily/ReactiveField.tsx +106 -0
  748. package/src/flow/formily/index.tsx +8 -0
  749. package/src/flow/getViewDiffAndUpdateHidden.tsx +86 -0
  750. package/src/flow/index.ts +108 -0
  751. package/src/flow/internal/components/Markdown/DisplayMarkdown.tsx +85 -0
  752. package/src/flow/internal/components/Markdown/markdown-it-plugins/mermaidPlugin.ts +85 -0
  753. package/src/flow/internal/components/Markdown/md.ts +24 -0
  754. package/src/flow/internal/components/Markdown/style.ts +165 -0
  755. package/src/flow/internal/components/Markdown/util.ts +45 -0
  756. package/src/flow/internal/constants/HeightMode.ts +14 -0
  757. package/src/flow/internal/index.ts +16 -0
  758. package/src/flow/internal/utils/__tests__/associationValueCoercion.test.ts +57 -0
  759. package/src/flow/internal/utils/__tests__/enumOptionsUtils.test.ts +58 -0
  760. package/src/flow/internal/utils/__tests__/modelUtils.test.ts +66 -0
  761. package/src/flow/internal/utils/__tests__/rebuildFieldSubModel.test.ts +109 -0
  762. package/src/flow/internal/utils/__tests__/titleFieldQuickSync.test.ts +93 -0
  763. package/src/flow/internal/utils/associationValueCoercion.ts +29 -0
  764. package/src/flow/internal/utils/dateTimeUtils.ts +53 -0
  765. package/src/flow/internal/utils/enumOptionsUtils.ts +66 -0
  766. package/src/flow/internal/utils/modelUtils.ts +158 -0
  767. package/src/flow/internal/utils/operatorSchemaHelper.ts +30 -0
  768. package/src/flow/internal/utils/rebuildFieldSubModel.ts +94 -0
  769. package/src/flow/internal/utils/saveStepParamsWithSubModels.ts +40 -0
  770. package/src/flow/internal/utils/titleFieldQuickSync.ts +95 -0
  771. package/src/flow/models/actions/AddChildActionModel.tsx +69 -0
  772. package/src/flow/models/actions/AddNewActionModel.tsx +32 -0
  773. package/src/flow/models/actions/BulkDeleteActionModel.tsx +61 -0
  774. package/src/flow/models/actions/DeleteActionModel.tsx +61 -0
  775. package/src/flow/models/actions/EditActionModel.tsx +31 -0
  776. package/src/flow/models/actions/ExpandCollapseActionModel.tsx +113 -0
  777. package/src/flow/models/actions/FilterActionModel.tsx +320 -0
  778. package/src/flow/models/actions/JSActionModel.tsx +84 -0
  779. package/src/flow/models/actions/JSCollectionActionModel.tsx +90 -0
  780. package/src/flow/models/actions/JSItemActionModel.tsx +166 -0
  781. package/src/flow/models/actions/JSRecordActionModel.tsx +90 -0
  782. package/src/flow/models/actions/LinkActionModel.tsx +198 -0
  783. package/src/flow/models/actions/LinkActionUtils.ts +65 -0
  784. package/src/flow/models/actions/PopupCollectionActionModel.tsx +25 -0
  785. package/src/flow/models/actions/RefreshActionModel.tsx +45 -0
  786. package/src/flow/models/actions/UpdateRecordActionModel.tsx +201 -0
  787. package/src/flow/models/actions/UpdateRecordActionUtils.ts +89 -0
  788. package/src/flow/models/actions/ViewActionModel.tsx +31 -0
  789. package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +55 -0
  790. package/src/flow/models/actions/__tests__/JSItemActionModel.test.tsx +85 -0
  791. package/src/flow/models/actions/__tests__/LinkActionModel.test.ts +110 -0
  792. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +72 -0
  793. package/src/flow/models/actions/index.ts +26 -0
  794. package/src/flow/models/actions/joinUrlSearch.ts +42 -0
  795. package/src/flow/models/base/ActionGroupModel.tsx +10 -0
  796. package/src/flow/models/base/ActionGroupModelCore.tsx +117 -0
  797. package/src/flow/models/base/ActionModel.tsx +141 -0
  798. package/src/flow/models/base/ActionModelCore.tsx +174 -0
  799. package/src/flow/models/base/AssociationFieldGroupModel.tsx +100 -0
  800. package/src/flow/models/base/BlockGridModel.tsx +144 -0
  801. package/src/flow/models/base/BlockModel.tsx +247 -0
  802. package/src/flow/models/base/CollectionActionModel.tsx +23 -0
  803. package/src/flow/models/base/CollectionBlockModel.tsx +703 -0
  804. package/src/flow/models/base/CommonItemModel.tsx +12 -0
  805. package/src/flow/models/base/DataBlockModel.tsx +41 -0
  806. package/src/flow/models/base/FieldModel.tsx +26 -0
  807. package/src/flow/models/base/FilterBlockModel.tsx +15 -0
  808. package/src/flow/models/base/GridModel.tsx +1053 -0
  809. package/src/flow/models/base/PageModel/ChildPageModel.tsx +114 -0
  810. package/src/flow/models/base/PageModel/MainPageModel.tsx +32 -0
  811. package/src/flow/models/base/PageModel/OldPageModel.tsx +97 -0
  812. package/src/flow/models/base/PageModel/PageModel.tsx +486 -0
  813. package/src/flow/models/base/PageModel/PageTabModel.tsx +259 -0
  814. package/src/flow/models/base/PageModel/RootPageModel.tsx +161 -0
  815. package/src/flow/models/base/PageModel/SubPageModel.tsx +32 -0
  816. package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +196 -0
  817. package/src/flow/models/base/PageModel/__tests__/PageModel.beforeCloseGuard.test.ts +111 -0
  818. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +753 -0
  819. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +225 -0
  820. package/src/flow/models/base/PageModel/__tests__/RootPageModel.test.ts +242 -0
  821. package/src/flow/models/base/PageModel/closeGuard.ts +48 -0
  822. package/src/flow/models/base/PageModel/index.tsx +16 -0
  823. package/src/flow/models/base/PopupActionModel.tsx +26 -0
  824. package/src/flow/models/base/RecordActionModel.tsx +23 -0
  825. package/src/flow/models/base/RouteModel.tsx +29 -0
  826. package/src/flow/models/base/__tests__/ActionGroupModel.test.ts +200 -0
  827. package/src/flow/models/base/__tests__/BlockGridModel.newScene.test.ts +37 -0
  828. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneAddBlock.test.ts +83 -0
  829. package/src/flow/models/base/__tests__/CollectionBlockModel.capability.test.ts +96 -0
  830. package/src/flow/models/base/__tests__/CollectionBlockModel.dirtyRefresh.association.test.ts +110 -0
  831. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +131 -0
  832. package/src/flow/models/base/__tests__/CollectionBlockModel.newScene.associatedRecordsVisibility.test.ts +161 -0
  833. package/src/flow/models/base/__tests__/CollectionBlockModel.title.test.ts +95 -0
  834. package/src/flow/models/base/__tests__/GridModel.computeOverlayRect.test.ts +423 -0
  835. package/src/flow/models/base/__tests__/GridModel.dragOverlay.test.ts +129 -0
  836. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +107 -0
  837. package/src/flow/models/base/__tests__/GridModel.mergeRows.test.ts +107 -0
  838. package/src/flow/models/base/__tests__/GridModel.recalculateGridSizes.test.ts +67 -0
  839. package/src/flow/models/base/__tests__/GridModel.visibleLayout.test.ts +217 -0
  840. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +58 -0
  841. package/src/flow/models/base/index.ts +26 -0
  842. package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +161 -0
  843. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +409 -0
  844. package/src/flow/models/blocks/assign-form/AssignFormModel.tsx +96 -0
  845. package/src/flow/models/blocks/assign-form/index.ts +12 -0
  846. package/src/flow/models/blocks/details/DetailsAssociationFieldGroupModel.tsx +18 -0
  847. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +404 -0
  848. package/src/flow/models/blocks/details/DetailsCustomItemModel.tsx +51 -0
  849. package/src/flow/models/blocks/details/DetailsGridModel.tsx +106 -0
  850. package/src/flow/models/blocks/details/DetailsItemModel.tsx +384 -0
  851. package/src/flow/models/blocks/details/DetailsJSFieldItemModel.tsx +37 -0
  852. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.blockHeight.test.tsx +139 -0
  853. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +128 -0
  854. package/src/flow/models/blocks/details/__tests__/DetailsItemModel.value.test.ts +49 -0
  855. package/src/flow/models/blocks/details/index.ts +16 -0
  856. package/src/flow/models/blocks/details/utils.ts +67 -0
  857. package/src/flow/models/blocks/filter-form/FieldModelSelect.tsx +59 -0
  858. package/src/flow/models/blocks/filter-form/FieldOperatorSelect.tsx +109 -0
  859. package/src/flow/models/blocks/filter-form/FilterFormActionGroupModel.tsx +15 -0
  860. package/src/flow/models/blocks/filter-form/FilterFormActionModel.tsx +12 -0
  861. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +334 -0
  862. package/src/flow/models/blocks/filter-form/FilterFormCollapseActionModel.tsx +132 -0
  863. package/src/flow/models/blocks/filter-form/FilterFormCustomItemModel.tsx +42 -0
  864. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +218 -0
  865. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +780 -0
  866. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +72 -0
  867. package/src/flow/models/blocks/filter-form/FilterFormResetActionModel.tsx +52 -0
  868. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +49 -0
  869. package/src/flow/models/blocks/filter-form/SourceCascader.tsx +137 -0
  870. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +247 -0
  871. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +143 -0
  872. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +329 -0
  873. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +117 -0
  874. package/src/flow/models/blocks/filter-form/__tests__/customFieldOperators.test.tsx +642 -0
  875. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +19 -0
  876. package/src/flow/models/blocks/filter-form/__tests__/legacyDefaultValueMigration.test.ts +107 -0
  877. package/src/flow/models/blocks/filter-form/__tests__/valueNormalization.test.ts +50 -0
  878. package/src/flow/models/blocks/filter-form/customFieldOperators.ts +225 -0
  879. package/src/flow/models/blocks/filter-form/fields/FieldComponentProps.tsx +474 -0
  880. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +411 -0
  881. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomRecordSelectFieldModel.tsx +543 -0
  882. package/src/flow/models/blocks/filter-form/fields/FilterFormFieldModel.tsx +12 -0
  883. package/src/flow/models/blocks/filter-form/fields/FilterFormRecordSelectFieldModel.tsx +85 -0
  884. package/src/flow/models/blocks/filter-form/fields/__tests__/FilterFormCustomFieldModel.recordSelect.test.tsx +633 -0
  885. package/src/flow/models/blocks/filter-form/fields/date-time/DateOnlyFilterFieldModel.tsx +42 -0
  886. package/src/flow/models/blocks/filter-form/fields/date-time/DateTimeFilterFieldModel.tsx +17 -0
  887. package/src/flow/models/blocks/filter-form/fields/date-time/DateTimeNoTzFilterFieldModel.tsx +30 -0
  888. package/src/flow/models/blocks/filter-form/fields/date-time/DateTimeTzFilterFieldModel.tsx +48 -0
  889. package/src/flow/models/blocks/filter-form/fields/date-time/components/DateFilterDynamicComponent.tsx +184 -0
  890. package/src/flow/models/blocks/filter-form/fields/date-time/components/FilterDatePicker.tsx +108 -0
  891. package/src/flow/models/blocks/filter-form/fields/date-time/components/FilterRangePicker.tsx +84 -0
  892. package/src/flow/models/blocks/filter-form/fields/date-time/components/__tests__/FilterDatePickers.test.tsx +121 -0
  893. package/src/flow/models/blocks/filter-form/fields/date-time/index.ts +13 -0
  894. package/src/flow/models/blocks/filter-form/fields/index.ts +14 -0
  895. package/src/flow/models/blocks/filter-form/index.ts +25 -0
  896. package/src/flow/models/blocks/filter-form/legacyDefaultValueMigration.ts +36 -0
  897. package/src/flow/models/blocks/filter-form/valueNormalization.ts +92 -0
  898. package/src/flow/models/blocks/filter-manager/FilterManager.ts +488 -0
  899. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.bindToTarget.test.ts +446 -0
  900. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +627 -0
  901. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.unbindFromTarget.test.ts +242 -0
  902. package/src/flow/models/blocks/filter-manager/__tests__/getDefaultOperator.test.ts +69 -0
  903. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/defaultOperator.test.tsx +58 -0
  904. package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +488 -0
  905. package/src/flow/models/blocks/filter-manager/flow-actions/customizeFilterRender.tsx +78 -0
  906. package/src/flow/models/blocks/filter-manager/flow-actions/defaultOperator.tsx +48 -0
  907. package/src/flow/models/blocks/filter-manager/flow-actions/index.ts +13 -0
  908. package/src/flow/models/blocks/filter-manager/flow-actions/operatorComponentProps.tsx +47 -0
  909. package/src/flow/models/blocks/filter-manager/index.ts +13 -0
  910. package/src/flow/models/blocks/filter-manager/utils.ts +71 -0
  911. package/src/flow/models/blocks/form/CreateFormModel.tsx +124 -0
  912. package/src/flow/models/blocks/form/EditFormModel.tsx +298 -0
  913. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +15 -0
  914. package/src/flow/models/blocks/form/FormActionModel.tsx +118 -0
  915. package/src/flow/models/blocks/form/FormAssociationFieldGroupModel.tsx +18 -0
  916. package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +359 -0
  917. package/src/flow/models/blocks/form/FormBlockModel.tsx +736 -0
  918. package/src/flow/models/blocks/form/FormCustomItemModel.tsx +54 -0
  919. package/src/flow/models/blocks/form/FormGridModel.tsx +106 -0
  920. package/src/flow/models/blocks/form/FormItemModel.tsx +472 -0
  921. package/src/flow/models/blocks/form/FormJSFieldItemModel.tsx +36 -0
  922. package/src/flow/models/blocks/form/JSFormActionModel.tsx +85 -0
  923. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +303 -0
  924. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +813 -0
  925. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +69 -0
  926. package/src/flow/models/blocks/form/__tests__/assignRulesUpdateAssociationValues.test.ts +81 -0
  927. package/src/flow/models/blocks/form/__tests__/dynamicNamePath.test.ts +33 -0
  928. package/src/flow/models/blocks/form/__tests__/legacyDefaultValueMigration.test.ts +135 -0
  929. package/src/flow/models/blocks/form/__tests__/submitValues.test.ts +224 -0
  930. package/src/flow/models/blocks/form/assignRulesUpdateAssociationValues.ts +73 -0
  931. package/src/flow/models/blocks/form/dynamicNamePath.ts +68 -0
  932. package/src/flow/models/blocks/form/index.tsx +23 -0
  933. package/src/flow/models/blocks/form/legacyDefaultValueMigration.ts +32 -0
  934. package/src/flow/models/blocks/form/submitHandler.ts +63 -0
  935. package/src/flow/models/blocks/form/submitValues.ts +227 -0
  936. package/src/flow/models/blocks/form/value-runtime/__tests__/deps.test.ts +28 -0
  937. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +3270 -0
  938. package/src/flow/models/blocks/form/value-runtime/__tests__/subtable-nested.test.ts +103 -0
  939. package/src/flow/models/blocks/form/value-runtime/conditions.ts +22 -0
  940. package/src/flow/models/blocks/form/value-runtime/deps.ts +181 -0
  941. package/src/flow/models/blocks/form/value-runtime/form-patch.ts +83 -0
  942. package/src/flow/models/blocks/form/value-runtime/index.ts +12 -0
  943. package/src/flow/models/blocks/form/value-runtime/path.ts +144 -0
  944. package/src/flow/models/blocks/form/value-runtime/rules.ts +1930 -0
  945. package/src/flow/models/blocks/form/value-runtime/runtime.ts +1118 -0
  946. package/src/flow/models/blocks/form/value-runtime/types.ts +72 -0
  947. package/src/flow/models/blocks/form/value-runtime/utils.ts +25 -0
  948. package/src/flow/models/blocks/index.ts +17 -0
  949. package/src/flow/models/blocks/js-block/JSBlock.tsx +180 -0
  950. package/src/flow/models/blocks/js-block/index.ts +10 -0
  951. package/src/flow/models/blocks/shared/legacyDefaultValueMigrationBase.ts +176 -0
  952. package/src/flow/models/blocks/table/JSColumnModel.tsx +279 -0
  953. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +238 -0
  954. package/src/flow/models/blocks/table/TableAssociationFieldGroupModel.tsx +18 -0
  955. package/src/flow/models/blocks/table/TableBlockModel.tsx +1073 -0
  956. package/src/flow/models/blocks/table/TableColumnModel.tsx +555 -0
  957. package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +116 -0
  958. package/src/flow/models/blocks/table/TableJSFieldItemModel.tsx +38 -0
  959. package/src/flow/models/blocks/table/TableSelectModel.tsx +75 -0
  960. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +282 -0
  961. package/src/flow/models/blocks/table/__tests__/TableBlockModel.blockHeight.test.tsx +120 -0
  962. package/src/flow/models/blocks/table/__tests__/TableBlockModel.popupSourceId.test.ts +87 -0
  963. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +72 -0
  964. package/src/flow/models/blocks/table/__tests__/sortUtils.test.ts +58 -0
  965. package/src/flow/models/blocks/table/dragSort/dragSortComponents.tsx +90 -0
  966. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +130 -0
  967. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +62 -0
  968. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +57 -0
  969. package/src/flow/models/blocks/table/dragSort/index.ts +13 -0
  970. package/src/flow/models/blocks/table/index.ts +18 -0
  971. package/src/flow/models/blocks/table/sortUtils.ts +52 -0
  972. package/src/flow/models/blocks/table/utils.ts +125 -0
  973. package/src/flow/models/blocks/utils/__tests__/transformChildrenToJS.test.ts +216 -0
  974. package/src/flow/models/blocks/utils/transformChildrenToJS.ts +158 -0
  975. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +47 -0
  976. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +822 -0
  977. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableActionGroupModel.tsx +19 -0
  978. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableActionsColumnModel.tsx +212 -0
  979. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +909 -0
  980. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +383 -0
  981. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableFormSubmitActionModel.tsx +121 -0
  982. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableRemoveActionModel.tsx +101 -0
  983. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/__tests__/popupSubTableBeforeClose.test.ts +98 -0
  984. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/popupSubTableBeforeClose.ts +158 -0
  985. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/blocks/PopupSubTableFormModel.tsx +235 -0
  986. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/index.tsx +18 -0
  987. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +514 -0
  988. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +1005 -0
  989. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +649 -0
  990. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +865 -0
  991. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +259 -0
  992. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +395 -0
  993. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/rowIdentity.ts +70 -0
  994. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +248 -0
  995. package/src/flow/models/fields/AssociationFieldModel/__tests__/SubTableRowIdentity.test.ts +45 -0
  996. package/src/flow/models/fields/AssociationFieldModel/__tests__/recordSelectShared.fieldNames.test.ts +60 -0
  997. package/src/flow/models/fields/AssociationFieldModel/index.tsx +16 -0
  998. package/src/flow/models/fields/AssociationFieldModel/itemChain.ts +362 -0
  999. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +283 -0
  1000. package/src/flow/models/fields/CheckboxFieldModel.tsx +31 -0
  1001. package/src/flow/models/fields/CheckboxGroupFieldModel.tsx +25 -0
  1002. package/src/flow/models/fields/ClickableFieldModel.tsx +286 -0
  1003. package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +94 -0
  1004. package/src/flow/models/fields/ColorFieldModel.tsx +61 -0
  1005. package/src/flow/models/fields/DateTimeFieldModel/DateOnlyFieldModel.tsx +61 -0
  1006. package/src/flow/models/fields/DateTimeFieldModel/DateTimeFieldModel.tsx +26 -0
  1007. package/src/flow/models/fields/DateTimeFieldModel/DateTimeNoTzFieldModel.tsx +53 -0
  1008. package/src/flow/models/fields/DateTimeFieldModel/DateTimeTzFieldModel.tsx +96 -0
  1009. package/src/flow/models/fields/DateTimeFieldModel/index.ts +12 -0
  1010. package/src/flow/models/fields/DisplayAssociationField/DisplaySubItemFieldModel.tsx +70 -0
  1011. package/src/flow/models/fields/DisplayAssociationField/DisplaySubListFieldModel.tsx +136 -0
  1012. package/src/flow/models/fields/DisplayAssociationField/DisplaySubTableFieldModel.tsx +271 -0
  1013. package/src/flow/models/fields/DisplayAssociationField/__tests__/DisplaySubListFieldModel.utils.test.ts +41 -0
  1014. package/src/flow/models/fields/DisplayAssociationField/displaySubListUtils.ts +29 -0
  1015. package/src/flow/models/fields/DisplayAssociationField/index.tsx +12 -0
  1016. package/src/flow/models/fields/DisplayCheckboxFieldModel.tsx +36 -0
  1017. package/src/flow/models/fields/DisplayColorFieldModel.tsx +45 -0
  1018. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +59 -0
  1019. package/src/flow/models/fields/DisplayEnumFieldModel.tsx +123 -0
  1020. package/src/flow/models/fields/DisplayHtmlFieldModel.tsx +39 -0
  1021. package/src/flow/models/fields/DisplayIconFieldModel.tsx +24 -0
  1022. package/src/flow/models/fields/DisplayJSONFieldModel.tsx +103 -0
  1023. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +239 -0
  1024. package/src/flow/models/fields/DisplayPasswordFieldModel.tsx +30 -0
  1025. package/src/flow/models/fields/DisplayPercentFieldModel.tsx +165 -0
  1026. package/src/flow/models/fields/DisplayTextFieldModel.tsx +51 -0
  1027. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +47 -0
  1028. package/src/flow/models/fields/DisplayTitleFieldModel.tsx +79 -0
  1029. package/src/flow/models/fields/DisplayURLFieldModel.tsx +41 -0
  1030. package/src/flow/models/fields/DividerItemModel.tsx +100 -0
  1031. package/src/flow/models/fields/IconFieldModel.tsx +172 -0
  1032. package/src/flow/models/fields/InputFieldModel.tsx +65 -0
  1033. package/src/flow/models/fields/JSEditableFieldModel.tsx +219 -0
  1034. package/src/flow/models/fields/JSFieldModel.tsx +184 -0
  1035. package/src/flow/models/fields/JSItemModel.tsx +163 -0
  1036. package/src/flow/models/fields/JsonFieldModel.tsx +121 -0
  1037. package/src/flow/models/fields/MarkdownItemModel.tsx +68 -0
  1038. package/src/flow/models/fields/NumberFieldModel.tsx +68 -0
  1039. package/src/flow/models/fields/PasswordFieldModel.tsx +53 -0
  1040. package/src/flow/models/fields/PercentFieldModel.tsx +53 -0
  1041. package/src/flow/models/fields/RadioGroupFieldModel.tsx +26 -0
  1042. package/src/flow/models/fields/RichTextFieldModel/index.tsx +115 -0
  1043. package/src/flow/models/fields/RichTextFieldModel/registerFontSize.tsx +18 -0
  1044. package/src/flow/models/fields/RichTextFieldModel/registerImageResize.tsx +56 -0
  1045. package/src/flow/models/fields/RichTextFieldModel/registerSmartBreak.tsx +90 -0
  1046. package/src/flow/models/fields/RichTextFieldModel/style.ts +493 -0
  1047. package/src/flow/models/fields/SelectFieldModel.tsx +136 -0
  1048. package/src/flow/models/fields/TextareaFieldModel.tsx +31 -0
  1049. package/src/flow/models/fields/TimeFieldModel.tsx +66 -0
  1050. package/src/flow/models/fields/VariableFieldFormModel.tsx +67 -0
  1051. package/src/flow/models/fields/index.ts +52 -0
  1052. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +109 -0
  1053. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +319 -0
  1054. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +93 -0
  1055. package/src/flow/models/fields/mobile-components/MobileTimePicker.tsx +37 -0
  1056. package/src/flow/models/index.ts +14 -0
  1057. package/src/flow/models/topbar/TopbarActionModel.tsx +414 -0
  1058. package/src/flow/models/topbar/UserCenterTopbarActionModel.tsx +592 -0
  1059. package/src/flow/models/topbar/index.ts +11 -0
  1060. package/src/flow/models/utils/resolveRunJsParams.ts +24 -0
  1061. package/src/flow/resolveViewParamsToViewList.tsx +79 -0
  1062. package/src/flow/system-settings/SystemSettingsSource.ts +155 -0
  1063. package/src/flow/system-settings/index.ts +11 -0
  1064. package/src/flow/system-settings/useSystemSettings.tsx +51 -0
  1065. package/src/flow/utils/__tests__/actionCapability.test.ts +79 -0
  1066. package/src/flow/utils/__tests__/dispatchEventDeep.test.ts +72 -0
  1067. package/src/flow/utils/__tests__/pagination.test.ts +64 -0
  1068. package/src/flow/utils/actionCapability.ts +150 -0
  1069. package/src/flow/utils/blockUtils.ts +32 -0
  1070. package/src/flow/utils/dispatchEventDeep.ts +66 -0
  1071. package/src/flow/utils/index.ts +12 -0
  1072. package/src/flow/utils/pagination.ts +110 -0
  1073. package/src/flow/utils/useJsonTemplateResolver.ts +25 -0
  1074. package/src/flow-compat/ColorPicker.tsx +88 -0
  1075. package/src/flow-compat/FieldValidation.tsx +598 -0
  1076. package/src/flow-compat/HighPerformanceSpin.tsx +45 -0
  1077. package/src/flow-compat/Icon.tsx +11 -0
  1078. package/src/flow-compat/IconPicker.tsx +186 -0
  1079. package/src/flow-compat/Password.tsx +78 -0
  1080. package/src/flow-compat/PasswordStrength.tsx +27 -0
  1081. package/src/flow-compat/Popover.tsx +60 -0
  1082. package/src/flow-compat/data.ts +21 -0
  1083. package/src/flow-compat/date.ts +116 -0
  1084. package/src/flow-compat/fieldValidationConstants.ts +305 -0
  1085. package/src/flow-compat/index.ts +28 -0
  1086. package/src/flow-compat/lazy.tsx +110 -0
  1087. package/src/flow-compat/operators.ts +367 -0
  1088. package/src/flow-compat/passwordUtils.ts +153 -0
  1089. package/src/flow-compat/routeTypes.ts +61 -0
  1090. package/src/flow-compat/useFullscreenOverlay.ts +139 -0
  1091. package/src/flow-compat/zIndexContext.tsx +28 -0
  1092. package/src/hooks/useApp.ts +5 -3
  1093. package/src/hooks/usePlugin.ts +2 -2
  1094. package/src/i18n.ts +25 -0
  1095. package/src/index.ts +20 -0
  1096. package/src/locale/languageCodes.ts +83 -0
  1097. package/src/nocobase-buildin-plugin/index.tsx +318 -0
  1098. package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +61 -0
  1099. package/src/nocobase-buildin-plugin/plugins/dayjsLocale.ts +77 -0
  1100. package/src/nocobase-buildin-plugin/plugins/loadConstrueLocale.ts +182 -0
  1101. package/src/react-is.d.ts +12 -0
  1102. package/src/settings-center/AdminSettingsLayout.tsx +333 -0
  1103. package/src/settings-center/AdminSettingsLayoutModel.tsx +32 -0
  1104. package/src/settings-center/PluginManagerPage.tsx +162 -0
  1105. package/src/settings-center/SystemSettingsPage.tsx +298 -0
  1106. package/src/settings-center/index.ts +14 -0
  1107. package/src/settings-center/utils.tsx +277 -0
  1108. package/src/theme/AntdAppProvider.tsx +52 -0
  1109. package/src/theme/compatOldTheme.ts +21 -0
  1110. package/src/theme/customAlgorithm.ts +43 -0
  1111. package/src/theme/defaultTheme.ts +32 -0
  1112. package/src/theme/index.tsx +104 -0
  1113. package/src/theme/type.ts +50 -0
  1114. package/src/utils/globalDeps.ts +3 -0
  1115. package/src/utils/index.tsx +8 -4
  1116. package/src/utils/remotePlugins.ts +10 -13
  1117. package/lib/Application.d.ts +0 -124
  1118. package/lib/Application.js +0 -499
  1119. package/lib/MockApplication.d.ts +0 -16
  1120. package/lib/MockApplication.js +0 -96
  1121. package/lib/Plugin.d.ts +0 -33
  1122. package/lib/Plugin.js +0 -89
  1123. package/lib/PluginManager.d.ts +0 -46
  1124. package/lib/PluginManager.js +0 -114
  1125. package/lib/PluginSettingsManager.d.ts +0 -68
  1126. package/lib/PluginSettingsManager.js +0 -149
  1127. package/lib/RouterManager.d.ts +0 -69
  1128. package/lib/RouterManager.js +0 -231
  1129. package/lib/WebSocketClient.d.ts +0 -45
  1130. package/lib/WebSocketClient.js +0 -217
  1131. package/lib/components/BlankComponent.js +0 -48
  1132. package/lib/components/MainComponent.js +0 -54
  1133. package/lib/components/RouterBridge.js +0 -66
  1134. package/lib/components/RouterContextCleaner.js +0 -61
  1135. package/lib/components/index.js +0 -32
  1136. package/lib/context.js +0 -38
  1137. package/lib/hooks/index.js +0 -34
  1138. package/lib/hooks/useApp.js +0 -41
  1139. package/lib/hooks/usePlugin.js +0 -42
  1140. package/lib/hooks/useRouter.js +0 -41
  1141. package/lib/utils/globalDeps.js +0 -82
  1142. package/lib/utils/index.js +0 -79
  1143. package/lib/utils/remotePlugins.d.ts +0 -44
  1144. package/lib/utils/remotePlugins.js +0 -131
  1145. package/lib/utils/requirejs.js +0 -1361
  1146. package/lib/utils/types.d.ts +0 -330
  1147. package/lib/utils/types.js +0 -28
@@ -0,0 +1,3270 @@
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 { waitFor } from '@testing-library/react';
12
+ import { EventEmitter } from 'events';
13
+ import dayjs from 'dayjs';
14
+ import { observable } from '@formily/reactive';
15
+ import { get as lodashGet, merge as lodashMerge, set as lodashSet } from 'lodash';
16
+ import { FlowContext, JSRunner } from '@nocobase/flow-engine';
17
+ import { FormValueRuntime } from '..';
18
+
19
+ function createFormStub(initialValues: any = {}) {
20
+ const store: any = JSON.parse(JSON.stringify(initialValues || {}));
21
+ return {
22
+ __store: store,
23
+ getFieldValue: (namePath: any) => lodashGet(store, namePath),
24
+ setFieldValue: (namePath: any, value: any) => lodashSet(store, namePath, value),
25
+ getFieldsValue: () => JSON.parse(JSON.stringify(store)),
26
+ setFieldsValue: (patch: any) => lodashMerge(store, patch),
27
+ };
28
+ }
29
+
30
+ function createFieldContext(runtime: FormValueRuntime) {
31
+ const ctx: any = new FlowContext();
32
+ ctx.defineProperty('formValues', { get: () => runtime.formValues, cache: false });
33
+ ctx.defineProperty('app', {
34
+ value: {
35
+ jsonLogic: {
36
+ apply: (logic: any) => {
37
+ try {
38
+ const op = logic && typeof logic === 'object' ? Object.keys(logic)[0] : '';
39
+ const args = op ? (logic as any)[op] : [];
40
+ const left = Array.isArray(args) ? args[0] : undefined;
41
+ const right = Array.isArray(args) ? args[1] : undefined;
42
+
43
+ switch (op) {
44
+ case '$eq':
45
+ return left === right;
46
+ case '$ne':
47
+ return left !== right;
48
+ case '$gt':
49
+ return left > right;
50
+ case '$lt':
51
+ return left < right;
52
+ case '$includes':
53
+ return String(left ?? '').includes(String(right ?? ''));
54
+ case '$notIncludes':
55
+ return !String(left ?? '').includes(String(right ?? ''));
56
+ case '$empty':
57
+ return left == null || left === '' || (Array.isArray(left) && left.length === 0);
58
+ case '$notEmpty':
59
+ return !(left == null || left === '' || (Array.isArray(left) && left.length === 0));
60
+ default:
61
+ return false;
62
+ }
63
+ } catch {
64
+ return false;
65
+ }
66
+ },
67
+ },
68
+ },
69
+ });
70
+ ctx.defineMethod('runjs', async function (this: any, code: string, variables?: Record<string, any>, options?: any) {
71
+ const runner = new JSRunner({ globals: { ctx: this, ...(variables || {}) }, timeoutMs: options?.timeoutMs });
72
+ return runner.run(code);
73
+ });
74
+ ctx.defineMethod('getVar', async function (this: any, varPath: string) {
75
+ const raw = typeof varPath === 'string' ? varPath : String(varPath ?? '');
76
+ const s = raw.trim();
77
+ if (!s) return undefined;
78
+ if (s !== 'ctx' && !s.startsWith('ctx.')) {
79
+ throw new Error(`ctx.getVar(path) expects an expression starting with "ctx.", got: "${s}"`);
80
+ }
81
+ return this.resolveJsonTemplate(`{{ ${s} }}`);
82
+ });
83
+ ctx.defineMethod('resolveJsonTemplate', async function (this: any, template: any) {
84
+ const resolveAny = (val: any): any => {
85
+ if (typeof val === 'string') {
86
+ if (val === '__B__') return this.formValues.b;
87
+ if (val === '__DYNAMIC__') {
88
+ const key = this.formValues.selector;
89
+ return this.formValues[key];
90
+ }
91
+
92
+ const trimmed = val.trim();
93
+ const match = trimmed.match(/^\{\{\s*ctx(?:\.(.+?))?\s*\}\}$/);
94
+ if (match) {
95
+ const pathString = match[1];
96
+ if (!pathString) return this;
97
+ const segs = pathString.split('.').filter(Boolean);
98
+ return lodashGet(this, segs);
99
+ }
100
+ return val;
101
+ }
102
+
103
+ if (Array.isArray(val)) {
104
+ return val.map((v) => resolveAny(v));
105
+ }
106
+
107
+ if (val && typeof val === 'object') {
108
+ const next: any = {};
109
+ for (const [k, v] of Object.entries(val)) {
110
+ next[k] = resolveAny(v);
111
+ }
112
+ return next;
113
+ }
114
+
115
+ return val;
116
+ };
117
+
118
+ return resolveAny(template);
119
+ });
120
+ return ctx;
121
+ }
122
+
123
+ describe('FormValueRuntime (default rules)', () => {
124
+ it('recomputes default on dependency change when current equals last default; user change disables default permanently', async () => {
125
+ const engineEmitter = new EventEmitter();
126
+ const blockEmitter = new EventEmitter();
127
+ const formStub = createFormStub({ b: 'X' });
128
+
129
+ const blockModel: any = {
130
+ uid: 'form-1',
131
+ flowEngine: { emitter: engineEmitter },
132
+ emitter: blockEmitter,
133
+ dispatchEvent: vi.fn(),
134
+ getAclActionName: () => 'create',
135
+ };
136
+
137
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
138
+ runtime.mount({ sync: true });
139
+
140
+ const fieldCtx = createFieldContext(runtime);
141
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
142
+ fieldCtx.defineProperty('fieldPathArray', { value: ['a'] });
143
+
144
+ const fieldModel: any = {
145
+ uid: 'field-a',
146
+ context: fieldCtx,
147
+ props: { initialValue: '__B__' },
148
+ getProps: function () {
149
+ return this.props;
150
+ },
151
+ };
152
+ fieldCtx.defineProperty('model', { value: fieldModel });
153
+
154
+ engineEmitter.emit('model:mounted', { model: fieldModel });
155
+
156
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
157
+
158
+ // change dependency -> default updates
159
+ await runtime.setFormValues(fieldCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
160
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
161
+
162
+ // user changes target -> default should be disabled permanently
163
+ await runtime.setFormValues(fieldCtx, [{ path: ['a'], value: 'user' }], { source: 'user' });
164
+ await runtime.setFormValues(fieldCtx, [{ path: ['b'], value: 'Z' }], { source: 'user' });
165
+ expect(formStub.getFieldValue(['a'])).toBe('user');
166
+ });
167
+
168
+ it('handles onFieldsChange name as string and triggers default recompute', async () => {
169
+ const engineEmitter = new EventEmitter();
170
+ const blockEmitter = new EventEmitter();
171
+ const formStub = createFormStub({ b: 'X' });
172
+
173
+ const blockModel: any = {
174
+ uid: 'form-3',
175
+ flowEngine: { emitter: engineEmitter },
176
+ emitter: blockEmitter,
177
+ dispatchEvent: vi.fn(),
178
+ getAclActionName: () => 'create',
179
+ };
180
+
181
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
182
+ runtime.mount({ sync: true });
183
+
184
+ const fieldCtx = createFieldContext(runtime);
185
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
186
+ fieldCtx.defineProperty('fieldPathArray', { value: ['a'] });
187
+
188
+ const fieldModel: any = {
189
+ uid: 'field-a3',
190
+ context: fieldCtx,
191
+ props: { initialValue: '__B__' },
192
+ getProps: function () {
193
+ return this.props;
194
+ },
195
+ };
196
+ fieldCtx.defineProperty('model', { value: fieldModel });
197
+
198
+ engineEmitter.emit('model:mounted', { model: fieldModel });
199
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
200
+
201
+ // 模拟 antd onFieldsChange: name 可能是 string
202
+ lodashSet((formStub as any).__store, ['b'], 'Y');
203
+ runtime.handleFormFieldsChange([{ name: 'b', touched: true } as any]);
204
+
205
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
206
+ });
207
+
208
+ it('patched form.setFieldsValue replaces top-level array fields instead of merging stale rows', async () => {
209
+ const engineEmitter = new EventEmitter();
210
+ const blockEmitter = new EventEmitter();
211
+ const formStub = createFormStub({ users: [{ id: 1 }, { id: 2 }, { id: 3 }] });
212
+
213
+ const blockModel: any = {
214
+ uid: 'form-set-fields-value-array-replace',
215
+ flowEngine: { emitter: engineEmitter },
216
+ emitter: blockEmitter,
217
+ dispatchEvent: vi.fn(),
218
+ getAclActionName: () => 'create',
219
+ };
220
+
221
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
222
+ runtime.mount({ sync: true });
223
+
224
+ formStub.setFieldsValue({ users: [] });
225
+ formStub.setFieldsValue({ users: [{ id: 'A' }, { id: 'B' }] });
226
+
227
+ await waitFor(() => {
228
+ expect(formStub.getFieldValue(['users'])).toEqual([{ id: 'A' }, { id: 'B' }]);
229
+ });
230
+ });
231
+
232
+ it('tracks dynamic deps (formValues[selector]) and updates subscriptions', async () => {
233
+ const engineEmitter = new EventEmitter();
234
+ const blockEmitter = new EventEmitter();
235
+ const formStub = createFormStub({ selector: 'x', x: 1, y: 2 });
236
+
237
+ const blockModel: any = {
238
+ uid: 'form-2',
239
+ flowEngine: { emitter: engineEmitter },
240
+ emitter: blockEmitter,
241
+ dispatchEvent: vi.fn(),
242
+ getAclActionName: () => 'create',
243
+ };
244
+
245
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
246
+ runtime.mount({ sync: true });
247
+
248
+ const fieldCtx = createFieldContext(runtime);
249
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
250
+ fieldCtx.defineProperty('fieldPathArray', { value: ['a'] });
251
+
252
+ const fieldModel: any = {
253
+ uid: 'field-a2',
254
+ context: fieldCtx,
255
+ props: { initialValue: '__DYNAMIC__' },
256
+ getProps: function () {
257
+ return this.props;
258
+ },
259
+ };
260
+ fieldCtx.defineProperty('model', { value: fieldModel });
261
+
262
+ engineEmitter.emit('model:mounted', { model: fieldModel });
263
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe(1));
264
+
265
+ // subscribed to x
266
+ await runtime.setFormValues(fieldCtx, [{ path: ['x'], value: 10 }], { source: 'user' });
267
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe(10));
268
+
269
+ // selector changes -> deps should shift to y
270
+ await runtime.setFormValues(fieldCtx, [{ path: ['selector'], value: 'y' }], { source: 'user' });
271
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe(2));
272
+
273
+ // x changes should no longer affect a
274
+ await runtime.setFormValues(fieldCtx, [{ path: ['x'], value: 20 }], { source: 'user' });
275
+ await new Promise((resolve) => setTimeout(resolve, 20));
276
+ expect(formStub.getFieldValue(['a'])).toBe(2);
277
+
278
+ // y changes should update a
279
+ await runtime.setFormValues(fieldCtx, [{ path: ['y'], value: 30 }], { source: 'user' });
280
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe(30));
281
+ });
282
+ });
283
+
284
+ describe('FormValueRuntime (form assign rules)', () => {
285
+ it('migrates block-level rule to field instance on mount and restores on unmount', async () => {
286
+ const engineEmitter = new EventEmitter();
287
+ const blockEmitter = new EventEmitter();
288
+ const formStub = createFormStub({ b: 'X' });
289
+
290
+ const blockModel: any = {
291
+ uid: 'form-assign-migrate-1',
292
+ flowEngine: { emitter: engineEmitter },
293
+ emitter: blockEmitter,
294
+ dispatchEvent: vi.fn(),
295
+ getAclActionName: () => 'create',
296
+ };
297
+
298
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
299
+ runtime.mount({ sync: true });
300
+
301
+ const blockCtx = createFieldContext(runtime);
302
+ blockModel.context = blockCtx;
303
+
304
+ runtime.syncAssignRules([
305
+ {
306
+ key: 'r1',
307
+ enable: true,
308
+ targetPath: 'a',
309
+ mode: 'assign',
310
+ condition: { logic: '$and', items: [] },
311
+ value: '__B__',
312
+ },
313
+ ]);
314
+
315
+ const ruleEngine: any = (runtime as any).ruleEngine;
316
+ const rules: Map<string, any> = ruleEngine.rules;
317
+ const blockId = 'form-assign:r1:block';
318
+ expect(rules.has(blockId)).toBe(true);
319
+
320
+ const fieldCtx = createFieldContext(runtime);
321
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
322
+
323
+ const fieldModel: any = {
324
+ uid: 'field-a-migrate-1',
325
+ subModels: { field: {} },
326
+ getStepParams(flowKey: string, stepKey: string) {
327
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
328
+ return { fieldPath: 'a' };
329
+ }
330
+ return undefined;
331
+ },
332
+ };
333
+ fieldCtx.defineProperty('model', { value: fieldModel });
334
+ fieldModel.context = fieldCtx;
335
+
336
+ engineEmitter.emit('model:mounted', { model: fieldModel });
337
+
338
+ const instanceId = 'form-assign:r1:field-a-migrate-1:master';
339
+ expect(rules.has(blockId)).toBe(false);
340
+ expect(rules.has(instanceId)).toBe(true);
341
+
342
+ engineEmitter.emit('model:unmounted', { model: fieldModel });
343
+
344
+ expect(rules.has(instanceId)).toBe(false);
345
+ expect(rules.has(blockId)).toBe(true);
346
+ });
347
+
348
+ it('normalizes date-only value for unixTimestamp target to start-of-day datetime', async () => {
349
+ const engineEmitter = new EventEmitter();
350
+ const blockEmitter = new EventEmitter();
351
+ const formStub = createFormStub({});
352
+
353
+ const blockModel: any = {
354
+ uid: 'form-assign-unix-ts-1',
355
+ flowEngine: { emitter: engineEmitter },
356
+ emitter: blockEmitter,
357
+ dispatchEvent: vi.fn(),
358
+ getAclActionName: () => 'create',
359
+ };
360
+
361
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
362
+ runtime.mount({ sync: true });
363
+
364
+ const blockCtx = createFieldContext(runtime);
365
+ blockModel.context = blockCtx;
366
+
367
+ runtime.syncAssignRules([
368
+ {
369
+ key: 'r1',
370
+ enable: true,
371
+ targetPath: 'a',
372
+ mode: 'assign',
373
+ condition: { logic: '$and', items: [] },
374
+ value: '2026-02-12',
375
+ },
376
+ ]);
377
+
378
+ const fieldCtx = createFieldContext(runtime);
379
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
380
+ fieldCtx.defineProperty('collectionField', { value: { interface: 'unixTimestamp' } });
381
+
382
+ const fieldModel: any = {
383
+ uid: 'field-a-unix-ts-1',
384
+ subModels: { field: {} },
385
+ getStepParams(flowKey: string, stepKey: string) {
386
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
387
+ return { fieldPath: 'a' };
388
+ }
389
+ return undefined;
390
+ },
391
+ };
392
+ fieldCtx.defineProperty('model', { value: fieldModel });
393
+ fieldModel.context = fieldCtx;
394
+
395
+ engineEmitter.emit('model:mounted', { model: fieldModel });
396
+
397
+ await waitFor(() => {
398
+ const assigned = formStub.getFieldValue(['a']);
399
+ expect(typeof assigned).toBe('string');
400
+ expect(assigned).not.toBe('2026-02-12');
401
+ expect(dayjs(assigned).isValid()).toBe(true);
402
+ expect(dayjs(assigned).format('YYYY-MM-DD')).toBe('2026-02-12');
403
+ });
404
+ });
405
+
406
+ it('supports RunJSValue and updates on formValues dependency change', async () => {
407
+ const engineEmitter = new EventEmitter();
408
+ const blockEmitter = new EventEmitter();
409
+ const formStub = createFormStub({ b: 'X' });
410
+
411
+ const blockModel: any = {
412
+ uid: 'form-assign-runjs-1',
413
+ flowEngine: { emitter: engineEmitter },
414
+ emitter: blockEmitter,
415
+ dispatchEvent: vi.fn(),
416
+ getAclActionName: () => 'create',
417
+ };
418
+
419
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
420
+ runtime.mount({ sync: true });
421
+
422
+ const blockCtx = createFieldContext(runtime);
423
+ const fieldModel: any = {
424
+ uid: 'field-a-runjs-1',
425
+ context: { fieldPathArray: ['a'] },
426
+ };
427
+ blockCtx.defineProperty('engine', {
428
+ value: {
429
+ getModel: (id: string) => (id === 'field-a-runjs-1' ? fieldModel : null),
430
+ },
431
+ });
432
+ blockModel.context = blockCtx;
433
+
434
+ runtime.syncAssignRules([
435
+ {
436
+ key: 'r1',
437
+ enable: true,
438
+ targetPath: 'a',
439
+ mode: 'assign',
440
+ condition: { logic: '$and', items: [] },
441
+ value: { code: 'return ctx.formValues.b', version: 'v1' },
442
+ },
443
+ ]);
444
+
445
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
446
+
447
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
448
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
449
+ });
450
+
451
+ it('supports RunJSValue and tracks ctx var deps from code', async () => {
452
+ const engineEmitter = new EventEmitter();
453
+ const blockEmitter = new EventEmitter();
454
+ const formStub = createFormStub({});
455
+
456
+ const blockModel: any = {
457
+ uid: 'form-assign-runjs-ctx',
458
+ flowEngine: { emitter: engineEmitter },
459
+ emitter: blockEmitter,
460
+ dispatchEvent: vi.fn(),
461
+ getAclActionName: () => 'create',
462
+ };
463
+
464
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
465
+ runtime.mount({ sync: true });
466
+
467
+ const store = observable({ someVar: 'X' });
468
+
469
+ const blockCtx = createFieldContext(runtime);
470
+ blockCtx.defineProperty('someVar', { get: () => store.someVar, cache: false });
471
+
472
+ const fieldModel: any = {
473
+ uid: 'field-a-runjs-ctx',
474
+ context: { fieldPathArray: ['a'] },
475
+ };
476
+ blockCtx.defineProperty('engine', {
477
+ value: {
478
+ getModel: (id: string) => (id === 'field-a-runjs-ctx' ? fieldModel : null),
479
+ },
480
+ });
481
+ blockModel.context = blockCtx;
482
+
483
+ runtime.syncAssignRules([
484
+ {
485
+ key: 'r1',
486
+ enable: true,
487
+ targetPath: 'a',
488
+ mode: 'assign',
489
+ condition: { logic: '$and', items: [] },
490
+ value: { code: 'return ctx.someVar', version: 'v1' },
491
+ },
492
+ ]);
493
+
494
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
495
+
496
+ store.someVar = 'Y';
497
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
498
+ });
499
+
500
+ it('does not write when RunJS execution fails', async () => {
501
+ const engineEmitter = new EventEmitter();
502
+ const blockEmitter = new EventEmitter();
503
+ const formStub = createFormStub({ a: 'INIT' });
504
+
505
+ const blockModel: any = {
506
+ uid: 'form-assign-runjs-error',
507
+ flowEngine: { emitter: engineEmitter },
508
+ emitter: blockEmitter,
509
+ dispatchEvent: vi.fn(),
510
+ getAclActionName: () => 'create',
511
+ };
512
+
513
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
514
+ runtime.mount({ sync: true });
515
+
516
+ const blockCtx = createFieldContext(runtime);
517
+ const fieldModel: any = {
518
+ uid: 'field-a-runjs-error',
519
+ context: { fieldPathArray: ['a'] },
520
+ };
521
+ blockCtx.defineProperty('engine', {
522
+ value: {
523
+ getModel: (id: string) => (id === 'field-a-runjs-error' ? fieldModel : null),
524
+ },
525
+ });
526
+ blockModel.context = blockCtx;
527
+
528
+ runtime.syncAssignRules([
529
+ {
530
+ key: 'r1',
531
+ enable: true,
532
+ targetPath: 'a',
533
+ mode: 'assign',
534
+ condition: { logic: '$and', items: [] },
535
+ value: { code: 'throw new Error("bad")', version: 'v1' },
536
+ },
537
+ ]);
538
+
539
+ await new Promise((resolve) => setTimeout(resolve, 30));
540
+ expect(formStub.getFieldValue(['a'])).toBe('INIT');
541
+ });
542
+
543
+ it('mode=assign keeps overriding after user change', async () => {
544
+ const engineEmitter = new EventEmitter();
545
+ const blockEmitter = new EventEmitter();
546
+ const formStub = createFormStub({ b: 'X' });
547
+
548
+ const blockModel: any = {
549
+ uid: 'form-assign-1',
550
+ flowEngine: { emitter: engineEmitter },
551
+ emitter: blockEmitter,
552
+ dispatchEvent: vi.fn(),
553
+ getAclActionName: () => 'create',
554
+ };
555
+
556
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
557
+ runtime.mount({ sync: true });
558
+
559
+ const blockCtx = createFieldContext(runtime);
560
+ const fieldModel: any = {
561
+ uid: 'field-a',
562
+ context: { fieldPathArray: ['a'] },
563
+ };
564
+ blockCtx.defineProperty('engine', {
565
+ value: {
566
+ getModel: (id: string) => (id === 'field-a' ? fieldModel : null),
567
+ },
568
+ });
569
+ blockModel.context = blockCtx;
570
+
571
+ runtime.syncAssignRules([
572
+ {
573
+ key: 'r1',
574
+ enable: true,
575
+ targetPath: 'a',
576
+ mode: 'assign',
577
+ condition: { logic: '$and', items: [] },
578
+ value: '__B__',
579
+ },
580
+ ]);
581
+
582
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
583
+
584
+ // user writes to target; assign rule MUST NOT stop(可能会被规则立即回写覆盖)
585
+ await runtime.setFormValues(blockCtx, [{ path: ['a'], value: 'user' }], { source: 'user' });
586
+
587
+ // dependency change triggers overwrite again
588
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
589
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
590
+ });
591
+
592
+ it('mode=default follows explicit semantics and stops after user change', async () => {
593
+ const engineEmitter = new EventEmitter();
594
+ const blockEmitter = new EventEmitter();
595
+ const formStub = createFormStub({ b: 'X' });
596
+
597
+ const blockModel: any = {
598
+ uid: 'form-assign-2',
599
+ flowEngine: { emitter: engineEmitter },
600
+ emitter: blockEmitter,
601
+ dispatchEvent: vi.fn(),
602
+ getAclActionName: () => 'create',
603
+ };
604
+
605
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
606
+ runtime.mount({ sync: true });
607
+
608
+ const blockCtx = createFieldContext(runtime);
609
+ const fieldModel: any = {
610
+ uid: 'field-a2',
611
+ context: { fieldPathArray: ['a'] },
612
+ };
613
+ blockCtx.defineProperty('engine', {
614
+ value: {
615
+ getModel: (id: string) => (id === 'field-a2' ? fieldModel : null),
616
+ },
617
+ });
618
+ blockModel.context = blockCtx;
619
+
620
+ runtime.syncAssignRules([
621
+ {
622
+ key: 'r1',
623
+ enable: true,
624
+ targetPath: 'a',
625
+ mode: 'default',
626
+ condition: { logic: '$and', items: [] },
627
+ value: '__B__',
628
+ },
629
+ ]);
630
+
631
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
632
+
633
+ // user writes to target; default mode MUST stop permanently
634
+ await runtime.setFormValues(blockCtx, [{ path: ['a'], value: 'user' }], { source: 'user' });
635
+ expect(formStub.getFieldValue(['a'])).toBe('user');
636
+
637
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
638
+ await new Promise((resolve) => setTimeout(resolve, 20));
639
+ expect(formStub.getFieldValue(['a'])).toBe('user');
640
+ });
641
+
642
+ it('reapplies mode=default value after form reset starts a new create session', async () => {
643
+ const engineEmitter = new EventEmitter();
644
+ const blockEmitter = new EventEmitter();
645
+ const formStub = createFormStub({});
646
+
647
+ const blockModel: any = {
648
+ uid: 'form-assign-reset-reapply-default',
649
+ flowEngine: { emitter: engineEmitter },
650
+ emitter: blockEmitter,
651
+ dispatchEvent: vi.fn(),
652
+ getAclActionName: () => 'create',
653
+ };
654
+
655
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
656
+ runtime.mount({ sync: true });
657
+
658
+ const blockCtx = createFieldContext(runtime);
659
+ const fieldModel: any = {
660
+ uid: 'field-a-reset-reapply-default',
661
+ context: { fieldPathArray: ['a'] },
662
+ };
663
+ blockCtx.defineProperty('engine', {
664
+ value: {
665
+ getModel: (id: string) => (id === 'field-a-reset-reapply-default' ? fieldModel : null),
666
+ },
667
+ });
668
+ blockModel.context = blockCtx;
669
+
670
+ runtime.syncAssignRules([
671
+ {
672
+ key: 'r1',
673
+ enable: true,
674
+ targetPath: 'a',
675
+ mode: 'default',
676
+ condition: { logic: '$and', items: [] },
677
+ value: 'AAA',
678
+ },
679
+ ]);
680
+
681
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('AAA'));
682
+
683
+ await runtime.setFormValues(blockCtx, [{ path: ['a'], value: 'AAA1' }], { source: 'user' });
684
+ expect(formStub.getFieldValue(['a'])).toBe('AAA1');
685
+
686
+ formStub.__store.a = undefined;
687
+ delete formStub.__store.a;
688
+ runtime.resetAfterFormReset();
689
+
690
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('AAA'));
691
+ });
692
+
693
+ it('resets valuesMirror before rescheduling default rules after form reset', async () => {
694
+ const engineEmitter = new EventEmitter();
695
+ const blockEmitter = new EventEmitter();
696
+ const formStub = createFormStub({ selector: 'x', x: 'AAA', y: 'BBB' });
697
+
698
+ const blockModel: any = {
699
+ uid: 'form-assign-reset-values-mirror',
700
+ flowEngine: { emitter: engineEmitter },
701
+ emitter: blockEmitter,
702
+ dispatchEvent: vi.fn(),
703
+ getAclActionName: () => 'create',
704
+ };
705
+
706
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
707
+ runtime.mount({ sync: true });
708
+
709
+ const blockCtx = createFieldContext(runtime);
710
+ const fieldModel: any = {
711
+ uid: 'field-a-reset-values-mirror',
712
+ context: { fieldPathArray: ['a'] },
713
+ };
714
+ blockCtx.defineProperty('engine', {
715
+ value: {
716
+ getModel: (id: string) => (id === 'field-a-reset-values-mirror' ? fieldModel : null),
717
+ },
718
+ });
719
+ blockModel.context = blockCtx;
720
+
721
+ runtime.syncAssignRules([
722
+ {
723
+ key: 'r1',
724
+ enable: true,
725
+ targetPath: 'a',
726
+ mode: 'default',
727
+ condition: { logic: '$and', items: [] },
728
+ value: '__DYNAMIC__',
729
+ },
730
+ ]);
731
+
732
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('AAA'));
733
+
734
+ await runtime.setFormValues(blockCtx, [{ path: ['a'], value: 'AAA1' }], { source: 'user' });
735
+ expect(formStub.getFieldValue(['a'])).toBe('AAA1');
736
+
737
+ for (const key of Object.keys(formStub.__store)) {
738
+ delete formStub.__store[key];
739
+ }
740
+ runtime.resetAfterFormReset();
741
+
742
+ await new Promise((resolve) => setTimeout(resolve, 20));
743
+ expect(formStub.getFieldValue(['a'])).toBeUndefined();
744
+
745
+ await runtime.setFormValues(blockCtx, [{ path: ['selector'], value: 'y' }], { source: 'user' });
746
+ await runtime.setFormValues(blockCtx, [{ path: ['y'], value: 'BBB2' }], { source: 'user' });
747
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('BBB2'));
748
+ });
749
+
750
+ it('skips mode=default form assignment in update form', async () => {
751
+ const engineEmitter = new EventEmitter();
752
+ const blockEmitter = new EventEmitter();
753
+ const formStub = createFormStub({ b: 'X' });
754
+
755
+ const blockModel: any = {
756
+ uid: 'form-assign-update-skip-default',
757
+ flowEngine: { emitter: engineEmitter },
758
+ emitter: blockEmitter,
759
+ dispatchEvent: vi.fn(),
760
+ getAclActionName: () => 'update',
761
+ };
762
+
763
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
764
+ runtime.mount({ sync: true });
765
+
766
+ const blockCtx = createFieldContext(runtime);
767
+ const fieldModel: any = {
768
+ uid: 'field-a-update-skip-default',
769
+ context: { fieldPathArray: ['a'] },
770
+ };
771
+ blockCtx.defineProperty('engine', {
772
+ value: {
773
+ getModel: (id: string) => (id === 'field-a-update-skip-default' ? fieldModel : null),
774
+ },
775
+ });
776
+ blockModel.context = blockCtx;
777
+
778
+ runtime.syncAssignRules([
779
+ {
780
+ key: 'r1',
781
+ enable: true,
782
+ targetPath: 'a',
783
+ mode: 'default',
784
+ condition: { logic: '$and', items: [] },
785
+ value: '__B__',
786
+ },
787
+ ]);
788
+
789
+ await new Promise((resolve) => setTimeout(resolve, 20));
790
+ expect(formStub.getFieldValue(['a'])).toBeUndefined();
791
+
792
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
793
+ await new Promise((resolve) => setTimeout(resolve, 20));
794
+ expect(formStub.getFieldValue(['a'])).toBeUndefined();
795
+ });
796
+
797
+ it('syncs UI props for mode=default form assignment on configured fields', async () => {
798
+ const engineEmitter = new EventEmitter();
799
+ const blockEmitter = new EventEmitter();
800
+ const formStub = createFormStub();
801
+
802
+ const blockModel: any = {
803
+ uid: 'form-assign-default-ui-sync',
804
+ flowEngine: { emitter: engineEmitter },
805
+ emitter: blockEmitter,
806
+ dispatchEvent: vi.fn(),
807
+ getAclActionName: () => 'create',
808
+ };
809
+
810
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
811
+ runtime.mount({ sync: true });
812
+
813
+ const blockCtx = createFieldContext(runtime);
814
+ blockModel.context = blockCtx;
815
+
816
+ const fieldCtx = createFieldContext(runtime);
817
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
818
+ fieldCtx.defineProperty('fieldPathArray', { value: ['color'] });
819
+
820
+ const colorFieldModel: any = {
821
+ setProps: vi.fn(),
822
+ };
823
+
824
+ const formItemModel: any = {
825
+ uid: 'form-item-color-default-ui-sync',
826
+ context: fieldCtx,
827
+ subModels: { field: colorFieldModel },
828
+ getStepParams: (flowKey: string, stepKey: string) => {
829
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
830
+ return { fieldPath: 'color' };
831
+ }
832
+ return undefined;
833
+ },
834
+ setProps: vi.fn(),
835
+ };
836
+ fieldCtx.defineProperty('model', { value: formItemModel });
837
+
838
+ blockCtx.defineProperty('engine', {
839
+ value: {
840
+ forEachModel: (visit: (model: any) => void) => visit(formItemModel),
841
+ },
842
+ });
843
+
844
+ runtime.syncAssignRules([
845
+ {
846
+ key: 'color-default',
847
+ enable: true,
848
+ targetPath: 'color',
849
+ mode: 'default',
850
+ condition: { logic: '$and', items: [] },
851
+ value: '#1677FF',
852
+ },
853
+ ]);
854
+
855
+ await waitFor(() => expect(formStub.getFieldValue(['color'])).toBe('#1677FF'));
856
+ expect(colorFieldModel.setProps).toHaveBeenCalledWith({ value: '#1677FF' });
857
+ });
858
+
859
+ it('linkage assignment takes precedence over mode=assign form assignment', async () => {
860
+ const engineEmitter = new EventEmitter();
861
+ const blockEmitter = new EventEmitter();
862
+ const formStub = createFormStub({ b: 'X' });
863
+
864
+ const blockModel: any = {
865
+ uid: 'form-assign-linkage-precedence',
866
+ flowEngine: { emitter: engineEmitter },
867
+ emitter: blockEmitter,
868
+ dispatchEvent: vi.fn(),
869
+ getAclActionName: () => 'create',
870
+ };
871
+
872
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
873
+ runtime.mount({ sync: true });
874
+
875
+ const blockCtx = createFieldContext(runtime);
876
+ blockModel.context = blockCtx;
877
+
878
+ runtime.syncAssignRules([
879
+ {
880
+ key: 'r1',
881
+ enable: true,
882
+ targetPath: 'a',
883
+ mode: 'assign',
884
+ condition: { logic: '$and', items: [] },
885
+ value: '__B__',
886
+ },
887
+ ]);
888
+
889
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
890
+
891
+ // linkage writes to target; form assignment should NOT immediately write back and override
892
+ await runtime.setFormValues(blockCtx, [{ path: ['a'], value: 'LINK' }], { source: 'linkage' });
893
+ expect(formStub.getFieldValue(['a'])).toBe('LINK');
894
+ await new Promise((resolve) => setTimeout(resolve, 30));
895
+ expect(formStub.getFieldValue(['a'])).toBe('LINK');
896
+
897
+ // dependency change should still update via form assignment (if linkage doesn't re-run)
898
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
899
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
900
+ });
901
+
902
+ it('linkage default value takes precedence over mode=default form assignment', async () => {
903
+ const engineEmitter = new EventEmitter();
904
+ const blockEmitter = new EventEmitter();
905
+ const formStub = createFormStub({ b: 'X' });
906
+
907
+ const blockModel: any = {
908
+ uid: 'form-assign-linkage-default-precedence',
909
+ flowEngine: { emitter: engineEmitter },
910
+ emitter: blockEmitter,
911
+ dispatchEvent: vi.fn(),
912
+ getAclActionName: () => 'create',
913
+ };
914
+
915
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
916
+ runtime.mount({ sync: true });
917
+
918
+ const blockCtx = createFieldContext(runtime);
919
+ blockModel.context = blockCtx;
920
+
921
+ const fieldCtx = createFieldContext(runtime);
922
+ fieldCtx.defineProperty('blockModel', { value: blockModel });
923
+ fieldCtx.defineProperty('fieldPathArray', { value: ['a'] });
924
+
925
+ const fieldModel: any = {
926
+ uid: 'field-a-linkage-default-precedence',
927
+ context: fieldCtx,
928
+ props: observable({ initialValue: undefined }),
929
+ getProps: function () {
930
+ return this.props;
931
+ },
932
+ };
933
+ fieldCtx.defineProperty('model', { value: fieldModel });
934
+
935
+ engineEmitter.emit('model:mounted', { model: fieldModel });
936
+
937
+ runtime.syncAssignRules([
938
+ {
939
+ key: 'r1',
940
+ enable: true,
941
+ targetPath: 'a',
942
+ mode: 'default',
943
+ condition: { logic: '$and', items: [] },
944
+ value: '__B__',
945
+ },
946
+ ]);
947
+
948
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
949
+
950
+ // linkage writes initialValue (default semantics) and SHOULD override form assignment default
951
+ fieldModel.props.initialValue = '__LINK__';
952
+
953
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('__LINK__'));
954
+
955
+ // dependency change triggers form assignment default recompute, but linkage default still wins
956
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Y' }], { source: 'user' });
957
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('__LINK__'));
958
+
959
+ // linkage default removed -> form assignment default takes effect again
960
+ fieldModel.props.initialValue = undefined;
961
+
962
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'Z' }], { source: 'user' });
963
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Z'));
964
+ });
965
+
966
+ it('tracks ctx var deps and updates when ctx var changes', async () => {
967
+ const engineEmitter = new EventEmitter();
968
+ const blockEmitter = new EventEmitter();
969
+ const formStub = createFormStub({});
970
+
971
+ const blockModel: any = {
972
+ uid: 'form-assign-ctx-deps',
973
+ flowEngine: { emitter: engineEmitter },
974
+ emitter: blockEmitter,
975
+ dispatchEvent: vi.fn(),
976
+ getAclActionName: () => 'create',
977
+ };
978
+
979
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
980
+ runtime.mount({ sync: true });
981
+
982
+ const store = observable({ someVar: 'X' });
983
+
984
+ const blockCtx = createFieldContext(runtime);
985
+ blockCtx.defineProperty('someVar', { get: () => store.someVar, cache: false });
986
+
987
+ const fieldModel: any = {
988
+ uid: 'field-a-ctx',
989
+ context: { fieldPathArray: ['a'] },
990
+ };
991
+ blockCtx.defineProperty('engine', {
992
+ value: {
993
+ getModel: (id: string) => (id === 'field-a-ctx' ? fieldModel : null),
994
+ },
995
+ });
996
+ blockModel.context = blockCtx;
997
+
998
+ runtime.syncAssignRules([
999
+ {
1000
+ key: 'r1',
1001
+ enable: true,
1002
+ targetPath: 'a',
1003
+ mode: 'assign',
1004
+ condition: { logic: '$and', items: [] },
1005
+ value: '{{ ctx.someVar }}',
1006
+ },
1007
+ ]);
1008
+
1009
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
1010
+
1011
+ store.someVar = 'Y';
1012
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('Y'));
1013
+ });
1014
+
1015
+ it('runs assign rule even when target model is not mounted yet', async () => {
1016
+ const engineEmitter = new EventEmitter();
1017
+ const blockEmitter = new EventEmitter();
1018
+ const formStub = createFormStub({ b: 'X' });
1019
+
1020
+ const blockModel: any = {
1021
+ uid: 'form-assign-mount',
1022
+ flowEngine: { emitter: engineEmitter },
1023
+ emitter: blockEmitter,
1024
+ dispatchEvent: vi.fn(),
1025
+ getAclActionName: () => 'create',
1026
+ };
1027
+
1028
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1029
+ runtime.mount({ sync: true });
1030
+
1031
+ const blockCtx = createFieldContext(runtime);
1032
+ const fieldModel: any = {
1033
+ uid: 'field-a-mount',
1034
+ context: { blockModel, fieldPathArray: undefined },
1035
+ };
1036
+ blockCtx.defineProperty('engine', {
1037
+ value: {
1038
+ getModel: (id: string) => (id === 'field-a-mount' ? fieldModel : null),
1039
+ },
1040
+ });
1041
+ blockModel.context = blockCtx;
1042
+
1043
+ runtime.syncAssignRules([
1044
+ {
1045
+ key: 'r1',
1046
+ enable: true,
1047
+ targetPath: 'a',
1048
+ mode: 'assign',
1049
+ condition: { logic: '$and', items: [] },
1050
+ value: '__B__',
1051
+ },
1052
+ ]);
1053
+
1054
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('X'));
1055
+ });
1056
+
1057
+ it('only exposes ctx.item under association target paths', async () => {
1058
+ const engineEmitter = new EventEmitter();
1059
+ const blockEmitter = new EventEmitter();
1060
+ const formStub = createFormStub({});
1061
+
1062
+ const blockModel: any = {
1063
+ uid: 'form-assign-item-scope',
1064
+ flowEngine: { emitter: engineEmitter },
1065
+ emitter: blockEmitter,
1066
+ dispatchEvent: vi.fn(),
1067
+ getAclActionName: () => 'create',
1068
+ };
1069
+
1070
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1071
+ runtime.mount({ sync: true });
1072
+
1073
+ const blockCtx = createFieldContext(runtime);
1074
+ // 提供集合元信息,避免因 collection 缺失导致 item 直接为 undefined 的短路逻辑误判。
1075
+ blockCtx.defineProperty('collection', { value: { getField: () => null } });
1076
+ blockModel.context = blockCtx;
1077
+
1078
+ runtime.syncAssignRules([
1079
+ {
1080
+ key: 'r1',
1081
+ enable: true,
1082
+ targetPath: 'a',
1083
+ mode: 'assign',
1084
+ condition: { logic: '$and', items: [] },
1085
+ value: {
1086
+ code: "return typeof ctx.item === 'undefined' ? 'YES' : 'NO'",
1087
+ version: 'v1',
1088
+ },
1089
+ },
1090
+ ]);
1091
+
1092
+ await waitFor(() => expect(formStub.getFieldValue(['a'])).toBe('YES'));
1093
+ });
1094
+
1095
+ it('supports ctx.item.index in condition for to-many subform list (fork models)', async () => {
1096
+ const engineEmitter = new EventEmitter();
1097
+ const blockEmitter = new EventEmitter();
1098
+ const formStub = createFormStub({ users: [{ nickname: 'A' }, { nickname: 'B' }] });
1099
+
1100
+ const blockModel: any = {
1101
+ uid: 'form-assign-index',
1102
+ flowEngine: { emitter: engineEmitter },
1103
+ emitter: blockEmitter,
1104
+ dispatchEvent: vi.fn(),
1105
+ getAclActionName: () => 'create',
1106
+ };
1107
+
1108
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1109
+ runtime.mount({ sync: true });
1110
+
1111
+ const blockCtx = createFieldContext(runtime);
1112
+ // 提供集合元信息:RuleEngine 需要依赖 rootCollection 来识别对多关联并计算 ctx.item.index
1113
+ const userRowCollection: any = { getField: () => null };
1114
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1115
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1116
+ blockCtx.defineProperty('collection', { value: rootCollection });
1117
+ blockModel.context = blockCtx;
1118
+
1119
+ const masterModel: any = {
1120
+ uid: 'users.nickname',
1121
+ subModels: { field: {} },
1122
+ getStepParams(flowKey: string, stepKey: string) {
1123
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1124
+ return { fieldPath: 'users.nickname' };
1125
+ }
1126
+ return undefined;
1127
+ },
1128
+ };
1129
+ const masterCtx = createFieldContext(runtime);
1130
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1131
+ masterCtx.defineProperty('model', { value: masterModel });
1132
+ masterModel.context = masterCtx;
1133
+
1134
+ const createRowModel = (forkId: string) => {
1135
+ const rowModel: any = {
1136
+ uid: 'users.nickname',
1137
+ isFork: true,
1138
+ forkId,
1139
+ subModels: { field: {} },
1140
+ getStepParams(flowKey: string, stepKey: string) {
1141
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1142
+ return { fieldPath: 'users.nickname' };
1143
+ }
1144
+ return undefined;
1145
+ },
1146
+ };
1147
+ const rowCtx = createFieldContext(runtime);
1148
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1149
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1150
+ rowCtx.defineProperty('model', { value: rowModel });
1151
+ rowModel.context = rowCtx;
1152
+ return rowModel;
1153
+ };
1154
+
1155
+ const row0 = createRowModel('users:0');
1156
+ const row1 = createRowModel('users:1');
1157
+
1158
+ // 在真实 FlowEngine 中,fork models 不会出现在 engine.forEachModel 的遍历里;
1159
+ // 需要从 master.forks 中补齐,确保 syncAssignRules 后仍能覆盖已挂载的子表单行。
1160
+ masterModel.forks = new Set([row0, row1]);
1161
+
1162
+ blockCtx.defineProperty('engine', {
1163
+ value: {
1164
+ forEachModel: (cb: any) => {
1165
+ cb(masterModel);
1166
+ },
1167
+ },
1168
+ });
1169
+
1170
+ runtime.syncAssignRules([
1171
+ {
1172
+ key: 'r1',
1173
+ enable: true,
1174
+ targetPath: 'users.nickname',
1175
+ mode: 'assign',
1176
+ condition: {
1177
+ logic: '$and',
1178
+ items: [{ path: '{{ ctx.item.index }}', operator: '$eq', value: 1 }],
1179
+ },
1180
+ value: 'Z',
1181
+ },
1182
+ ]);
1183
+
1184
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'nickname'])).toBe('Z'));
1185
+ expect(formStub.getFieldValue(['users', 0, 'nickname'])).toBe('A');
1186
+
1187
+ // 重新同步规则(模拟保存后生效):fork 行上的规则也应被重新注册并生效
1188
+ runtime.syncAssignRules([
1189
+ {
1190
+ key: 'r1',
1191
+ enable: true,
1192
+ targetPath: 'users.nickname',
1193
+ mode: 'assign',
1194
+ condition: {
1195
+ logic: '$and',
1196
+ items: [{ path: '{{ ctx.item.index }}', operator: '$eq', value: 1 }],
1197
+ },
1198
+ value: 'Q',
1199
+ },
1200
+ ]);
1201
+
1202
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'nickname'])).toBe('Q'));
1203
+ });
1204
+
1205
+ it('supports ctx.item.length in condition for to-many subform list (fork models)', async () => {
1206
+ const engineEmitter = new EventEmitter();
1207
+ const blockEmitter = new EventEmitter();
1208
+ const formStub = createFormStub({ users: [{ nickname: 'A' }, { nickname: 'B' }] });
1209
+
1210
+ const blockModel: any = {
1211
+ uid: 'form-assign-length',
1212
+ flowEngine: { emitter: engineEmitter },
1213
+ emitter: blockEmitter,
1214
+ dispatchEvent: vi.fn(),
1215
+ getAclActionName: () => 'create',
1216
+ };
1217
+
1218
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1219
+ runtime.mount({ sync: true });
1220
+
1221
+ const blockCtx = createFieldContext(runtime);
1222
+ // 提供集合元信息:RuleEngine 需要依赖 rootCollection 来识别对多关联并计算 ctx.item.length
1223
+ const userRowCollection: any = { getField: () => null };
1224
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1225
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1226
+ blockCtx.defineProperty('collection', { value: rootCollection });
1227
+ blockModel.context = blockCtx;
1228
+
1229
+ const masterModel: any = {
1230
+ uid: 'users.nickname',
1231
+ subModels: { field: {} },
1232
+ getStepParams(flowKey: string, stepKey: string) {
1233
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1234
+ return { fieldPath: 'users.nickname' };
1235
+ }
1236
+ return undefined;
1237
+ },
1238
+ };
1239
+ const masterCtx = createFieldContext(runtime);
1240
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1241
+ masterCtx.defineProperty('model', { value: masterModel });
1242
+ masterModel.context = masterCtx;
1243
+
1244
+ const createRowModel = (forkId: string) => {
1245
+ const rowModel: any = {
1246
+ uid: 'users.nickname',
1247
+ isFork: true,
1248
+ forkId,
1249
+ subModels: { field: {} },
1250
+ getStepParams(flowKey: string, stepKey: string) {
1251
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1252
+ return { fieldPath: 'users.nickname' };
1253
+ }
1254
+ return undefined;
1255
+ },
1256
+ };
1257
+ const rowCtx = createFieldContext(runtime);
1258
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1259
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1260
+ rowCtx.defineProperty('model', { value: rowModel });
1261
+ rowModel.context = rowCtx;
1262
+ return rowModel;
1263
+ };
1264
+
1265
+ const row0 = createRowModel('users:0');
1266
+ const row1 = createRowModel('users:1');
1267
+ masterModel.forks = new Set([row0, row1]);
1268
+
1269
+ blockCtx.defineProperty('engine', {
1270
+ value: {
1271
+ forEachModel: (cb: any) => {
1272
+ cb(masterModel);
1273
+ },
1274
+ },
1275
+ });
1276
+
1277
+ runtime.syncAssignRules([
1278
+ {
1279
+ key: 'r1',
1280
+ enable: true,
1281
+ targetPath: 'users.nickname',
1282
+ mode: 'assign',
1283
+ condition: {
1284
+ logic: '$and',
1285
+ items: [
1286
+ { path: '{{ ctx.item.index }}', operator: '$eq', value: 1 },
1287
+ { path: '{{ ctx.item.length }}', operator: '$eq', value: 2 },
1288
+ ],
1289
+ },
1290
+ value: 'Z',
1291
+ },
1292
+ ]);
1293
+
1294
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'nickname'])).toBe('Z'));
1295
+ expect(formStub.getFieldValue(['users', 0, 'nickname'])).toBe('A');
1296
+ });
1297
+
1298
+ it('supports ctx.item.value.* in assign rule value and updates on row value changes (to-many fork models)', async () => {
1299
+ const engineEmitter = new EventEmitter();
1300
+ const blockEmitter = new EventEmitter();
1301
+ const formStub = createFormStub({
1302
+ users: [
1303
+ { nickname: 'A', display: '' },
1304
+ { nickname: 'B', display: '' },
1305
+ ],
1306
+ });
1307
+
1308
+ const blockModel: any = {
1309
+ uid: 'form-assign-item-value',
1310
+ flowEngine: { emitter: engineEmitter },
1311
+ emitter: blockEmitter,
1312
+ dispatchEvent: vi.fn(),
1313
+ getAclActionName: () => 'create',
1314
+ };
1315
+
1316
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1317
+ runtime.mount({ sync: true });
1318
+
1319
+ const blockCtx = createFieldContext(runtime);
1320
+ // 提供集合元信息:RuleEngine 需要依赖 rootCollection 来识别对多关联并计算 ctx.item.value
1321
+ const userRowCollection: any = { getField: () => null };
1322
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1323
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1324
+ blockCtx.defineProperty('collection', { value: rootCollection });
1325
+ blockModel.context = blockCtx;
1326
+
1327
+ const masterModel: any = {
1328
+ uid: 'users.display',
1329
+ subModels: { field: {} },
1330
+ getStepParams(flowKey: string, stepKey: string) {
1331
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1332
+ return { fieldPath: 'users.display' };
1333
+ }
1334
+ return undefined;
1335
+ },
1336
+ };
1337
+ const masterCtx = createFieldContext(runtime);
1338
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1339
+ masterCtx.defineProperty('model', { value: masterModel });
1340
+ masterModel.context = masterCtx;
1341
+
1342
+ const createRowModel = (forkId: string) => {
1343
+ const rowModel: any = {
1344
+ uid: 'users.display',
1345
+ isFork: true,
1346
+ forkId,
1347
+ subModels: { field: {} },
1348
+ getStepParams(flowKey: string, stepKey: string) {
1349
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1350
+ return { fieldPath: 'users.display' };
1351
+ }
1352
+ return undefined;
1353
+ },
1354
+ };
1355
+ const rowCtx = createFieldContext(runtime);
1356
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1357
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1358
+ rowCtx.defineProperty('model', { value: rowModel });
1359
+ rowModel.context = rowCtx;
1360
+ return rowModel;
1361
+ };
1362
+
1363
+ const row0 = createRowModel('users:0');
1364
+ const row1 = createRowModel('users:1');
1365
+ masterModel.forks = new Set([row0, row1]);
1366
+
1367
+ blockCtx.defineProperty('engine', {
1368
+ value: {
1369
+ forEachModel: (cb: any) => {
1370
+ cb(masterModel);
1371
+ },
1372
+ },
1373
+ });
1374
+
1375
+ runtime.syncAssignRules([
1376
+ {
1377
+ key: 'r1',
1378
+ enable: true,
1379
+ targetPath: 'users.display',
1380
+ mode: 'assign',
1381
+ condition: { logic: '$and', items: [] },
1382
+ value: '{{ ctx.item.value.nickname }}',
1383
+ },
1384
+ ]);
1385
+
1386
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'display'])).toBe('A'));
1387
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'display'])).toBe('B'));
1388
+
1389
+ await runtime.setFormValues(blockCtx, [{ path: ['users', 1, 'nickname'], value: 'C' }], { source: 'user' });
1390
+
1391
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'display'])).toBe('C'));
1392
+ expect(formStub.getFieldValue(['users', 0, 'display'])).toBe('A');
1393
+ });
1394
+
1395
+ it('supports ctx.getVar("ctx.item.value.*") in RunJS assign value for to-many fork models', async () => {
1396
+ const engineEmitter = new EventEmitter();
1397
+ const blockEmitter = new EventEmitter();
1398
+ const formStub = createFormStub({
1399
+ users: [
1400
+ { nickname: 'A', display: '' },
1401
+ { nickname: 'B', display: '' },
1402
+ ],
1403
+ });
1404
+
1405
+ const blockModel: any = {
1406
+ uid: 'form-assign-item-getvar',
1407
+ flowEngine: { emitter: engineEmitter },
1408
+ emitter: blockEmitter,
1409
+ dispatchEvent: vi.fn(),
1410
+ getAclActionName: () => 'create',
1411
+ };
1412
+
1413
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1414
+ runtime.mount({ sync: true });
1415
+
1416
+ const blockCtx = createFieldContext(runtime);
1417
+ const userRowCollection: any = { getField: () => null };
1418
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1419
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1420
+ blockCtx.defineProperty('collection', { value: rootCollection });
1421
+ blockModel.context = blockCtx;
1422
+
1423
+ const masterModel: any = {
1424
+ uid: 'users.display.getvar',
1425
+ subModels: { field: {} },
1426
+ getStepParams(flowKey: string, stepKey: string) {
1427
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1428
+ return { fieldPath: 'users.display' };
1429
+ }
1430
+ return undefined;
1431
+ },
1432
+ };
1433
+ const masterCtx = createFieldContext(runtime);
1434
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1435
+ masterCtx.defineProperty('model', { value: masterModel });
1436
+ masterModel.context = masterCtx;
1437
+
1438
+ const createRowModel = (forkId: string) => {
1439
+ const rowModel: any = {
1440
+ uid: 'users.display.getvar',
1441
+ isFork: true,
1442
+ forkId,
1443
+ subModels: { field: {} },
1444
+ getStepParams(flowKey: string, stepKey: string) {
1445
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1446
+ return { fieldPath: 'users.display' };
1447
+ }
1448
+ return undefined;
1449
+ },
1450
+ };
1451
+ const rowCtx = createFieldContext(runtime);
1452
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1453
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1454
+ rowCtx.defineProperty('model', { value: rowModel });
1455
+ rowModel.context = rowCtx;
1456
+ return rowModel;
1457
+ };
1458
+
1459
+ const row0 = createRowModel('users:0');
1460
+ const row1 = createRowModel('users:1');
1461
+ masterModel.forks = new Set([row0, row1]);
1462
+
1463
+ blockCtx.defineProperty('engine', {
1464
+ value: {
1465
+ forEachModel: (cb: any) => {
1466
+ cb(masterModel);
1467
+ },
1468
+ },
1469
+ });
1470
+
1471
+ runtime.syncAssignRules([
1472
+ {
1473
+ key: 'r-getvar-item',
1474
+ enable: true,
1475
+ targetPath: 'users.display',
1476
+ mode: 'assign',
1477
+ condition: { logic: '$and', items: [] },
1478
+ value: {
1479
+ code: `const nickname = await ctx.getVar('ctx.item.value.nickname'); return nickname;`,
1480
+ version: 'v2',
1481
+ },
1482
+ },
1483
+ ]);
1484
+
1485
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'display'])).toBe('A'));
1486
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'display'])).toBe('B'));
1487
+
1488
+ await runtime.setFormValues(blockCtx, [{ path: ['users', 1, 'nickname'], value: 'C' }], { source: 'user' });
1489
+
1490
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'display'])).toBe('C'));
1491
+ expect(formStub.getFieldValue(['users', 0, 'display'])).toBe('A');
1492
+ });
1493
+
1494
+ it('skips default assign rules in update mode for existing to-many row even if list container became explicit', async () => {
1495
+ const engineEmitter = new EventEmitter();
1496
+ const blockEmitter = new EventEmitter();
1497
+ const formStub = createFormStub({ users: [{ name: '' }, { name: '' }] });
1498
+
1499
+ const blockModel: any = {
1500
+ uid: 'form-assign-default-update',
1501
+ flowEngine: { emitter: engineEmitter },
1502
+ emitter: blockEmitter,
1503
+ dispatchEvent: vi.fn(),
1504
+ getAclActionName: () => 'update',
1505
+ };
1506
+
1507
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1508
+ runtime.mount({ sync: true });
1509
+
1510
+ const blockCtx = createFieldContext(runtime);
1511
+ // 提供集合元信息:识别对多关联,并计算 ctx.item.index
1512
+ const userRowCollection: any = { getField: () => null };
1513
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1514
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1515
+ blockCtx.defineProperty('collection', { value: rootCollection });
1516
+ blockModel.context = blockCtx;
1517
+
1518
+ // 模拟:用户在子表单列表中“增删项/添加行”时,外部系统会触发 users 的 onValuesChange,导致 users 被标记为 explicit。
1519
+ runtime.handleFormValuesChange({ users: [{ name: '' }, { name: '' }] }, formStub.getFieldsValue());
1520
+
1521
+ const masterModel: any = {
1522
+ uid: 'users.name',
1523
+ subModels: { field: {} },
1524
+ getStepParams(flowKey: string, stepKey: string) {
1525
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1526
+ return { fieldPath: 'users.name' };
1527
+ }
1528
+ return undefined;
1529
+ },
1530
+ };
1531
+ const masterCtx = createFieldContext(runtime);
1532
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1533
+ masterCtx.defineProperty('model', { value: masterModel });
1534
+ masterModel.context = masterCtx;
1535
+
1536
+ const createRowModel = (forkId: string, rowIndex: number, rowIsNew: boolean) => {
1537
+ const rowModel: any = {
1538
+ uid: 'users.name',
1539
+ isFork: true,
1540
+ forkId,
1541
+ subModels: { field: {} },
1542
+ getStepParams(flowKey: string, stepKey: string) {
1543
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1544
+ return { fieldPath: 'users.name' };
1545
+ }
1546
+ return undefined;
1547
+ },
1548
+ };
1549
+ const rowCtx = createFieldContext(runtime);
1550
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1551
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1552
+ rowCtx.defineProperty('item', {
1553
+ get: () => {
1554
+ const row = formStub.getFieldValue(['users', rowIndex]);
1555
+ const list = formStub.getFieldValue(['users']);
1556
+ return {
1557
+ index: rowIndex,
1558
+ length: Array.isArray(list) ? list.length : undefined,
1559
+ __is_new__: rowIsNew,
1560
+ __is_stored__: !rowIsNew,
1561
+ value: row,
1562
+ };
1563
+ },
1564
+ cache: false,
1565
+ });
1566
+ rowCtx.defineProperty('model', { value: rowModel });
1567
+ rowModel.context = rowCtx;
1568
+ return rowModel;
1569
+ };
1570
+
1571
+ const row0 = createRowModel('users:0', 0, false);
1572
+ const row1 = createRowModel('users:1', 1, false);
1573
+ masterModel.forks = new Set([row0, row1]);
1574
+
1575
+ blockCtx.defineProperty('engine', {
1576
+ value: {
1577
+ forEachModel: (cb: any) => {
1578
+ cb(masterModel);
1579
+ },
1580
+ },
1581
+ });
1582
+
1583
+ runtime.syncAssignRules([
1584
+ {
1585
+ key: 'r1',
1586
+ enable: true,
1587
+ targetPath: 'users.name',
1588
+ mode: 'default',
1589
+ condition: {
1590
+ logic: '$and',
1591
+ items: [{ path: '{{ ctx.item.index }}', operator: '$eq', value: 1 }],
1592
+ },
1593
+ value: 'Z',
1594
+ },
1595
+ ]);
1596
+
1597
+ await new Promise((resolve) => setTimeout(resolve, 20));
1598
+ expect(formStub.getFieldValue(['users', 1, 'name'])).toBe('');
1599
+ expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('');
1600
+ });
1601
+
1602
+ it('applies default assign rules for newly added to-many row in update mode', async () => {
1603
+ const engineEmitter = new EventEmitter();
1604
+ const blockEmitter = new EventEmitter();
1605
+ const formStub = createFormStub({ users: [{ name: '' }, { name: '', __is_new__: true }] });
1606
+
1607
+ const blockModel: any = {
1608
+ uid: 'form-assign-default-update-new-row',
1609
+ flowEngine: { emitter: engineEmitter },
1610
+ emitter: blockEmitter,
1611
+ dispatchEvent: vi.fn(),
1612
+ getAclActionName: () => 'update',
1613
+ };
1614
+
1615
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1616
+ runtime.mount({ sync: true });
1617
+
1618
+ const blockCtx = createFieldContext(runtime);
1619
+ const userRowCollection: any = { getField: () => null };
1620
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
1621
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
1622
+ blockCtx.defineProperty('collection', { value: rootCollection });
1623
+ blockModel.context = blockCtx;
1624
+ runtime.handleFormValuesChange(
1625
+ { users: [{ name: '' }, { name: '', __is_new__: true }] },
1626
+ formStub.getFieldsValue(),
1627
+ );
1628
+
1629
+ const masterModel: any = {
1630
+ uid: 'users.name',
1631
+ subModels: { field: {} },
1632
+ getStepParams(flowKey: string, stepKey: string) {
1633
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1634
+ return { fieldPath: 'users.name' };
1635
+ }
1636
+ return undefined;
1637
+ },
1638
+ };
1639
+ const masterCtx = createFieldContext(runtime);
1640
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1641
+ masterCtx.defineProperty('model', { value: masterModel });
1642
+ masterModel.context = masterCtx;
1643
+
1644
+ const createRowModel = (forkId: string, rowIndex: number, rowIsNew: boolean) => {
1645
+ const rowModel: any = {
1646
+ uid: 'users.name',
1647
+ isFork: true,
1648
+ forkId,
1649
+ subModels: { field: {} },
1650
+ getStepParams(flowKey: string, stepKey: string) {
1651
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1652
+ return { fieldPath: 'users.name' };
1653
+ }
1654
+ return undefined;
1655
+ },
1656
+ };
1657
+ const rowCtx = createFieldContext(runtime);
1658
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1659
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
1660
+ rowCtx.defineProperty('item', {
1661
+ get: () => {
1662
+ const row = formStub.getFieldValue(['users', rowIndex]);
1663
+ const list = formStub.getFieldValue(['users']);
1664
+ return {
1665
+ index: rowIndex,
1666
+ length: Array.isArray(list) ? list.length : undefined,
1667
+ __is_new__: rowIsNew,
1668
+ __is_stored__: !rowIsNew,
1669
+ value: row,
1670
+ };
1671
+ },
1672
+ cache: false,
1673
+ });
1674
+ rowCtx.defineProperty('model', { value: rowModel });
1675
+ rowModel.context = rowCtx;
1676
+ return rowModel;
1677
+ };
1678
+
1679
+ const row0 = createRowModel('users:0', 0, false);
1680
+ const row1 = createRowModel('users:1', 1, true);
1681
+ masterModel.forks = new Set([row0, row1]);
1682
+
1683
+ blockCtx.defineProperty('engine', {
1684
+ value: {
1685
+ forEachModel: (cb: any) => {
1686
+ cb(masterModel);
1687
+ },
1688
+ },
1689
+ });
1690
+
1691
+ runtime.syncAssignRules([
1692
+ {
1693
+ key: 'r1',
1694
+ enable: true,
1695
+ targetPath: 'users.name',
1696
+ mode: 'default',
1697
+ condition: {
1698
+ logic: '$and',
1699
+ items: [{ path: '{{ ctx.item.index }}', operator: '$eq', value: 1 }],
1700
+ },
1701
+ value: 'Z',
1702
+ },
1703
+ ]);
1704
+
1705
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'name'])).toBe('Z'));
1706
+ expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('');
1707
+ });
1708
+
1709
+ it('clears explicit state for deleted to-many rows even when allValues is stale', () => {
1710
+ const engineEmitter = new EventEmitter();
1711
+ const blockEmitter = new EventEmitter();
1712
+ const formStub = createFormStub({
1713
+ roles: [{ title: 'Z', __is_new__: true }],
1714
+ });
1715
+
1716
+ const blockModel: any = {
1717
+ uid: 'form-assign-default-create-readd-row',
1718
+ flowEngine: { emitter: engineEmitter },
1719
+ emitter: blockEmitter,
1720
+ dispatchEvent: vi.fn(),
1721
+ getAclActionName: () => 'create',
1722
+ };
1723
+
1724
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1725
+ runtime.mount({ sync: true });
1726
+
1727
+ lodashSet((formStub as any).__store, ['roles', 0, 'title'], 'custom');
1728
+ runtime.handleFormFieldsChange([{ name: ['roles', 0, 'title'], touched: true } as any]);
1729
+ expect((runtime as any).findExplicitHit('roles[0].title')).toBe('roles[0].title');
1730
+
1731
+ const staleAllValues = { roles: [{ title: 'custom', __is_new__: true }] };
1732
+ lodashSet((formStub as any).__store, ['roles'], []);
1733
+ runtime.handleFormValuesChange({ roles: [] }, staleAllValues);
1734
+
1735
+ expect((runtime as any).findExplicitHit('roles[0].title')).toBeNull();
1736
+ });
1737
+
1738
+ it('moves explicit state with filterTargetKey identified to-many rows after deleting a preceding row', () => {
1739
+ const engineEmitter = new EventEmitter();
1740
+ const blockEmitter = new EventEmitter();
1741
+ const formStub = createFormStub({
1742
+ roles: [
1743
+ { code: 'admin', title: 'same-title' },
1744
+ { code: 'editor', title: 'custom' },
1745
+ ],
1746
+ });
1747
+
1748
+ const blockModel: any = {
1749
+ uid: 'form-assign-default-create-shift-row',
1750
+ flowEngine: { emitter: engineEmitter },
1751
+ emitter: blockEmitter,
1752
+ dispatchEvent: vi.fn(),
1753
+ getAclActionName: () => 'create',
1754
+ };
1755
+
1756
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1757
+ runtime.mount({ sync: true });
1758
+
1759
+ const blockCtx = createFieldContext(runtime);
1760
+ const roleRowCollection: any = { getField: () => null, filterTargetKey: 'code' };
1761
+ const rolesField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: roleRowCollection };
1762
+ const rootCollection: any = { getField: (name: string) => (name === 'roles' ? rolesField : null) };
1763
+ blockCtx.defineProperty('collection', { value: rootCollection });
1764
+ blockModel.context = blockCtx;
1765
+
1766
+ lodashSet((formStub as any).__store, ['roles', 1, 'title'], 'same-title');
1767
+ runtime.handleFormFieldsChange([{ name: ['roles', 1, 'title'], touched: true } as any]);
1768
+ expect((runtime as any).findExplicitHit('roles[1].title')).toBe('roles[1].title');
1769
+
1770
+ const shiftedRow = formStub.getFieldValue(['roles', 1]);
1771
+ lodashSet((formStub as any).__store, ['roles'], [shiftedRow]);
1772
+ runtime.handleFormValuesChange({ roles: [shiftedRow] }, formStub.getFieldsValue());
1773
+
1774
+ expect((runtime as any).findExplicitHit('roles[1].title')).toBeNull();
1775
+ expect((runtime as any).findExplicitHit('roles[0].title')).toBe('roles[0].title');
1776
+ });
1777
+
1778
+ it('moves observable binding writes with filterTargetKey identified rows after deleting a preceding row', async () => {
1779
+ const engineEmitter = new EventEmitter();
1780
+ const blockEmitter = new EventEmitter();
1781
+ const formStub = createFormStub({
1782
+ roles: [{ code: 'admin' }, { code: 'editor' }],
1783
+ });
1784
+
1785
+ const blockModel: any = {
1786
+ uid: 'form-assign-default-observable-shift-row',
1787
+ flowEngine: { emitter: engineEmitter },
1788
+ emitter: blockEmitter,
1789
+ dispatchEvent: vi.fn(),
1790
+ getAclActionName: () => 'create',
1791
+ };
1792
+
1793
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1794
+ runtime.mount({ sync: true });
1795
+
1796
+ const blockCtx = createFieldContext(runtime);
1797
+ const roleRowCollection: any = { getField: () => null, filterTargetKey: 'code' };
1798
+ const rolesField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: roleRowCollection };
1799
+ const rootCollection: any = { getField: (name: string) => (name === 'roles' ? rolesField : null) };
1800
+ blockCtx.defineProperty('collection', { value: rootCollection });
1801
+ blockCtx.defineProperty('fieldIndex', { value: ['roles:1'] });
1802
+ blockModel.context = blockCtx;
1803
+
1804
+ const defaultMeta = observable({ label: 'Editor' });
1805
+ await runtime.setFormValues(blockCtx, [{ path: 'roles.meta', value: defaultMeta }], {
1806
+ source: 'linkage',
1807
+ markExplicit: false,
1808
+ });
1809
+
1810
+ expect(formStub.getFieldValue(['roles', 1, 'meta'])).toEqual({ label: 'Editor' });
1811
+
1812
+ const shiftedRow = formStub.getFieldValue(['roles', 1]);
1813
+ lodashSet((formStub as any).__store, ['roles'], [shiftedRow]);
1814
+ runtime.handleFormValuesChange({ roles: [shiftedRow] }, formStub.getFieldsValue());
1815
+
1816
+ defaultMeta.label = 'Updated';
1817
+
1818
+ await waitFor(() => expect(formStub.getFieldValue(['roles', 0, 'meta'])).toEqual({ label: 'Updated' }));
1819
+ expect(formStub.getFieldValue(['roles', 1])).toBeUndefined();
1820
+ });
1821
+
1822
+ it('skips stale to-many row rules after the row index is out of bounds', async () => {
1823
+ const engineEmitter = new EventEmitter();
1824
+ const blockEmitter = new EventEmitter();
1825
+ const formStub = createFormStub({
1826
+ roles: [{ title: 'Z', __is_new__: true, __index__: 'row-1' }],
1827
+ });
1828
+
1829
+ const blockModel: any = {
1830
+ uid: 'form-assign-default-stale-row',
1831
+ flowEngine: { emitter: engineEmitter },
1832
+ emitter: blockEmitter,
1833
+ dispatchEvent: vi.fn(),
1834
+ getAclActionName: () => 'create',
1835
+ };
1836
+
1837
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1838
+ runtime.mount({ sync: true });
1839
+
1840
+ const blockCtx = createFieldContext(runtime);
1841
+ const roleRowCollection: any = { getField: () => null };
1842
+ const rolesField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: roleRowCollection };
1843
+ const rootCollection: any = { getField: (name: string) => (name === 'roles' ? rolesField : null) };
1844
+ blockCtx.defineProperty('collection', { value: rootCollection });
1845
+ blockModel.context = blockCtx;
1846
+
1847
+ const masterModel: any = {
1848
+ uid: 'roles.title',
1849
+ subModels: { field: {} },
1850
+ getStepParams(flowKey: string, stepKey: string) {
1851
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1852
+ return { fieldPath: 'roles.title' };
1853
+ }
1854
+ return undefined;
1855
+ },
1856
+ };
1857
+ const masterCtx = createFieldContext(runtime);
1858
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1859
+ masterCtx.defineProperty('model', { value: masterModel });
1860
+ masterModel.context = masterCtx;
1861
+
1862
+ const staleRow: any = {
1863
+ uid: 'roles.title',
1864
+ isFork: true,
1865
+ forkId: 'roles:stale',
1866
+ subModels: { field: {} },
1867
+ getStepParams(flowKey: string, stepKey: string) {
1868
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1869
+ return { fieldPath: 'roles.title' };
1870
+ }
1871
+ return undefined;
1872
+ },
1873
+ };
1874
+ const staleCtx = createFieldContext(runtime);
1875
+ staleCtx.defineProperty('blockModel', { value: blockModel });
1876
+ staleCtx.defineProperty('fieldIndex', { value: ['roles:1'] });
1877
+ staleCtx.defineProperty('item', {
1878
+ value: {
1879
+ index: 1,
1880
+ length: 1,
1881
+ __is_new__: true,
1882
+ value: { title: 'Z', __is_new__: true, __index__: 'row-1' },
1883
+ },
1884
+ });
1885
+ staleCtx.defineProperty('model', { value: staleRow });
1886
+ staleRow.context = staleCtx;
1887
+ masterModel.forks = new Set([staleRow]);
1888
+
1889
+ blockCtx.defineProperty('engine', {
1890
+ value: {
1891
+ forEachModel: (cb: any) => {
1892
+ cb(masterModel);
1893
+ },
1894
+ },
1895
+ });
1896
+
1897
+ runtime.syncAssignRules([
1898
+ {
1899
+ key: 'r1',
1900
+ enable: true,
1901
+ targetPath: 'roles.title',
1902
+ mode: 'default',
1903
+ condition: { logic: '$and', items: [] },
1904
+ value: 'Z',
1905
+ },
1906
+ ]);
1907
+
1908
+ await new Promise((resolve) => setTimeout(resolve, 20));
1909
+ expect(formStub.getFieldValue(['roles'])).toHaveLength(1);
1910
+ expect(formStub.getFieldValue(['roles', 1])).toBeUndefined();
1911
+ });
1912
+
1913
+ it('skips to-many row rules when filterTargetKey identity does not match the current row at that index', async () => {
1914
+ const engineEmitter = new EventEmitter();
1915
+ const blockEmitter = new EventEmitter();
1916
+ const formStub = createFormStub({
1917
+ roles: [{ code: 'admin', title: '' }],
1918
+ });
1919
+
1920
+ const blockModel: any = {
1921
+ uid: 'form-assign-default-mismatched-row',
1922
+ flowEngine: { emitter: engineEmitter },
1923
+ emitter: blockEmitter,
1924
+ dispatchEvent: vi.fn(),
1925
+ getAclActionName: () => 'create',
1926
+ };
1927
+
1928
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
1929
+ runtime.mount({ sync: true });
1930
+
1931
+ const blockCtx = createFieldContext(runtime);
1932
+ const roleRowCollection: any = { getField: () => null, filterTargetKey: 'code' };
1933
+ const rolesField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: roleRowCollection };
1934
+ const rootCollection: any = { getField: (name: string) => (name === 'roles' ? rolesField : null) };
1935
+ blockCtx.defineProperty('collection', { value: rootCollection });
1936
+ blockModel.context = blockCtx;
1937
+
1938
+ const masterModel: any = {
1939
+ uid: 'roles.title',
1940
+ subModels: { field: {} },
1941
+ getStepParams(flowKey: string, stepKey: string) {
1942
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1943
+ return { fieldPath: 'roles.title' };
1944
+ }
1945
+ return undefined;
1946
+ },
1947
+ };
1948
+ const masterCtx = createFieldContext(runtime);
1949
+ masterCtx.defineProperty('blockModel', { value: blockModel });
1950
+ masterCtx.defineProperty('model', { value: masterModel });
1951
+ masterModel.context = masterCtx;
1952
+
1953
+ const mismatchedRow: any = {
1954
+ uid: 'roles.title',
1955
+ isFork: true,
1956
+ forkId: 'roles:mismatched',
1957
+ subModels: { field: {} },
1958
+ getStepParams(flowKey: string, stepKey: string) {
1959
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
1960
+ return { fieldPath: 'roles.title' };
1961
+ }
1962
+ return undefined;
1963
+ },
1964
+ };
1965
+ const rowCtx = createFieldContext(runtime);
1966
+ rowCtx.defineProperty('blockModel', { value: blockModel });
1967
+ rowCtx.defineProperty('fieldIndex', { value: ['roles:0'] });
1968
+ rowCtx.defineProperty('item', {
1969
+ value: {
1970
+ index: 0,
1971
+ length: 1,
1972
+ value: { code: 'editor', title: 'custom' },
1973
+ },
1974
+ });
1975
+ rowCtx.defineProperty('model', { value: mismatchedRow });
1976
+ mismatchedRow.context = rowCtx;
1977
+ masterModel.forks = new Set([mismatchedRow]);
1978
+
1979
+ blockCtx.defineProperty('engine', {
1980
+ value: {
1981
+ forEachModel: (cb: any) => {
1982
+ cb(masterModel);
1983
+ },
1984
+ },
1985
+ });
1986
+
1987
+ runtime.syncAssignRules([
1988
+ {
1989
+ key: 'r1',
1990
+ enable: true,
1991
+ targetPath: 'roles.title',
1992
+ mode: 'default',
1993
+ condition: { logic: '$and', items: [] },
1994
+ value: 'Z',
1995
+ },
1996
+ ]);
1997
+
1998
+ await new Promise((resolve) => setTimeout(resolve, 20));
1999
+ expect(formStub.getFieldValue(['roles', 0, 'title'])).toBe('');
2000
+ });
2001
+
2002
+ it('keeps default-disabled for edited to-many leaf when onValuesChange only provides top-level path', async () => {
2003
+ const engineEmitter = new EventEmitter();
2004
+ const blockEmitter = new EventEmitter();
2005
+ const formStub = createFormStub({ b: 'ABC', users: [{ name: '' }] });
2006
+
2007
+ const blockModel: any = {
2008
+ uid: 'form-assign-default-create-user-clear',
2009
+ flowEngine: { emitter: engineEmitter },
2010
+ emitter: blockEmitter,
2011
+ dispatchEvent: vi.fn(),
2012
+ getAclActionName: () => 'create',
2013
+ };
2014
+
2015
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2016
+ runtime.mount({ sync: true });
2017
+
2018
+ const blockCtx = createFieldContext(runtime);
2019
+ const userRowCollection: any = { getField: () => null };
2020
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
2021
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2022
+ blockCtx.defineProperty('collection', { value: rootCollection });
2023
+ blockModel.context = blockCtx;
2024
+
2025
+ const masterModel: any = {
2026
+ uid: 'users.name',
2027
+ subModels: { field: {} },
2028
+ getStepParams(flowKey: string, stepKey: string) {
2029
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2030
+ return { fieldPath: 'users.name' };
2031
+ }
2032
+ return undefined;
2033
+ },
2034
+ };
2035
+ const masterCtx = createFieldContext(runtime);
2036
+ masterCtx.defineProperty('blockModel', { value: blockModel });
2037
+ masterCtx.defineProperty('model', { value: masterModel });
2038
+ masterModel.context = masterCtx;
2039
+
2040
+ const row0: any = {
2041
+ uid: 'users.name',
2042
+ isFork: true,
2043
+ forkId: 'users:0',
2044
+ subModels: { field: {} },
2045
+ getStepParams(flowKey: string, stepKey: string) {
2046
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2047
+ return { fieldPath: 'users.name' };
2048
+ }
2049
+ return undefined;
2050
+ },
2051
+ };
2052
+ const row0Ctx = createFieldContext(runtime);
2053
+ row0Ctx.defineProperty('blockModel', { value: blockModel });
2054
+ row0Ctx.defineProperty('fieldIndex', { value: ['users:0'] });
2055
+ row0Ctx.defineProperty('model', { value: row0 });
2056
+ row0.context = row0Ctx;
2057
+ masterModel.forks = new Set([row0]);
2058
+
2059
+ blockCtx.defineProperty('engine', {
2060
+ value: {
2061
+ forEachModel: (cb: any) => {
2062
+ cb(masterModel);
2063
+ },
2064
+ },
2065
+ });
2066
+
2067
+ runtime.syncAssignRules([
2068
+ {
2069
+ key: 'r1',
2070
+ enable: true,
2071
+ targetPath: 'users.name',
2072
+ mode: 'default',
2073
+ condition: { logic: '$and', items: [] },
2074
+ value: '__B__',
2075
+ },
2076
+ ]);
2077
+
2078
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('ABC'));
2079
+
2080
+ lodashSet((formStub as any).__store, ['users', 0, 'name'], '');
2081
+ runtime.handleFormFieldsChange([{ name: ['users', 0, 'name'], touched: true } as any]);
2082
+ runtime.handleFormValuesChange({ users: formStub.getFieldValue(['users']) }, formStub.getFieldsValue());
2083
+
2084
+ await new Promise((resolve) => setTimeout(resolve, 20));
2085
+ expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('');
2086
+
2087
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'DEF' }], { source: 'user' });
2088
+
2089
+ await new Promise((resolve) => setTimeout(resolve, 20));
2090
+ expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('');
2091
+ });
2092
+
2093
+ it('keeps default enabled for non-user to-many leaf changes (touched=false)', async () => {
2094
+ const engineEmitter = new EventEmitter();
2095
+ const blockEmitter = new EventEmitter();
2096
+ const formStub = createFormStub({ b: 'ABC', users: [{ name: '' }] });
2097
+
2098
+ const blockModel: any = {
2099
+ uid: 'form-assign-default-create-user-clear-system',
2100
+ flowEngine: { emitter: engineEmitter },
2101
+ emitter: blockEmitter,
2102
+ dispatchEvent: vi.fn(),
2103
+ getAclActionName: () => 'create',
2104
+ };
2105
+
2106
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2107
+ runtime.mount({ sync: true });
2108
+
2109
+ const blockCtx = createFieldContext(runtime);
2110
+ const userRowCollection: any = { getField: () => null };
2111
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
2112
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2113
+ blockCtx.defineProperty('collection', { value: rootCollection });
2114
+ blockModel.context = blockCtx;
2115
+
2116
+ const masterModel: any = {
2117
+ uid: 'users.name',
2118
+ subModels: { field: {} },
2119
+ getStepParams(flowKey: string, stepKey: string) {
2120
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2121
+ return { fieldPath: 'users.name' };
2122
+ }
2123
+ return undefined;
2124
+ },
2125
+ };
2126
+ const masterCtx = createFieldContext(runtime);
2127
+ masterCtx.defineProperty('blockModel', { value: blockModel });
2128
+ masterCtx.defineProperty('model', { value: masterModel });
2129
+ masterModel.context = masterCtx;
2130
+
2131
+ const row0: any = {
2132
+ uid: 'users.name',
2133
+ isFork: true,
2134
+ forkId: 'users:0',
2135
+ subModels: { field: {} },
2136
+ getStepParams(flowKey: string, stepKey: string) {
2137
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2138
+ return { fieldPath: 'users.name' };
2139
+ }
2140
+ return undefined;
2141
+ },
2142
+ };
2143
+ const row0Ctx = createFieldContext(runtime);
2144
+ row0Ctx.defineProperty('blockModel', { value: blockModel });
2145
+ row0Ctx.defineProperty('fieldIndex', { value: ['users:0'] });
2146
+ row0Ctx.defineProperty('model', { value: row0 });
2147
+ row0.context = row0Ctx;
2148
+ masterModel.forks = new Set([row0]);
2149
+
2150
+ blockCtx.defineProperty('engine', {
2151
+ value: {
2152
+ forEachModel: (cb: any) => {
2153
+ cb(masterModel);
2154
+ },
2155
+ },
2156
+ });
2157
+
2158
+ runtime.syncAssignRules([
2159
+ {
2160
+ key: 'r1',
2161
+ enable: true,
2162
+ targetPath: 'users.name',
2163
+ mode: 'default',
2164
+ condition: { logic: '$and', items: [] },
2165
+ value: '__B__',
2166
+ },
2167
+ ]);
2168
+
2169
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('ABC'));
2170
+
2171
+ lodashSet((formStub as any).__store, ['users', 0, 'name'], '');
2172
+ runtime.handleFormFieldsChange([{ name: ['users', 0, 'name'], touched: false } as any]);
2173
+ runtime.handleFormValuesChange({ users: formStub.getFieldValue(['users']) }, formStub.getFieldsValue());
2174
+
2175
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('ABC'));
2176
+ });
2177
+
2178
+ it('keeps to-many defaults available after add-only list structure changes', async () => {
2179
+ const engineEmitter = new EventEmitter();
2180
+ const blockEmitter = new EventEmitter();
2181
+ const formStub = createFormStub({ b: 'ABC', users: [] });
2182
+
2183
+ const blockModel: any = {
2184
+ uid: 'form-assign-default-create-add-row',
2185
+ flowEngine: { emitter: engineEmitter },
2186
+ emitter: blockEmitter,
2187
+ dispatchEvent: vi.fn(),
2188
+ getAclActionName: () => 'create',
2189
+ };
2190
+
2191
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2192
+ runtime.mount({ sync: true });
2193
+
2194
+ const blockCtx = createFieldContext(runtime);
2195
+ const userRowCollection: any = { getField: () => null };
2196
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
2197
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2198
+ blockCtx.defineProperty('collection', { value: rootCollection });
2199
+ blockModel.context = blockCtx;
2200
+
2201
+ const masterModel: any = {
2202
+ uid: 'users.name',
2203
+ subModels: { field: {} },
2204
+ forks: new Set(),
2205
+ getStepParams(flowKey: string, stepKey: string) {
2206
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2207
+ return { fieldPath: 'users.name' };
2208
+ }
2209
+ return undefined;
2210
+ },
2211
+ };
2212
+ const masterCtx = createFieldContext(runtime);
2213
+ masterCtx.defineProperty('blockModel', { value: blockModel });
2214
+ masterCtx.defineProperty('model', { value: masterModel });
2215
+ masterModel.context = masterCtx;
2216
+
2217
+ blockCtx.defineProperty('engine', {
2218
+ value: {
2219
+ forEachModel: (cb: any) => {
2220
+ cb(masterModel);
2221
+ },
2222
+ },
2223
+ });
2224
+
2225
+ runtime.syncAssignRules([
2226
+ {
2227
+ key: 'r1',
2228
+ enable: true,
2229
+ targetPath: 'users.name',
2230
+ mode: 'default',
2231
+ condition: { logic: '$and', items: [] },
2232
+ value: '__B__',
2233
+ },
2234
+ ]);
2235
+
2236
+ await new Promise((resolve) => setTimeout(resolve, 20));
2237
+ expect(formStub.getFieldValue(['users', 0, 'name'])).toBeUndefined();
2238
+
2239
+ lodashSet((formStub as any).__store, ['users'], [{ __is_new__: true, name: '' }]);
2240
+ runtime.handleFormValuesChange({ users: formStub.getFieldValue(['users']) }, formStub.getFieldsValue());
2241
+
2242
+ const row0: any = {
2243
+ uid: 'users.name',
2244
+ isFork: true,
2245
+ forkId: 'users:0',
2246
+ subModels: { field: {} },
2247
+ getStepParams(flowKey: string, stepKey: string) {
2248
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2249
+ return { fieldPath: 'users.name' };
2250
+ }
2251
+ return undefined;
2252
+ },
2253
+ };
2254
+ const row0Ctx = createFieldContext(runtime);
2255
+ row0Ctx.defineProperty('blockModel', { value: blockModel });
2256
+ row0Ctx.defineProperty('fieldIndex', { value: ['users:0'] });
2257
+ row0Ctx.defineProperty('model', { value: row0 });
2258
+ row0.context = row0Ctx;
2259
+ masterModel.forks = new Set([row0]);
2260
+
2261
+ engineEmitter.emit('model:mounted', { model: row0 });
2262
+
2263
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'name'])).toBe('ABC'));
2264
+ });
2265
+
2266
+ it('marks only edited nested to-many leaf as explicit when payload is top-level', async () => {
2267
+ const engineEmitter = new EventEmitter();
2268
+ const blockEmitter = new EventEmitter();
2269
+ const formStub = createFormStub({
2270
+ b: 'ABC',
2271
+ users: [
2272
+ {
2273
+ roles: [{ roleName: '' }, { roleName: '' }],
2274
+ },
2275
+ ],
2276
+ });
2277
+
2278
+ const blockModel: any = {
2279
+ uid: 'form-assign-default-create-nested-roles',
2280
+ flowEngine: { emitter: engineEmitter },
2281
+ emitter: blockEmitter,
2282
+ dispatchEvent: vi.fn(),
2283
+ getAclActionName: () => 'create',
2284
+ };
2285
+
2286
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2287
+ runtime.mount({ sync: true });
2288
+
2289
+ const blockCtx = createFieldContext(runtime);
2290
+ const rolesCollection: any = { getField: () => null };
2291
+ const rolesField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: rolesCollection };
2292
+ const usersItemCollection: any = { getField: (name: string) => (name === 'roles' ? rolesField : null) };
2293
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: usersItemCollection };
2294
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2295
+ blockCtx.defineProperty('collection', { value: rootCollection });
2296
+ blockModel.context = blockCtx;
2297
+
2298
+ const masterModel: any = {
2299
+ uid: 'users.roles.roleName',
2300
+ subModels: { field: {} },
2301
+ getStepParams(flowKey: string, stepKey: string) {
2302
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2303
+ return { fieldPath: 'users.roles.roleName' };
2304
+ }
2305
+ return undefined;
2306
+ },
2307
+ };
2308
+ const masterCtx = createFieldContext(runtime);
2309
+ masterCtx.defineProperty('blockModel', { value: blockModel });
2310
+ masterCtx.defineProperty('model', { value: masterModel });
2311
+ masterModel.context = masterCtx;
2312
+
2313
+ const createRoleRowModel = (forkId: string, roleIndex: number) => {
2314
+ const rowModel: any = {
2315
+ uid: 'users.roles.roleName',
2316
+ isFork: true,
2317
+ forkId,
2318
+ subModels: { field: {} },
2319
+ getStepParams(flowKey: string, stepKey: string) {
2320
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2321
+ return { fieldPath: 'users.roles.roleName' };
2322
+ }
2323
+ return undefined;
2324
+ },
2325
+ };
2326
+ const rowCtx = createFieldContext(runtime);
2327
+ rowCtx.defineProperty('blockModel', { value: blockModel });
2328
+ rowCtx.defineProperty('fieldIndex', { value: ['users:0', `roles:${roleIndex}`] });
2329
+ rowCtx.defineProperty('model', { value: rowModel });
2330
+ rowModel.context = rowCtx;
2331
+ return rowModel;
2332
+ };
2333
+
2334
+ const roleRow0 = createRoleRowModel('users:0|roles:0', 0);
2335
+ const roleRow1 = createRoleRowModel('users:0|roles:1', 1);
2336
+ masterModel.forks = new Set([roleRow0, roleRow1]);
2337
+
2338
+ blockCtx.defineProperty('engine', {
2339
+ value: {
2340
+ forEachModel: (cb: any) => {
2341
+ cb(masterModel);
2342
+ },
2343
+ },
2344
+ });
2345
+
2346
+ runtime.syncAssignRules([
2347
+ {
2348
+ key: 'r1',
2349
+ enable: true,
2350
+ targetPath: 'users.roles.roleName',
2351
+ mode: 'default',
2352
+ condition: { logic: '$and', items: [] },
2353
+ value: '__B__',
2354
+ },
2355
+ ]);
2356
+
2357
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'roles', 0, 'roleName'])).toBe('ABC'));
2358
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'roles', 1, 'roleName'])).toBe('ABC'));
2359
+
2360
+ lodashSet((formStub as any).__store, ['users', 0, 'roles', 0, 'roleName'], '');
2361
+ runtime.handleFormValuesChange({ users: formStub.getFieldValue(['users']) }, formStub.getFieldsValue());
2362
+
2363
+ await new Promise((resolve) => setTimeout(resolve, 20));
2364
+ expect(formStub.getFieldValue(['users', 0, 'roles', 0, 'roleName'])).toBe('');
2365
+ expect(formStub.getFieldValue(['users', 0, 'roles', 1, 'roleName'])).toBe('ABC');
2366
+
2367
+ await runtime.setFormValues(blockCtx, [{ path: ['b'], value: 'DEF' }], { source: 'user' });
2368
+
2369
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'roles', 1, 'roleName'])).toBe('DEF'));
2370
+ expect(formStub.getFieldValue(['users', 0, 'roles', 0, 'roleName'])).toBe('');
2371
+ });
2372
+
2373
+ it('assigns to unconfigured field under to-many association using row grid context', async () => {
2374
+ const engineEmitter = new EventEmitter();
2375
+ const blockEmitter = new EventEmitter();
2376
+ const formStub = createFormStub({ users: [{ nickname: 'A' }, { nickname: 'B' }] });
2377
+
2378
+ const blockModel: any = {
2379
+ uid: 'form-assign-grid-row',
2380
+ flowEngine: { emitter: engineEmitter },
2381
+ emitter: blockEmitter,
2382
+ dispatchEvent: vi.fn(),
2383
+ getAclActionName: () => 'create',
2384
+ };
2385
+
2386
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2387
+ runtime.mount({ sync: true });
2388
+
2389
+ const blockCtx = createFieldContext(runtime);
2390
+ // 提供集合元信息:需要识别 users 为对多关联,并允许解析 users.age 的字段元信息
2391
+ const userRowCollection: any = {
2392
+ getField: (name: string) => ({ name, isAssociationField: () => false }),
2393
+ };
2394
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: userRowCollection };
2395
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2396
+ blockCtx.defineProperty('collection', { value: rootCollection });
2397
+ blockModel.context = blockCtx;
2398
+
2399
+ // 已配置到 UI 的字段(用于让 RuleEngine 识别哪些 targetPath 已有 FormItemModel,避免重复挂载到 row grid)
2400
+ const configuredFieldModel: any = {
2401
+ uid: 'users.nickname',
2402
+ subModels: { field: {} },
2403
+ getStepParams(flowKey: string, stepKey: string) {
2404
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2405
+ return { fieldPath: 'users.nickname' };
2406
+ }
2407
+ return undefined;
2408
+ },
2409
+ };
2410
+ const configuredCtx = createFieldContext(runtime);
2411
+ configuredCtx.defineProperty('blockModel', { value: blockModel });
2412
+ configuredCtx.defineProperty('model', { value: configuredFieldModel });
2413
+ configuredFieldModel.context = configuredCtx;
2414
+
2415
+ // row grid fork models(每行一个),用于承载未配置字段的赋值规则
2416
+ const gridMaster: any = {
2417
+ uid: 'users.grid',
2418
+ subModels: { items: [] },
2419
+ };
2420
+ const gridMasterCtx = createFieldContext(runtime);
2421
+ gridMasterCtx.defineProperty('blockModel', { value: blockModel });
2422
+ gridMasterCtx.defineProperty('model', { value: gridMaster });
2423
+ gridMaster.context = gridMasterCtx;
2424
+
2425
+ const createGridRow = (forkId: string, rowIndex: number) => {
2426
+ const row: any = {
2427
+ uid: 'users.grid',
2428
+ isFork: true,
2429
+ forkId,
2430
+ subModels: { items: [] },
2431
+ };
2432
+ const ctx = createFieldContext(runtime);
2433
+ ctx.defineProperty('blockModel', { value: blockModel });
2434
+ ctx.defineProperty('fieldIndex', { value: [`users:${rowIndex}`] });
2435
+ ctx.defineProperty('model', { value: row });
2436
+ row.context = ctx;
2437
+ return row;
2438
+ };
2439
+
2440
+ const row0 = createGridRow('row0', 0);
2441
+ const row1 = createGridRow('row1', 1);
2442
+ gridMaster.forks = new Set([row0, row1]);
2443
+
2444
+ blockCtx.defineProperty('engine', {
2445
+ value: {
2446
+ forEachModel: (cb: any) => {
2447
+ cb(configuredFieldModel);
2448
+ cb(gridMaster);
2449
+ },
2450
+ },
2451
+ });
2452
+
2453
+ runtime.syncAssignRules([
2454
+ {
2455
+ key: 'r1',
2456
+ enable: true,
2457
+ targetPath: 'users.age',
2458
+ mode: 'assign',
2459
+ condition: {
2460
+ logic: '$and',
2461
+ items: [{ path: '{{ ctx.item.index }}', operator: '$eq', value: 1 }],
2462
+ },
2463
+ value: 99,
2464
+ },
2465
+ ]);
2466
+
2467
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'age'])).toBe(99));
2468
+ expect(formStub.getFieldValue(['users', 0, 'age'])).toBeUndefined();
2469
+ });
2470
+
2471
+ it('supports ctx.item.parentItem.index for nested association under to-many', async () => {
2472
+ const engineEmitter = new EventEmitter();
2473
+ const blockEmitter = new EventEmitter();
2474
+ const formStub = createFormStub({
2475
+ users: [{ user: { id: 1, name: 'Old0' } }, { user: { id: 2, name: 'Old1' } }],
2476
+ });
2477
+
2478
+ const blockModel: any = {
2479
+ uid: 'form-assign-current-parent',
2480
+ flowEngine: { emitter: engineEmitter },
2481
+ emitter: blockEmitter,
2482
+ dispatchEvent: vi.fn(),
2483
+ getAclActionName: () => 'create',
2484
+ };
2485
+
2486
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2487
+ runtime.mount({ sync: true });
2488
+
2489
+ const blockCtx = createFieldContext(runtime);
2490
+
2491
+ const userCollection: any = { getField: () => null };
2492
+ const userField: any = { isAssociationField: () => true, type: 'belongsTo', targetCollection: userCollection };
2493
+ const usersItemCollection: any = { getField: (name: string) => (name === 'user' ? userField : null) };
2494
+ const usersField: any = { isAssociationField: () => true, type: 'hasMany', targetCollection: usersItemCollection };
2495
+ const collection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2496
+
2497
+ blockCtx.defineProperty('collection', { value: collection });
2498
+
2499
+ const createRowModel = (forkId: string) => {
2500
+ const rowModel: any = {
2501
+ uid: `users.user:${forkId}`,
2502
+ isFork: true,
2503
+ forkId,
2504
+ subModels: { field: { context: { collectionField: userField } } },
2505
+ getStepParams(flowKey: string, stepKey: string) {
2506
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2507
+ return { fieldPath: 'users.user' };
2508
+ }
2509
+ return undefined;
2510
+ },
2511
+ };
2512
+ const rowCtx = createFieldContext(runtime);
2513
+ rowCtx.defineProperty('blockModel', { value: blockModel });
2514
+ rowCtx.defineProperty('collection', { value: collection });
2515
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
2516
+ rowCtx.defineProperty('model', { value: rowModel });
2517
+ rowModel.context = rowCtx;
2518
+ return rowModel;
2519
+ };
2520
+
2521
+ const row0 = createRowModel('users:0');
2522
+ const row1 = createRowModel('users:1');
2523
+
2524
+ blockCtx.defineProperty('engine', {
2525
+ value: {
2526
+ forEachModel: (cb: any) => {
2527
+ cb(row0);
2528
+ cb(row1);
2529
+ },
2530
+ },
2531
+ });
2532
+ blockModel.context = blockCtx;
2533
+
2534
+ runtime.syncAssignRules([
2535
+ {
2536
+ key: 'r1',
2537
+ enable: true,
2538
+ targetPath: 'users.user.name',
2539
+ mode: 'assign',
2540
+ condition: {
2541
+ logic: '$and',
2542
+ items: [
2543
+ { path: '{{ ctx.item.parentItem.index }}', operator: '$eq', value: 1 },
2544
+ { path: '{{ ctx.item.parentItem.length }}', operator: '$eq', value: 2 },
2545
+ ],
2546
+ },
2547
+ value: 'Z',
2548
+ },
2549
+ ]);
2550
+
2551
+ await waitFor(() => expect(formStub.getFieldValue(['users', 1, 'user', 'name'])).toBe('Z'));
2552
+ expect(formStub.getFieldValue(['users', 0, 'user', 'name'])).toBe('Old0');
2553
+ });
2554
+
2555
+ it('skips nested association write when association value is empty', async () => {
2556
+ const engineEmitter = new EventEmitter();
2557
+ const blockEmitter = new EventEmitter();
2558
+ const formStub = createFormStub({ user: null });
2559
+
2560
+ const blockModel: any = {
2561
+ uid: 'form-assign-assoc-skip-1',
2562
+ flowEngine: { emitter: engineEmitter },
2563
+ emitter: blockEmitter,
2564
+ dispatchEvent: vi.fn(),
2565
+ getAclActionName: () => 'create',
2566
+ };
2567
+
2568
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2569
+ runtime.mount({ sync: true });
2570
+
2571
+ const blockCtx = createFieldContext(runtime);
2572
+ const userCollection: any = { getField: () => null };
2573
+ const userField: any = { isAssociationField: () => true, targetCollection: userCollection };
2574
+ const collection: any = { getField: (name: string) => (name === 'user' ? userField : null) };
2575
+ blockCtx.defineProperty('collection', { value: collection });
2576
+ blockModel.context = blockCtx;
2577
+
2578
+ runtime.syncAssignRules([
2579
+ {
2580
+ key: 'r1',
2581
+ enable: true,
2582
+ targetPath: 'user.name',
2583
+ mode: 'assign',
2584
+ condition: { logic: '$and', items: [] },
2585
+ value: 'Alice',
2586
+ },
2587
+ ]);
2588
+
2589
+ // user 为空时不应隐式创建关联对象
2590
+ await new Promise((resolve) => setTimeout(resolve, 20));
2591
+ expect(formStub.getFieldValue(['user'])).toBeNull();
2592
+
2593
+ // 当 user 从 null -> {id} 时,应触发重新计算并写入 user.name
2594
+ await runtime.setFormValues(blockCtx, [{ path: ['user'], value: { id: 1 } }], { source: 'user' });
2595
+ await waitFor(() => expect(formStub.getFieldValue(['user', 'name'])).toBe('Alice'));
2596
+ expect(formStub.getFieldValue(['user', 'id'])).toBe(1);
2597
+ });
2598
+
2599
+ it('applies nested association targetKey write when association value is empty', async () => {
2600
+ const engineEmitter = new EventEmitter();
2601
+ const blockEmitter = new EventEmitter();
2602
+ const formStub = createFormStub({ user: null });
2603
+
2604
+ const blockModel: any = {
2605
+ uid: 'form-assign-assoc-tk-1',
2606
+ flowEngine: { emitter: engineEmitter },
2607
+ emitter: blockEmitter,
2608
+ dispatchEvent: vi.fn(),
2609
+ getAclActionName: () => 'create',
2610
+ };
2611
+
2612
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2613
+ runtime.mount({ sync: true });
2614
+
2615
+ const blockCtx = createFieldContext(runtime);
2616
+ const userCollection: any = { getField: () => null, filterTargetKey: 'id' };
2617
+ const userField: any = { isAssociationField: () => true, type: 'belongsTo', targetCollection: userCollection };
2618
+ const collection: any = { getField: (name: string) => (name === 'user' ? userField : null) };
2619
+ blockCtx.defineProperty('collection', { value: collection });
2620
+ blockModel.context = blockCtx;
2621
+
2622
+ runtime.syncAssignRules([
2623
+ {
2624
+ key: 'r1',
2625
+ enable: true,
2626
+ targetPath: 'user.id',
2627
+ mode: 'default',
2628
+ condition: { logic: '$and', items: [] },
2629
+ value: 123,
2630
+ },
2631
+ ]);
2632
+
2633
+ await waitFor(() => expect(formStub.getFieldValue(['user', 'id'])).toBe(123));
2634
+ expect(formStub.getFieldValue(['user'])).toEqual({ id: 123 });
2635
+ });
2636
+
2637
+ it('normalizes primitive value to to-one association object by targetKey', async () => {
2638
+ const engineEmitter = new EventEmitter();
2639
+ const blockEmitter = new EventEmitter();
2640
+ const formStub = createFormStub({});
2641
+
2642
+ const blockModel: any = {
2643
+ uid: 'form-assign-assoc-normalize-to-one',
2644
+ flowEngine: { emitter: engineEmitter },
2645
+ emitter: blockEmitter,
2646
+ dispatchEvent: vi.fn(),
2647
+ getAclActionName: () => 'create',
2648
+ };
2649
+
2650
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2651
+ runtime.mount({ sync: true });
2652
+
2653
+ const blockCtx = createFieldContext(runtime);
2654
+ blockCtx.defineProperty('user', { value: { mainDepartment: { id: 9, name: 'Dept 9' } } });
2655
+
2656
+ const departmentCollection: any = { getField: () => null, filterTargetKey: 'id' };
2657
+ const mainDepartmentField: any = {
2658
+ isAssociationField: () => true,
2659
+ type: 'belongsTo',
2660
+ targetCollection: departmentCollection,
2661
+ };
2662
+ const collection: any = {
2663
+ getField: (name: string) => {
2664
+ if (name === 'mainDepartment') return mainDepartmentField;
2665
+ return null;
2666
+ },
2667
+ };
2668
+ blockCtx.defineProperty('collection', { value: collection });
2669
+ blockModel.context = blockCtx;
2670
+
2671
+ runtime.syncAssignRules([
2672
+ {
2673
+ key: 'r1',
2674
+ enable: true,
2675
+ targetPath: 'mainDepartment',
2676
+ mode: 'assign',
2677
+ condition: { logic: '$and', items: [] },
2678
+ value: '{{ ctx.user.mainDepartment.id }}',
2679
+ },
2680
+ ]);
2681
+
2682
+ await waitFor(() => expect(formStub.getFieldValue(['mainDepartment'])).toEqual({ id: 9 }));
2683
+ });
2684
+
2685
+ it('normalizes to-many association value to array when source is a single record object', async () => {
2686
+ const engineEmitter = new EventEmitter();
2687
+ const blockEmitter = new EventEmitter();
2688
+ const formStub = createFormStub({});
2689
+
2690
+ const blockModel: any = {
2691
+ uid: 'form-assign-assoc-normalize-to-many',
2692
+ flowEngine: { emitter: engineEmitter },
2693
+ emitter: blockEmitter,
2694
+ dispatchEvent: vi.fn(),
2695
+ getAclActionName: () => 'create',
2696
+ };
2697
+
2698
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2699
+ runtime.mount({ sync: true });
2700
+
2701
+ const blockCtx = createFieldContext(runtime);
2702
+ blockCtx.defineProperty('user', { value: { mainDepartment: { id: 9, name: 'Dept 9' } } });
2703
+
2704
+ const departmentCollection: any = { getField: () => null, filterTargetKey: 'id' };
2705
+ const departmentsField: any = {
2706
+ isAssociationField: () => true,
2707
+ type: 'belongsToMany',
2708
+ targetCollection: departmentCollection,
2709
+ };
2710
+ const collection: any = {
2711
+ getField: (name: string) => {
2712
+ if (name === 'departments') return departmentsField;
2713
+ return null;
2714
+ },
2715
+ };
2716
+ blockCtx.defineProperty('collection', { value: collection });
2717
+ blockModel.context = blockCtx;
2718
+
2719
+ runtime.syncAssignRules([
2720
+ {
2721
+ key: 'r1',
2722
+ enable: true,
2723
+ targetPath: 'departments',
2724
+ mode: 'assign',
2725
+ condition: { logic: '$and', items: [] },
2726
+ value: '{{ ctx.user.mainDepartment }}',
2727
+ },
2728
+ ]);
2729
+
2730
+ await waitFor(() => expect(formStub.getFieldValue(['departments'])).toEqual([{ id: 9, name: 'Dept 9' }]));
2731
+ });
2732
+
2733
+ it('keeps rich to-many association value when assign result is semantically equal by targetKey', async () => {
2734
+ const engineEmitter = new EventEmitter();
2735
+ const blockEmitter = new EventEmitter();
2736
+ const formStub = createFormStub({ roles: [{ name: 'root', title: 'Root role' }] });
2737
+
2738
+ const blockModel: any = {
2739
+ uid: 'form-assign-assoc-semantic-to-many',
2740
+ flowEngine: { emitter: engineEmitter },
2741
+ emitter: blockEmitter,
2742
+ dispatchEvent: vi.fn(),
2743
+ getAclActionName: () => 'create',
2744
+ };
2745
+
2746
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2747
+ runtime.mount({ sync: true });
2748
+
2749
+ const blockCtx = createFieldContext(runtime);
2750
+ blockCtx.defineProperty('role', { value: 'root' });
2751
+
2752
+ const rolesCollection: any = { getField: () => null, filterTargetKey: 'name' };
2753
+ const rolesField: any = {
2754
+ isAssociationField: () => true,
2755
+ type: 'belongsToMany',
2756
+ targetCollection: rolesCollection,
2757
+ };
2758
+ const collection: any = {
2759
+ getField: (name: string) => {
2760
+ if (name === 'roles') return rolesField;
2761
+ return null;
2762
+ },
2763
+ };
2764
+ blockCtx.defineProperty('collection', { value: collection });
2765
+ blockModel.context = blockCtx;
2766
+
2767
+ runtime.syncAssignRules([
2768
+ {
2769
+ key: 'r1',
2770
+ enable: true,
2771
+ targetPath: 'roles',
2772
+ mode: 'assign',
2773
+ condition: { logic: '$and', items: [] },
2774
+ value: '{{ ctx.role }}',
2775
+ },
2776
+ ]);
2777
+
2778
+ await new Promise((resolve) => setTimeout(resolve, 30));
2779
+ expect(formStub.getFieldValue(['roles'])).toEqual([{ name: 'root', title: 'Root role' }]);
2780
+ });
2781
+
2782
+ it('keeps rich to-one association value when assign result is semantically equal by targetKey', async () => {
2783
+ const engineEmitter = new EventEmitter();
2784
+ const blockEmitter = new EventEmitter();
2785
+ const formStub = createFormStub({ mainDepartment: { id: 9, name: 'Dept 9' } });
2786
+
2787
+ const blockModel: any = {
2788
+ uid: 'form-assign-assoc-semantic-to-one',
2789
+ flowEngine: { emitter: engineEmitter },
2790
+ emitter: blockEmitter,
2791
+ dispatchEvent: vi.fn(),
2792
+ getAclActionName: () => 'create',
2793
+ };
2794
+
2795
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2796
+ runtime.mount({ sync: true });
2797
+
2798
+ const blockCtx = createFieldContext(runtime);
2799
+ blockCtx.defineProperty('user', { value: { mainDepartment: { id: 9 } } });
2800
+
2801
+ const departmentCollection: any = { getField: () => null, filterTargetKey: 'id' };
2802
+ const mainDepartmentField: any = {
2803
+ isAssociationField: () => true,
2804
+ type: 'belongsTo',
2805
+ targetCollection: departmentCollection,
2806
+ };
2807
+ const collection: any = {
2808
+ getField: (name: string) => {
2809
+ if (name === 'mainDepartment') return mainDepartmentField;
2810
+ return null;
2811
+ },
2812
+ };
2813
+ blockCtx.defineProperty('collection', { value: collection });
2814
+ blockModel.context = blockCtx;
2815
+
2816
+ runtime.syncAssignRules([
2817
+ {
2818
+ key: 'r1',
2819
+ enable: true,
2820
+ targetPath: 'mainDepartment',
2821
+ mode: 'assign',
2822
+ condition: { logic: '$and', items: [] },
2823
+ value: '{{ ctx.user.mainDepartment.id }}',
2824
+ },
2825
+ ]);
2826
+
2827
+ await new Promise((resolve) => setTimeout(resolve, 30));
2828
+ expect(formStub.getFieldValue(['mainDepartment'])).toEqual({ id: 9, name: 'Dept 9' });
2829
+ });
2830
+
2831
+ it('applies nested association write for updateAssociation field when association value is empty', async () => {
2832
+ const engineEmitter = new EventEmitter();
2833
+ const blockEmitter = new EventEmitter();
2834
+ const formStub = createFormStub({ user: null });
2835
+
2836
+ const blockModel: any = {
2837
+ uid: 'form-assign-assoc-update-1',
2838
+ flowEngine: { emitter: engineEmitter },
2839
+ emitter: blockEmitter,
2840
+ dispatchEvent: vi.fn(),
2841
+ getAclActionName: () => 'create',
2842
+ };
2843
+
2844
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2845
+ runtime.mount({ sync: true });
2846
+
2847
+ const blockCtx = createFieldContext(runtime);
2848
+ const userCollection: any = { getField: () => null };
2849
+ const userField: any = { isAssociationField: () => true, type: 'belongsTo', targetCollection: userCollection };
2850
+ const collection: any = { getField: (name: string) => (name === 'user' ? userField : null) };
2851
+ blockCtx.defineProperty('collection', { value: collection });
2852
+
2853
+ const assocFormItemModel: any = {
2854
+ uid: 'user',
2855
+ subModels: { field: { updateAssociation: true, context: { collectionField: userField } } },
2856
+ getStepParams(flowKey: string, stepKey: string) {
2857
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2858
+ return { fieldPath: 'user' };
2859
+ }
2860
+ return undefined;
2861
+ },
2862
+ };
2863
+ const assocCtx = createFieldContext(runtime);
2864
+ assocCtx.defineProperty('blockModel', { value: blockModel });
2865
+ assocCtx.defineProperty('collection', { value: collection });
2866
+ assocCtx.defineProperty('model', { value: assocFormItemModel });
2867
+ assocFormItemModel.context = assocCtx;
2868
+
2869
+ blockCtx.defineProperty('engine', {
2870
+ value: {
2871
+ forEachModel: (cb: any) => {
2872
+ cb(assocFormItemModel);
2873
+ },
2874
+ },
2875
+ });
2876
+ blockModel.context = blockCtx;
2877
+
2878
+ runtime.syncAssignRules([
2879
+ {
2880
+ key: 'r1',
2881
+ enable: true,
2882
+ targetPath: 'user.name',
2883
+ mode: 'assign',
2884
+ condition: { logic: '$and', items: [] },
2885
+ value: 'Alice',
2886
+ },
2887
+ ]);
2888
+
2889
+ await waitFor(() => expect(formStub.getFieldValue(['user', 'name'])).toBe('Alice'));
2890
+ expect(formStub.getFieldValue(['user', '__is_new__'])).toBe(true);
2891
+ });
2892
+
2893
+ it('applies nested assignment under to-many updateAssociation row when row value is missing', async () => {
2894
+ const engineEmitter = new EventEmitter();
2895
+ const blockEmitter = new EventEmitter();
2896
+ const formStub = createFormStub({ users: [] });
2897
+
2898
+ const blockModel: any = {
2899
+ uid: 'form-assign-to-many-update-assoc-1',
2900
+ flowEngine: { emitter: engineEmitter },
2901
+ emitter: blockEmitter,
2902
+ dispatchEvent: vi.fn(),
2903
+ getAclActionName: () => 'create',
2904
+ };
2905
+
2906
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
2907
+ runtime.mount({ sync: true });
2908
+
2909
+ const blockCtx = createFieldContext(runtime);
2910
+
2911
+ const usersItemCollection: any = {
2912
+ getField: (name: string) => ({ name, isAssociationField: () => false }),
2913
+ };
2914
+ const usersField: any = { type: 'hasMany', isAssociationField: () => true, targetCollection: usersItemCollection };
2915
+ const rootCollection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
2916
+ blockCtx.defineProperty('collection', { value: rootCollection });
2917
+
2918
+ const usersFormItemModel: any = {
2919
+ uid: 'users',
2920
+ subModels: { field: { updateAssociation: true, context: { collectionField: usersField } } },
2921
+ getStepParams(flowKey: string, stepKey: string) {
2922
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
2923
+ return { fieldPath: 'users' };
2924
+ }
2925
+ return undefined;
2926
+ },
2927
+ };
2928
+ const usersCtx = createFieldContext(runtime);
2929
+ usersCtx.defineProperty('blockModel', { value: blockModel });
2930
+ usersCtx.defineProperty('collection', { value: rootCollection });
2931
+ usersCtx.defineProperty('model', { value: usersFormItemModel });
2932
+ usersFormItemModel.context = usersCtx;
2933
+
2934
+ const gridMaster: any = {
2935
+ uid: 'users.grid',
2936
+ subModels: { items: [] },
2937
+ };
2938
+ const gridMasterCtx = createFieldContext(runtime);
2939
+ gridMasterCtx.defineProperty('blockModel', { value: blockModel });
2940
+ gridMasterCtx.defineProperty('model', { value: gridMaster });
2941
+ gridMaster.context = gridMasterCtx;
2942
+
2943
+ const createGridRow = (forkId: string, rowIndex: number) => {
2944
+ const row: any = {
2945
+ uid: 'users.grid',
2946
+ isFork: true,
2947
+ forkId,
2948
+ subModels: { items: [] },
2949
+ };
2950
+ const ctx = createFieldContext(runtime);
2951
+ ctx.defineProperty('blockModel', { value: blockModel });
2952
+ ctx.defineProperty('fieldIndex', { value: [`users:${rowIndex}`] });
2953
+ ctx.defineProperty('model', { value: row });
2954
+ row.context = ctx;
2955
+ return row;
2956
+ };
2957
+
2958
+ const row0 = createGridRow('row0', 0);
2959
+ gridMaster.forks = new Set([row0]);
2960
+
2961
+ blockCtx.defineProperty('engine', {
2962
+ value: {
2963
+ forEachModel: (cb: any) => {
2964
+ cb(usersFormItemModel);
2965
+ cb(gridMaster);
2966
+ },
2967
+ },
2968
+ });
2969
+ blockModel.context = blockCtx;
2970
+
2971
+ runtime.syncAssignRules([
2972
+ {
2973
+ key: 'r1',
2974
+ enable: true,
2975
+ targetPath: 'users.age',
2976
+ mode: 'assign',
2977
+ condition: { logic: '$and', items: [] },
2978
+ value: 99,
2979
+ },
2980
+ ]);
2981
+
2982
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'age'])).toBe(99));
2983
+ expect(formStub.getFieldValue(['users', 0, '__is_new__'])).toBe(true);
2984
+ });
2985
+
2986
+ it('supports nested association assign under to-many using row context', async () => {
2987
+ const engineEmitter = new EventEmitter();
2988
+ const blockEmitter = new EventEmitter();
2989
+ const formStub = createFormStub({ users: [{ user: null }] });
2990
+
2991
+ const blockModel: any = {
2992
+ uid: 'form-assign-assoc-to-many-nested',
2993
+ flowEngine: { emitter: engineEmitter },
2994
+ emitter: blockEmitter,
2995
+ dispatchEvent: vi.fn(),
2996
+ getAclActionName: () => 'create',
2997
+ };
2998
+
2999
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3000
+ runtime.mount({ sync: true });
3001
+
3002
+ const blockCtx = createFieldContext(runtime);
3003
+
3004
+ const userCollection: any = { getField: () => null };
3005
+ const userField: any = { isAssociationField: () => true, type: 'belongsTo', targetCollection: userCollection };
3006
+ const usersItemCollection: any = { getField: (name: string) => (name === 'user' ? userField : null) };
3007
+ const usersField: any = { isAssociationField: () => true, type: 'hasMany', targetCollection: usersItemCollection };
3008
+ const collection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
3009
+
3010
+ blockCtx.defineProperty('collection', { value: collection });
3011
+
3012
+ const createRowModel = (forkId: string) => {
3013
+ const rowModel: any = {
3014
+ uid: `users.user:${forkId}`,
3015
+ isFork: true,
3016
+ forkId,
3017
+ subModels: { field: { context: { collectionField: userField } } },
3018
+ getStepParams(flowKey: string, stepKey: string) {
3019
+ if (flowKey === 'fieldSettings' && stepKey === 'init') {
3020
+ return { fieldPath: 'users.user' };
3021
+ }
3022
+ return undefined;
3023
+ },
3024
+ };
3025
+ const rowCtx = createFieldContext(runtime);
3026
+ rowCtx.defineProperty('blockModel', { value: blockModel });
3027
+ rowCtx.defineProperty('collection', { value: collection });
3028
+ rowCtx.defineProperty('fieldIndex', { value: [forkId] });
3029
+ rowCtx.defineProperty('model', { value: rowModel });
3030
+ rowModel.context = rowCtx;
3031
+ return rowModel;
3032
+ };
3033
+
3034
+ const row0 = createRowModel('users:0');
3035
+
3036
+ blockCtx.defineProperty('engine', {
3037
+ value: {
3038
+ forEachModel: (cb: any) => {
3039
+ cb(row0);
3040
+ },
3041
+ },
3042
+ });
3043
+ blockModel.context = blockCtx;
3044
+
3045
+ runtime.syncAssignRules([
3046
+ {
3047
+ key: 'r1',
3048
+ enable: true,
3049
+ targetPath: 'users.user.name',
3050
+ mode: 'assign',
3051
+ condition: { logic: '$and', items: [] },
3052
+ value: 'Alice',
3053
+ },
3054
+ ]);
3055
+
3056
+ // user 为空时不应隐式创建关联对象(users[0].user 仍为 null)
3057
+ await new Promise((resolve) => setTimeout(resolve, 20));
3058
+ expect(formStub.getFieldValue(['users', 0, 'user'])).toBeNull();
3059
+
3060
+ // 当 users[0].user 从 null -> {id} 时,应触发重新计算并写入 users[0].user.name
3061
+ await runtime.setFormValues(blockCtx, [{ path: ['users', 0, 'user'], value: { id: 1 } }], { source: 'user' });
3062
+ await waitFor(() => expect(formStub.getFieldValue(['users', 0, 'user', 'name'])).toBe('Alice'));
3063
+ expect(formStub.getFieldValue(['users', 0, 'user', 'id'])).toBe(1);
3064
+ });
3065
+
3066
+ it('does not write to to-many nested path without row index', async () => {
3067
+ const engineEmitter = new EventEmitter();
3068
+ const blockEmitter = new EventEmitter();
3069
+ const formStub = createFormStub({});
3070
+
3071
+ const blockModel: any = {
3072
+ uid: 'form-assign-to-many-skip',
3073
+ flowEngine: { emitter: engineEmitter },
3074
+ emitter: blockEmitter,
3075
+ dispatchEvent: vi.fn(),
3076
+ getAclActionName: () => 'create',
3077
+ };
3078
+
3079
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3080
+ runtime.mount({ sync: true });
3081
+
3082
+ const blockCtx = createFieldContext(runtime);
3083
+ const userCollection: any = { getField: () => null };
3084
+ const usersField: any = { isAssociationField: () => true, type: 'hasMany', targetCollection: userCollection };
3085
+ const collection: any = { getField: (name: string) => (name === 'users' ? usersField : null) };
3086
+ blockCtx.defineProperty('collection', { value: collection });
3087
+ blockModel.context = blockCtx;
3088
+
3089
+ runtime.syncAssignRules([
3090
+ {
3091
+ key: 'r1',
3092
+ enable: true,
3093
+ targetPath: 'users.nickname',
3094
+ mode: 'assign',
3095
+ condition: { logic: '$and', items: [] },
3096
+ value: 'Z',
3097
+ },
3098
+ ]);
3099
+
3100
+ await new Promise((resolve) => setTimeout(resolve, 20));
3101
+ expect(formStub.getFieldValue(['users'])).toBeUndefined();
3102
+ });
3103
+
3104
+ it('resolves nested to-many indices in order when list field names repeat', async () => {
3105
+ const engineEmitter = new EventEmitter();
3106
+ const blockEmitter = new EventEmitter();
3107
+ const formStub = createFormStub({});
3108
+
3109
+ const blockModel: any = {
3110
+ uid: 'form-resolve-repeated-list-name',
3111
+ flowEngine: { emitter: engineEmitter },
3112
+ emitter: blockEmitter,
3113
+ dispatchEvent: vi.fn(),
3114
+ getAclActionName: () => 'create',
3115
+ };
3116
+
3117
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3118
+ runtime.mount({ sync: true });
3119
+
3120
+ const ctx = createFieldContext(runtime);
3121
+ ctx.defineProperty('fieldIndex', { value: ['products:1', 'products:0'] });
3122
+
3123
+ const resolved = (runtime as any).tryResolveNamePath(ctx, 'products.products.id');
3124
+ expect(resolved).toEqual(['products', 1, 'products', 0, 'id']);
3125
+
3126
+ const resolvedWithExplicit = (runtime as any).tryResolveNamePath(ctx, 'products[0].products.id');
3127
+ expect(resolvedWithExplicit).toEqual(['products', 0, 'products', 0, 'id']);
3128
+ });
3129
+
3130
+ it('keeps deeper linkage write when same txId writes same path later from outer scope', async () => {
3131
+ const engineEmitter = new EventEmitter();
3132
+ const blockEmitter = new EventEmitter();
3133
+ const formStub = createFormStub({ name: 'init' });
3134
+
3135
+ const blockModel: any = {
3136
+ uid: 'form-linkage-scope-depth-keep-inner',
3137
+ flowEngine: { emitter: engineEmitter },
3138
+ emitter: blockEmitter,
3139
+ dispatchEvent: vi.fn(),
3140
+ getAclActionName: () => 'create',
3141
+ };
3142
+
3143
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3144
+ runtime.mount({ sync: true });
3145
+
3146
+ const blockCtx = createFieldContext(runtime);
3147
+ blockModel.context = blockCtx;
3148
+
3149
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: '456' }], {
3150
+ source: 'linkage',
3151
+ txId: 'tx-same',
3152
+ linkageTxId: 'tx-same',
3153
+ linkageScopeDepth: 1,
3154
+ });
3155
+ expect(formStub.getFieldValue(['name'])).toBe('456');
3156
+
3157
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: '123' }], {
3158
+ source: 'linkage',
3159
+ txId: 'tx-same',
3160
+ linkageTxId: 'tx-same',
3161
+ linkageScopeDepth: 0,
3162
+ });
3163
+ expect(formStub.getFieldValue(['name'])).toBe('456');
3164
+ });
3165
+
3166
+ it('keeps last-write behavior for same-depth linkage writes in same txId', async () => {
3167
+ const engineEmitter = new EventEmitter();
3168
+ const blockEmitter = new EventEmitter();
3169
+ const formStub = createFormStub({ name: 'init' });
3170
+
3171
+ const blockModel: any = {
3172
+ uid: 'form-linkage-scope-depth-same-level',
3173
+ flowEngine: { emitter: engineEmitter },
3174
+ emitter: blockEmitter,
3175
+ dispatchEvent: vi.fn(),
3176
+ getAclActionName: () => 'create',
3177
+ };
3178
+
3179
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3180
+ runtime.mount({ sync: true });
3181
+
3182
+ const blockCtx = createFieldContext(runtime);
3183
+ blockModel.context = blockCtx;
3184
+
3185
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: 'first' }], {
3186
+ source: 'linkage',
3187
+ txId: 'tx-same-level',
3188
+ linkageTxId: 'tx-same-level',
3189
+ linkageScopeDepth: 1,
3190
+ });
3191
+
3192
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: 'second' }], {
3193
+ source: 'linkage',
3194
+ txId: 'tx-same-level',
3195
+ linkageTxId: 'tx-same-level',
3196
+ linkageScopeDepth: 1,
3197
+ });
3198
+
3199
+ expect(formStub.getFieldValue(['name'])).toBe('second');
3200
+ });
3201
+
3202
+ it('does not block linkage writes across different linkageTxId', async () => {
3203
+ const engineEmitter = new EventEmitter();
3204
+ const blockEmitter = new EventEmitter();
3205
+ const formStub = createFormStub({ name: 'init' });
3206
+
3207
+ const blockModel: any = {
3208
+ uid: 'form-linkage-scope-depth-different-tx',
3209
+ flowEngine: { emitter: engineEmitter },
3210
+ emitter: blockEmitter,
3211
+ dispatchEvent: vi.fn(),
3212
+ getAclActionName: () => 'create',
3213
+ };
3214
+
3215
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3216
+ runtime.mount({ sync: true });
3217
+
3218
+ const blockCtx = createFieldContext(runtime);
3219
+ blockModel.context = blockCtx;
3220
+
3221
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: '456' }], {
3222
+ source: 'linkage',
3223
+ txId: 'tx-a',
3224
+ linkageTxId: 'tx-a',
3225
+ linkageScopeDepth: 1,
3226
+ });
3227
+
3228
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: '123' }], {
3229
+ source: 'linkage',
3230
+ txId: 'tx-b',
3231
+ linkageTxId: 'tx-b',
3232
+ linkageScopeDepth: 0,
3233
+ });
3234
+
3235
+ expect(formStub.getFieldValue(['name'])).toBe('123');
3236
+ });
3237
+
3238
+ it('propagates linkageTxId in emitted formValuesChange payload', async () => {
3239
+ const engineEmitter = new EventEmitter();
3240
+ const blockEmitter = new EventEmitter();
3241
+ const formStub = createFormStub({ name: 'init' });
3242
+
3243
+ const blockModel: any = {
3244
+ uid: 'form-linkage-propagate-linkage-txid',
3245
+ flowEngine: { emitter: engineEmitter },
3246
+ emitter: blockEmitter,
3247
+ dispatchEvent: vi.fn(),
3248
+ getAclActionName: () => 'create',
3249
+ };
3250
+
3251
+ const runtime = new FormValueRuntime({ model: blockModel, getForm: () => formStub as any });
3252
+ runtime.mount({ sync: true });
3253
+
3254
+ const blockCtx = createFieldContext(runtime);
3255
+ blockModel.context = blockCtx;
3256
+
3257
+ await runtime.setFormValues(blockCtx, [{ path: ['name'], value: '456' }], {
3258
+ source: 'linkage',
3259
+ txId: 'tx-current',
3260
+ linkageTxId: 'tx-root',
3261
+ linkageScopeDepth: 1,
3262
+ });
3263
+
3264
+ const dispatchCalls = blockModel.dispatchEvent.mock.calls.filter((call: any[]) => call[0] === 'formValuesChange');
3265
+ expect(dispatchCalls.length).toBeGreaterThan(0);
3266
+ const payload = dispatchCalls[dispatchCalls.length - 1][1];
3267
+ expect(payload.txId).toBe('tx-current');
3268
+ expect(payload.linkageTxId).toBe('tx-root');
3269
+ });
3270
+ });