@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.10

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 (334) hide show
  1. package/es/APIClient.d.ts +1 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/PluginSettingsManager.d.ts +33 -0
  4. package/es/RouteRepository.d.ts +29 -6
  5. package/es/RouterManager.d.ts +15 -0
  6. package/es/authRedirect.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  8. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  9. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  10. package/es/components/category-tabs/index.d.ts +9 -0
  11. package/es/components/form/JsonTextArea.d.ts +2 -1
  12. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  13. package/es/components/form/TypedVariableInput.d.ts +31 -5
  14. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  15. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  16. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  17. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  18. package/es/components/form/filter/index.d.ts +2 -0
  19. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  20. package/es/components/form/index.d.ts +1 -0
  21. package/es/components/index.d.ts +2 -0
  22. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  23. package/es/entry-actions/index.d.ts +9 -0
  24. package/es/flow/actions/afterSuccess.d.ts +8 -1
  25. package/es/flow/actions/index.d.ts +2 -2
  26. package/es/flow/actions/linkageRules.d.ts +2 -0
  27. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  28. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  29. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  30. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  31. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +3 -0
  32. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  33. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
  34. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  35. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  36. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  37. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  38. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  39. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  40. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  41. package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
  42. package/es/flow/components/FlowRoute.d.ts +2 -2
  43. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  44. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  45. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  46. package/es/flow/index.d.ts +1 -0
  47. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  48. package/es/flow/models/base/ActionModelCore.d.ts +2 -0
  49. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  50. package/es/flow/models/base/GridModel.d.ts +1 -1
  51. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  52. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  53. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  54. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  55. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  56. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  57. package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
  58. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  59. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
  60. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  61. package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
  62. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
  63. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  64. package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
  65. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
  66. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
  67. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
  68. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
  69. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
  70. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  71. package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
  72. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  73. package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
  74. package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
  75. package/es/flow/utils/formValueDeps.d.ts +32 -0
  76. package/es/flow-compat/FieldValidation.d.ts +2 -1
  77. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  78. package/es/flow-compat/routeTypes.d.ts +1 -0
  79. package/es/index.d.ts +8 -1
  80. package/es/index.mjs +367 -181
  81. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  82. package/es/utils/index.d.ts +1 -0
  83. package/es/utils/markdownSanitize.d.ts +11 -0
  84. package/lib/index.js +367 -181
  85. package/lib/locale/languageCodes.js +3 -1
  86. package/package.json +9 -7
  87. package/src/APIClient.ts +1 -10
  88. package/src/Application.tsx +4 -0
  89. package/src/BaseApplication.tsx +13 -6
  90. package/src/PluginSettingsManager.ts +53 -0
  91. package/src/RouteRepository.ts +151 -24
  92. package/src/RouterManager.tsx +142 -1
  93. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  94. package/src/__tests__/RouteRepository.test.ts +239 -0
  95. package/src/__tests__/RouterManager.test.ts +125 -0
  96. package/src/__tests__/app.test.tsx +73 -0
  97. package/src/__tests__/authRedirect.test.ts +17 -0
  98. package/src/__tests__/browserChecker.test.ts +61 -0
  99. package/src/__tests__/exports.test.ts +16 -0
  100. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  101. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +234 -2
  102. package/src/__tests__/plugin-manager.test.tsx +44 -2
  103. package/src/__tests__/settings-center.test.tsx +40 -1
  104. package/src/authRedirect.ts +38 -0
  105. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  106. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  107. package/src/collection-manager/field-configure.ts +1 -1
  108. package/src/collection-manager/field-validation.ts +1 -1
  109. package/src/collection-manager/interfaces/id.ts +1 -1
  110. package/src/collection-manager/interfaces/m2m.tsx +2 -2
  111. package/src/collection-manager/interfaces/m2o.tsx +2 -2
  112. package/src/collection-manager/interfaces/nanoid.ts +1 -1
  113. package/src/collection-manager/interfaces/o2m.tsx +2 -2
  114. package/src/collection-manager/interfaces/obo.tsx +2 -2
  115. package/src/collection-manager/interfaces/oho.tsx +2 -2
  116. package/src/collection-manager/interfaces/properties/index.ts +2 -2
  117. package/src/collection-manager/interfaces/uuid.ts +1 -1
  118. package/src/components/AppComponents.tsx +19 -3
  119. package/src/components/README.md +7 -1
  120. package/src/components/README.zh-CN.md +6 -1
  121. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  122. package/src/components/category-tabs/index.ts +10 -0
  123. package/src/components/form/JsonTextArea.tsx +25 -11
  124. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  125. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  126. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  127. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  128. package/src/components/form/TypedVariableInput.tsx +452 -101
  129. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  130. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  131. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  132. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  133. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  134. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  135. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  136. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  137. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  138. package/src/components/form/filter/index.ts +2 -0
  139. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  140. package/src/components/form/index.tsx +1 -0
  141. package/src/components/index.ts +2 -0
  142. package/src/entry-actions/EntryActionManager.ts +76 -0
  143. package/src/entry-actions/index.ts +10 -0
  144. package/src/flow/FlowPage.tsx +38 -3
  145. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  146. package/src/flow/__tests__/FlowRoute.test.tsx +1181 -80
  147. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  148. package/src/flow/__tests__/getKey.test.ts +7 -0
  149. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
  150. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +115 -0
  151. package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
  152. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
  153. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1124 -0
  154. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  155. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  156. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  157. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  158. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  159. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  160. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
  161. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
  162. package/src/flow/actions/afterSuccess.tsx +142 -3
  163. package/src/flow/actions/customVariable.tsx +1 -2
  164. package/src/flow/actions/dataScopeFilter.ts +20 -1
  165. package/src/flow/actions/dateTimeFormat.tsx +42 -28
  166. package/src/flow/actions/index.ts +3 -1
  167. package/src/flow/actions/linkageRules.tsx +209 -47
  168. package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
  169. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  170. package/src/flow/actions/openView.tsx +59 -5
  171. package/src/flow/actions/runjs.tsx +2 -14
  172. package/src/flow/actions/validation.tsx +62 -30
  173. package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
  174. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
  175. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
  176. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -14
  177. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
  178. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
  179. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  180. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  181. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  182. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  183. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
  184. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  185. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  186. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
  187. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +435 -0
  188. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  189. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
  190. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
  191. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  192. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  193. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  194. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  195. package/src/flow/common/Markdown/Display.tsx +14 -3
  196. package/src/flow/common/Markdown/Edit.tsx +19 -7
  197. package/src/flow/common/Markdown/style.ts +4 -0
  198. package/src/flow/components/BlockItemCard.tsx +2 -2
  199. package/src/flow/components/DefaultValue.tsx +1 -2
  200. package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
  201. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  202. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  203. package/src/flow/components/FieldAssignValueInput.tsx +91 -20
  204. package/src/flow/components/FlowRoute.tsx +256 -53
  205. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  206. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
  207. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  208. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  209. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  210. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  211. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  212. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  213. package/src/flow/components/fieldAssignOptions.ts +155 -27
  214. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  215. package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
  216. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  217. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  218. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
  219. package/src/flow/flows/editMarkdownFlow.tsx +1 -1
  220. package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
  221. package/src/flow/index.ts +4 -1
  222. package/src/flow/internal/components/Markdown/util.ts +2 -1
  223. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  224. package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
  225. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  226. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  227. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  228. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  229. package/src/flow/models/base/ActionModel.tsx +21 -8
  230. package/src/flow/models/base/ActionModelCore.tsx +11 -4
  231. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  232. package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
  233. package/src/flow/models/base/GridModel.tsx +38 -7
  234. package/src/flow/models/base/PageModel/PageModel.tsx +387 -50
  235. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  236. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  237. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +817 -13
  238. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  239. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  240. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  241. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  242. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
  243. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  244. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  245. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  246. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  247. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +59 -6
  248. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
  249. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  250. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
  251. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +143 -4
  252. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
  253. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
  254. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  255. package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
  256. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  257. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  258. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  259. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  260. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  261. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
  262. package/src/flow/models/blocks/form/submitHandler.ts +17 -3
  263. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
  264. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  265. package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
  266. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  267. package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
  268. package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
  269. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  270. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
  271. package/src/flow/models/blocks/table/TableBlockModel.tsx +42 -19
  272. package/src/flow/models/blocks/table/TableColumnModel.tsx +72 -6
  273. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
  274. package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
  275. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  276. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  277. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
  278. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +243 -1
  279. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
  280. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
  281. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
  282. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  283. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +31 -7
  284. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
  285. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
  286. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  287. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
  288. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  289. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  290. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  291. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
  292. package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
  293. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  294. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  295. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
  296. package/src/flow/models/fields/InputFieldModel.tsx +40 -2
  297. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  298. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  299. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  300. package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
  301. package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
  302. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  303. package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
  304. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
  305. package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
  306. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
  307. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  308. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
  309. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
  310. package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
  311. package/src/flow/resolveViewParamsToViewList.tsx +11 -3
  312. package/src/flow/routeTransientInputArgs.ts +27 -0
  313. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
  314. package/src/flow/utils/dataScopeFormValueClear.ts +219 -81
  315. package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
  316. package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
  317. package/src/flow/utils/formValueDeps.ts +170 -0
  318. package/src/flow-compat/FieldValidation.tsx +122 -60
  319. package/src/flow-compat/Popover.tsx +43 -4
  320. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  321. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  322. package/src/flow-compat/routeTypes.ts +1 -0
  323. package/src/index.ts +14 -1
  324. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  325. package/src/locale/languageCodes.ts +3 -1
  326. package/src/nocobase-buildin-plugin/index.tsx +63 -15
  327. package/src/settings-center/AdminSettingsLayout.tsx +10 -2
  328. package/src/settings-center/SystemSettingsPage.tsx +1 -2
  329. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  330. package/src/settings-center/plugin-manager/index.tsx +3 -0
  331. package/src/settings-center/utils.tsx +0 -6
  332. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  333. package/src/utils/index.tsx +1 -0
  334. package/src/utils/markdownSanitize.ts +88 -0
@@ -78,6 +78,19 @@ describe('PluginSettingsManager v2', () => {
78
78
  expect(app.router.get('admin.settings.demo.advanced')).toMatchObject({ path: 'advanced' });
79
79
  });
80
80
 
81
+ it('should resolve plugin settings links for plugin manager entries', () => {
82
+ const app = createMockClient();
83
+
84
+ app.pluginSettingsManager.addMenuItem({ key: 'shared', title: 'Shared' });
85
+ app.pluginSettingsManager.addPageTabItem({ menuKey: 'shared', key: 'target', title: 'Target' });
86
+ app.pluginSettingsManager.setPluginSettingsLink('demo-plugin', 'shared.target');
87
+
88
+ expect(app.pluginSettingsManager.has('demo-plugin')).toBe(false);
89
+ expect(app.pluginSettingsManager.hasPluginSettings('demo-plugin')).toBe(true);
90
+ expect(app.pluginSettingsManager.getPluginSettingsName('demo-plugin')).toBe('shared.target');
91
+ expect(app.pluginSettingsManager.getPluginSettingsRoutePath('demo-plugin')).toBe('/admin/settings/shared/target');
92
+ });
93
+
81
94
  it('should render string icon on both menu and page tab via renderIcon', () => {
82
95
  // Previously `renderPage` spread the raw `icon` string straight to the antd
83
96
  // Menu item, which displayed "LockOutlinedTitle" as text. Both `renderMenuItem`
@@ -0,0 +1,239 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import { describe, expect, it, vi } from 'vitest';
11
+ import type { NocoBaseDesktopRoute } from '../flow-compat';
12
+ import { RouteRepository } from '../RouteRepository';
13
+
14
+ function createRouteRepository() {
15
+ const request = vi.fn().mockResolvedValue({
16
+ data: {
17
+ data: [],
18
+ },
19
+ });
20
+ const create = vi.fn().mockResolvedValue({
21
+ data: {
22
+ data: {},
23
+ },
24
+ });
25
+ const resource = vi.fn(() => ({
26
+ create,
27
+ }));
28
+ const repository = new RouteRepository({
29
+ api: {
30
+ request,
31
+ resource,
32
+ },
33
+ } as never);
34
+
35
+ return {
36
+ create,
37
+ repository,
38
+ request,
39
+ resource,
40
+ };
41
+ }
42
+
43
+ function route(schemaUid: string): NocoBaseDesktopRoute {
44
+ return { schemaUid } as NocoBaseDesktopRoute;
45
+ }
46
+
47
+ describe('RouteRepository', () => {
48
+ it('should pass the default admin layout when loading accessible routes', async () => {
49
+ const { repository, request } = createRouteRepository();
50
+
51
+ await repository.refreshAccessible();
52
+
53
+ expect(request).toHaveBeenCalledWith({
54
+ url: '/desktopRoutes:listAccessible',
55
+ params: {
56
+ tree: true,
57
+ sort: 'sort',
58
+ layout: 'admin-layout-model',
59
+ },
60
+ });
61
+ });
62
+
63
+ it('should pass the active client-v2 layout when loading accessible routes', async () => {
64
+ const { repository, request } = createRouteRepository();
65
+ const deactivateLayout = repository.activateLayout({
66
+ uid: 'custom-desktop-layout-model',
67
+ });
68
+
69
+ await repository.refreshAccessible();
70
+ deactivateLayout();
71
+
72
+ expect(request).toHaveBeenCalledWith({
73
+ url: '/desktopRoutes:listAccessible',
74
+ params: {
75
+ tree: true,
76
+ sort: 'sort',
77
+ layout: 'custom-desktop-layout-model',
78
+ },
79
+ });
80
+ });
81
+
82
+ it('should reload accessible routes when the active client-v2 layout changes', async () => {
83
+ const { repository, request } = createRouteRepository();
84
+ const deactivateFirstLayout = repository.activateLayout({
85
+ uid: 'first-layout-model',
86
+ });
87
+ await repository.ensureAccessibleLoaded();
88
+ deactivateFirstLayout();
89
+
90
+ const deactivateSecondLayout = repository.activateLayout({
91
+ uid: 'second-layout-model',
92
+ });
93
+ await repository.ensureAccessibleLoaded();
94
+ deactivateSecondLayout();
95
+
96
+ expect(request).toHaveBeenNthCalledWith(1, {
97
+ url: '/desktopRoutes:listAccessible',
98
+ params: {
99
+ tree: true,
100
+ sort: 'sort',
101
+ layout: 'first-layout-model',
102
+ },
103
+ });
104
+ expect(request).toHaveBeenNthCalledWith(2, {
105
+ url: '/desktopRoutes:listAccessible',
106
+ params: {
107
+ tree: true,
108
+ sort: 'sort',
109
+ layout: 'second-layout-model',
110
+ },
111
+ });
112
+ });
113
+
114
+ it('should report accessible routes as unloaded when the active layout changes', () => {
115
+ const { repository } = createRouteRepository();
116
+
117
+ repository.setRoutes([], 'first-layout-model');
118
+ const deactivateLayout = repository.activateLayout({
119
+ uid: 'second-layout-model',
120
+ });
121
+
122
+ expect(repository.isAccessibleLoaded()).toBe(false);
123
+ deactivateLayout();
124
+ });
125
+
126
+ it('should keep accessible route caches isolated by active layout', () => {
127
+ const { repository } = createRouteRepository();
128
+
129
+ repository.setRoutes([route('admin-page')], 'admin-layout-model');
130
+ const deactivateLayout = repository.activateLayout({
131
+ uid: 'custom-desktop-layout-model',
132
+ });
133
+ repository.setRoutes([route('custom-page')], 'custom-desktop-layout-model');
134
+
135
+ expect(repository.listAccessible().map((route) => route.schemaUid)).toEqual(['custom-page']);
136
+ expect(repository.getRouteBySchemaUid('admin-page')).toBeUndefined();
137
+ deactivateLayout();
138
+
139
+ expect(repository.listAccessible().map((route) => route.schemaUid)).toEqual(['admin-page']);
140
+ expect(repository.getRouteBySchemaUid('custom-page')).toBeUndefined();
141
+ });
142
+
143
+ it('should find nested routes by id', () => {
144
+ const { repository } = createRouteRepository();
145
+
146
+ repository.setRoutes([
147
+ {
148
+ id: 1,
149
+ schemaUid: 'group-1',
150
+ children: [
151
+ {
152
+ id: 11,
153
+ schemaUid: 'page-1',
154
+ },
155
+ ],
156
+ },
157
+ ] as NocoBaseDesktopRoute[]);
158
+
159
+ expect(repository.getRouteById('11')).toMatchObject({
160
+ id: 11,
161
+ schemaUid: 'page-1',
162
+ });
163
+ expect(repository.getRouteById('missing')).toBeUndefined();
164
+ });
165
+
166
+ it('should notify only subscribers for the changed layout cache', () => {
167
+ const { repository } = createRouteRepository();
168
+ const adminEvents: string[][] = [];
169
+ const customEvents: string[][] = [];
170
+
171
+ const unsubscribeAdmin = repository.subscribe(() => {
172
+ adminEvents.push(repository.listAccessible().map((route) => route.schemaUid || ''));
173
+ });
174
+ const deactivateLayout = repository.activateLayout({
175
+ uid: 'custom-desktop-layout-model',
176
+ });
177
+ const unsubscribeCustom = repository.subscribe(() => {
178
+ customEvents.push(repository.listAccessible().map((route) => route.schemaUid || ''));
179
+ });
180
+
181
+ repository.setRoutes([route('custom-page')], 'custom-desktop-layout-model');
182
+
183
+ expect(adminEvents).toEqual([]);
184
+ expect(customEvents).toEqual([['custom-page']]);
185
+
186
+ deactivateLayout();
187
+ repository.setRoutes([route('admin-page')], 'admin-layout-model');
188
+
189
+ expect(adminEvents).toEqual([['admin-page']]);
190
+ expect(customEvents).toEqual([['custom-page']]);
191
+
192
+ unsubscribeAdmin();
193
+ unsubscribeCustom();
194
+ });
195
+
196
+ it('should pass the default admin layout when creating a route', async () => {
197
+ const { repository, create } = createRouteRepository();
198
+
199
+ await repository.createRoute(
200
+ {
201
+ title: 'Admin page',
202
+ },
203
+ {
204
+ refreshAfterMutation: false,
205
+ },
206
+ );
207
+
208
+ expect(create).toHaveBeenCalledWith({
209
+ values: {
210
+ title: 'Admin page',
211
+ },
212
+ layout: 'admin-layout-model',
213
+ });
214
+ });
215
+
216
+ it('should pass the active client-v2 layout when creating a route', async () => {
217
+ const { repository, create } = createRouteRepository();
218
+ const deactivateLayout = repository.activateLayout({
219
+ uid: 'custom-desktop-layout-model',
220
+ });
221
+
222
+ await repository.createRoute(
223
+ {
224
+ title: 'Custom page',
225
+ },
226
+ {
227
+ refreshAfterMutation: false,
228
+ },
229
+ );
230
+ deactivateLayout();
231
+
232
+ expect(create).toHaveBeenCalledWith({
233
+ values: {
234
+ title: 'Custom page',
235
+ },
236
+ layout: 'custom-desktop-layout-model',
237
+ });
238
+ });
239
+ });
@@ -0,0 +1,125 @@
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 type { BaseApplication } from '../BaseApplication';
11
+ import { RouterManager, shouldOpenAdminRouteInNewWindow } from '../RouterManager';
12
+
13
+ describe('RouterManager', () => {
14
+ describe('shouldOpenAdminRouteInNewWindow', () => {
15
+ it('opens admin route in a new window from a non-admin portal', () => {
16
+ expect(
17
+ shouldOpenAdminRouteInNewWindow({
18
+ currentPathname: '/v/test',
19
+ targetPathname: '/admin/settings',
20
+ basePath: '/v',
21
+ adminRoutePath: '/admin',
22
+ }),
23
+ ).toBe(true);
24
+ });
25
+
26
+ it('supports target pathname with the v2 base path', () => {
27
+ expect(
28
+ shouldOpenAdminRouteInNewWindow({
29
+ currentPathname: '/nocobase/v/test',
30
+ targetPathname: '/nocobase/v/admin/settings',
31
+ basePath: '/nocobase/v',
32
+ adminRoutePath: '/admin',
33
+ }),
34
+ ).toBe(true);
35
+ });
36
+
37
+ it('supports custom portal routes in sub-apps', () => {
38
+ expect(
39
+ shouldOpenAdminRouteInNewWindow({
40
+ currentPathname: '/v/apps/a_9xlild35jir/crm-amd/ekeisumx1zu',
41
+ targetPathname: '/v/apps/a_9xlild35jir/admin/settings',
42
+ basePath: '/v',
43
+ adminRoutePath: '/admin',
44
+ }),
45
+ ).toBe(true);
46
+ });
47
+
48
+ it('keeps admin route navigation in the current window when already in sub-app admin', () => {
49
+ expect(
50
+ shouldOpenAdminRouteInNewWindow({
51
+ currentPathname: '/v/apps/a_9xlild35jir/admin',
52
+ targetPathname: '/v/apps/a_9xlild35jir/admin/settings',
53
+ basePath: '/v',
54
+ adminRoutePath: '/admin',
55
+ }),
56
+ ).toBe(false);
57
+ });
58
+
59
+ it('keeps admin route navigation in the current window when already in admin', () => {
60
+ expect(
61
+ shouldOpenAdminRouteInNewWindow({
62
+ currentPathname: '/v/admin',
63
+ targetPathname: '/admin/settings',
64
+ basePath: '/v',
65
+ adminRoutePath: '/admin',
66
+ }),
67
+ ).toBe(false);
68
+ });
69
+
70
+ it('keeps non-admin portal navigation in the current window', () => {
71
+ expect(
72
+ shouldOpenAdminRouteInNewWindow({
73
+ currentPathname: '/v/test',
74
+ targetPathname: '/test/page',
75
+ basePath: '/v',
76
+ adminRoutePath: '/admin',
77
+ }),
78
+ ).toBe(false);
79
+ });
80
+
81
+ it('does not intercept replace navigation or relative paths', () => {
82
+ expect(
83
+ shouldOpenAdminRouteInNewWindow({
84
+ currentPathname: '/v/test',
85
+ targetPathname: '/admin/settings',
86
+ basePath: '/v',
87
+ adminRoutePath: '/admin',
88
+ replace: true,
89
+ }),
90
+ ).toBe(false);
91
+ expect(
92
+ shouldOpenAdminRouteInNewWindow({
93
+ currentPathname: '/v/test',
94
+ targetPathname: 'admin/settings',
95
+ basePath: '/v',
96
+ adminRoutePath: '/admin',
97
+ }),
98
+ ).toBe(false);
99
+ });
100
+ });
101
+
102
+ it('opens admin route in a new window through the underlying router navigate from a sub-app portal', async () => {
103
+ const app = {
104
+ name: 'a_9xlild35jir',
105
+ getPublicPath: () => '/v/',
106
+ getHref: (pathname: string) => `/v/apps/a_9xlild35jir/${pathname.replace(/^\/+/, '')}`,
107
+ layoutManager: {
108
+ getLayout: () => ({ routePath: '/admin' }),
109
+ },
110
+ renderComponent: () => null,
111
+ } as unknown as BaseApplication<unknown>;
112
+ const manager = new RouterManager({ type: 'browser' }, app);
113
+ const open = vi.spyOn(window, 'open').mockImplementation(() => null);
114
+
115
+ window.history.pushState({}, '', '/v/apps/a_9xlild35jir/crm-amd/ekeisumx1zu');
116
+ manager.getRouterComponent();
117
+ await manager.router.navigate('/admin/settings/version-control/list');
118
+
119
+ expect(open).toHaveBeenCalledWith(
120
+ '/v/apps/a_9xlild35jir/admin/settings/version-control/list',
121
+ '_blank',
122
+ 'noopener,noreferrer',
123
+ );
124
+ });
125
+ });
@@ -34,6 +34,8 @@ describe('app', () => {
34
34
  afterEach(() => {
35
35
  document.querySelectorAll('link[rel="shortcut icon"]').forEach((node) => node.remove());
36
36
  document.documentElement.removeAttribute('lang');
37
+ delete window['__webpack_public_path__'];
38
+ delete window['__nocobase_modern_client_prefix__'];
37
39
  vi.restoreAllMocks();
38
40
  });
39
41
 
@@ -50,6 +52,24 @@ describe('app', () => {
50
52
  expect(app.getHref('/test')).toBe('/test');
51
53
  });
52
54
 
55
+ it('should enable credentials for api clients by default', () => {
56
+ const sameOriginApp = new Application({
57
+ router,
58
+ apiClient: {
59
+ baseURL: '/api/',
60
+ },
61
+ });
62
+ const crossOriginApp = new Application({
63
+ router,
64
+ apiClient: {
65
+ baseURL: 'https://api.example.com/api/',
66
+ },
67
+ });
68
+
69
+ expect(sameOriginApp.apiClient.axios.defaults.withCredentials).toBe(true);
70
+ expect(crossOriginApp.apiClient.axios.defaults.withCredentials).toBe(true);
71
+ });
72
+
53
73
  it('should initialize shared jsonLogic operators', () => {
54
74
  const app = new Application({ router });
55
75
 
@@ -58,6 +78,59 @@ describe('app', () => {
58
78
  expect(app.jsonLogic.apply({ $testAlwaysTrue: [] })).toBe(true);
59
79
  });
60
80
 
81
+ it('should normalize publicPath and webpack public path with a single trailing slash', () => {
82
+ const app = new Application({
83
+ router,
84
+ publicPath: '/admin//',
85
+ });
86
+
87
+ expect(app.getPublicPath()).toBe('/admin/');
88
+
89
+ window['__webpack_public_path__'] = '/cdn/assets///';
90
+ expect(app.getCdnUrl()).toBe('/cdn/assets/');
91
+
92
+ delete window['__webpack_public_path__'];
93
+ expect(app.getCdnUrl()).toBe('/admin/');
94
+ });
95
+
96
+ it('should normalize webpack public path without a trailing slash', () => {
97
+ const app = new Application({
98
+ router,
99
+ publicPath: '/admin/',
100
+ });
101
+
102
+ window['__webpack_public_path__'] = '/cdn/assets';
103
+ expect(app.getCdnUrl()).toBe('/cdn/assets/');
104
+ });
105
+
106
+ it('should remove the modern client prefix from the CDN fallback path', () => {
107
+ const app = new Application({
108
+ router,
109
+ publicPath: '/v/',
110
+ });
111
+
112
+ expect(app.getCdnUrl()).toBe('/');
113
+ });
114
+
115
+ it('should preserve APP_PUBLIC_PATH when removing the modern client prefix', () => {
116
+ const app = new Application({
117
+ router,
118
+ publicPath: '/nocobase/v/',
119
+ });
120
+
121
+ expect(app.getCdnUrl()).toBe('/nocobase/');
122
+ });
123
+
124
+ it('should support a custom modern client prefix', () => {
125
+ window['__nocobase_modern_client_prefix__'] = 'modern';
126
+ const app = new Application({
127
+ router,
128
+ publicPath: '/nocobase/modern/',
129
+ });
130
+
131
+ expect(app.getCdnUrl()).toBe('/nocobase/');
132
+ });
133
+
61
134
  it('should apply the provided favicon immediately', () => {
62
135
  const app = new Application({ router });
63
136
 
@@ -10,6 +10,7 @@
10
10
  import {
11
11
  buildV2SigninHref,
12
12
  getCurrentV2RedirectPath,
13
+ normalizeV2RedirectPath,
13
14
  redirectToV2Signin,
14
15
  resolveV2SigninRedirect,
15
16
  } from '../authRedirect';
@@ -133,6 +134,22 @@ describe('auth redirect helpers', () => {
133
134
  });
134
135
 
135
136
  describe('v2 sub-app context (router basename contains /apps/<id>/)', () => {
137
+ it('should normalize signin redirect fallback under the current sub-app basename', () => {
138
+ const app = {
139
+ getPublicPath: () => '/v/',
140
+ router: {
141
+ getBasename: () => '/v/apps/test-app/',
142
+ },
143
+ } as any;
144
+
145
+ expect(normalizeV2RedirectPath(app, '')).toBe('/v/apps/test-app/admin/');
146
+ expect(normalizeV2RedirectPath(app, '/admin/?tab=overview#panel')).toBe(
147
+ '/v/apps/test-app/admin/?tab=overview#panel',
148
+ );
149
+ expect(normalizeV2RedirectPath(app, '/v/apps/test-app/admin/')).toBe('/v/apps/test-app/admin/');
150
+ expect(normalizeV2RedirectPath(app, '/v/admin/')).toBe('/v/apps/test-app/admin/');
151
+ });
152
+
136
153
  it('should preserve sub-app segment when building current redirect path under simple public path', () => {
137
154
  const app = {
138
155
  getPublicPath: () => '/v2/',
@@ -15,6 +15,8 @@ import { fileURLToPath } from 'node:url';
15
15
  type BrowserCheckerCase = {
16
16
  pathname: string;
17
17
  publicPath: string;
18
+ modernClientPrefix?: string;
19
+ appClientEntryMode?: string;
18
20
  expectedRedirect?: string;
19
21
  };
20
22
 
@@ -40,6 +42,8 @@ function executeBrowserChecker(scriptPath: string, input: BrowserCheckerCase) {
40
42
  showLog: false,
41
43
  window: {
42
44
  __nocobase_public_path__: input.publicPath,
45
+ __nocobase_modern_client_prefix__: input.modernClientPrefix,
46
+ __nocobase_app_client_entry_mode__: input.appClientEntryMode,
43
47
  location: {
44
48
  origin: 'http://c.local.nocobase.com',
45
49
  pathname: input.pathname,
@@ -109,4 +113,61 @@ describe.each(browserCheckerCases)('$label', ({ scriptPath }) => {
109
113
 
110
114
  expect(replace).not.toHaveBeenCalled();
111
115
  });
116
+
117
+ if (scriptPath.includes('/app/client/public/')) {
118
+ it('redirects app root to modern entry for modern-default', () => {
119
+ const replace = executeBrowserChecker(scriptPath, {
120
+ pathname: '/',
121
+ publicPath: '/',
122
+ modernClientPrefix: 'v',
123
+ appClientEntryMode: 'modern-default',
124
+ });
125
+
126
+ expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/v/');
127
+ });
128
+
129
+ it('does not redirect legacy deep links for modern-default', () => {
130
+ const replace = executeBrowserChecker(scriptPath, {
131
+ pathname: '/admin',
132
+ publicPath: '/',
133
+ modernClientPrefix: 'v',
134
+ appClientEntryMode: 'modern-default',
135
+ });
136
+
137
+ expect(replace).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it('rewrites legacy document paths for modern-only', () => {
141
+ const replace = executeBrowserChecker(scriptPath, {
142
+ pathname: '/admin/settings/workflow',
143
+ publicPath: '/',
144
+ modernClientPrefix: 'v',
145
+ appClientEntryMode: 'modern-only',
146
+ });
147
+
148
+ expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/v/admin/settings/workflow');
149
+ });
150
+
151
+ it('redirects sub-path site root directly to final modern target', () => {
152
+ const replace = executeBrowserChecker(scriptPath, {
153
+ pathname: '/nocobase/',
154
+ publicPath: '/nocobase/',
155
+ modernClientPrefix: 'v',
156
+ appClientEntryMode: 'modern-default',
157
+ });
158
+
159
+ expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/v/');
160
+ });
161
+
162
+ it('rewrites sub-app legacy deep links for modern-only without collapsing the sub-app segment', () => {
163
+ const replace = executeBrowserChecker(scriptPath, {
164
+ pathname: '/nocobase/apps/a_31itq60q4kg/admin/',
165
+ publicPath: '/nocobase/',
166
+ modernClientPrefix: 'v',
167
+ appClientEntryMode: 'modern-only',
168
+ });
169
+
170
+ expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/v/apps/a_31itq60q4kg/admin');
171
+ });
172
+ }
112
173
  });
@@ -0,0 +1,16 @@
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 { IconPicker } from '..';
11
+
12
+ describe('@nocobase/client-v2 exports', () => {
13
+ it('should export IconPicker from the root entry', () => {
14
+ expect(IconPicker).toBeDefined();
15
+ });
16
+ });
@@ -0,0 +1,68 @@
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 { afterEach, describe, expect, it } from 'vitest';
11
+ import { getRouteRuntimeVersion } from '../utils/getRouteRuntimeVersion';
12
+
13
+ function replacePathname(pathname: string) {
14
+ Object.defineProperty(window, 'location', {
15
+ configurable: true,
16
+ value: {
17
+ ...window.location,
18
+ pathname,
19
+ },
20
+ });
21
+ }
22
+
23
+ describe('getRouteRuntimeVersion', () => {
24
+ afterEach(() => {
25
+ delete window.__nocobase_modern_client_prefix__;
26
+ delete window.__nocobase_public_path__;
27
+ replacePathname('/');
28
+ });
29
+
30
+ it('returns modern when the current pathname is inside the modern client prefix', () => {
31
+ window.__nocobase_modern_client_prefix__ = 'v';
32
+ window.__nocobase_public_path__ = '/nocobase/v/';
33
+ replacePathname('/nocobase/v/admin/settings/workflow');
34
+
35
+ expect(getRouteRuntimeVersion()).toBe('modern');
36
+ });
37
+
38
+ it('returns legacy when reused v2 components run under the v1 settings page', () => {
39
+ window.__nocobase_modern_client_prefix__ = 'v';
40
+ window.__nocobase_public_path__ = '/nocobase/';
41
+ replacePathname('/nocobase/admin/settings/workflow/workflows/123');
42
+
43
+ expect(getRouteRuntimeVersion()).toBe('legacy');
44
+ });
45
+
46
+ it('returns legacy when root public path is "/"', () => {
47
+ window.__nocobase_modern_client_prefix__ = 'v';
48
+ window.__nocobase_public_path__ = '/';
49
+ replacePathname('/admin/settings/workflow/workflows/123');
50
+
51
+ expect(getRouteRuntimeVersion()).toBe('legacy');
52
+ });
53
+
54
+ it('returns modern when root public path is "/v/"', () => {
55
+ window.__nocobase_modern_client_prefix__ = 'v';
56
+ window.__nocobase_public_path__ = '/v/';
57
+ replacePathname('/v/admin/workflow/workflows/123');
58
+
59
+ expect(getRouteRuntimeVersion()).toBe('modern');
60
+ });
61
+
62
+ it('falls back to pathname matching when public path is unavailable', () => {
63
+ window.__nocobase_modern_client_prefix__ = 'v';
64
+ replacePathname('/v/admin/workflow/workflows/123');
65
+
66
+ expect(getRouteRuntimeVersion()).toBe('modern');
67
+ });
68
+ });