@nocobase/client-v2 2.2.0-beta.8 → 2.2.0

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 (271) hide show
  1. package/es/APIClient.d.ts +13 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/RouteRepository.d.ts +8 -0
  4. package/es/RouterManager.d.ts +15 -0
  5. package/es/authRedirect.d.ts +1 -0
  6. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  8. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  9. package/es/components/category-tabs/index.d.ts +9 -0
  10. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  11. package/es/components/form/TypedVariableInput.d.ts +31 -5
  12. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  13. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  14. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  15. package/es/components/form/filter/index.d.ts +2 -0
  16. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  17. package/es/components/index.d.ts +1 -0
  18. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  19. package/es/entry-actions/index.d.ts +9 -0
  20. package/es/flow/actions/index.d.ts +1 -1
  21. package/es/flow/actions/linkageRules.d.ts +27 -0
  22. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  23. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  24. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  25. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  26. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  27. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  28. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  29. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  30. package/es/flow/components/FieldAssignValueInput.d.ts +7 -24
  31. package/es/flow/components/FlowRoute.d.ts +2 -2
  32. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  33. package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
  34. package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
  35. package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
  36. package/es/flow/components/field-value-variable/index.d.ts +11 -0
  37. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  38. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  39. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
  40. package/es/flow/index.d.ts +1 -0
  41. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  42. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
  43. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
  44. package/es/flow/models/base/ActionModelCore.d.ts +6 -2
  45. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  46. package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
  47. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  48. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  49. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  50. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
  51. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  52. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
  53. package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
  54. package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
  55. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  56. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
  57. package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
  58. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  59. package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
  60. package/es/flow/models/blocks/table/utils.d.ts +1 -0
  61. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
  62. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
  63. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
  64. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  65. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  66. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  67. package/es/flow-compat/passwordUtils.d.ts +1 -1
  68. package/es/flow-compat/routeTypes.d.ts +1 -0
  69. package/es/index.d.ts +7 -0
  70. package/es/index.mjs +301 -177
  71. package/es/ui-operation/index.d.ts +15 -0
  72. package/es/ui-operation/ui-operation-codec.d.ts +10 -0
  73. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  74. package/es/utils/index.d.ts +1 -0
  75. package/es/utils/markdownSanitize.d.ts +11 -0
  76. package/lib/index.js +313 -189
  77. package/lib/locale/languageCodes.js +3 -1
  78. package/package.json +9 -7
  79. package/src/APIClient.ts +90 -7
  80. package/src/Application.tsx +7 -1
  81. package/src/BaseApplication.tsx +13 -6
  82. package/src/RouteRepository.ts +25 -0
  83. package/src/RouterManager.tsx +142 -1
  84. package/src/__tests__/APIClient.test.tsx +58 -0
  85. package/src/__tests__/RouteRepository.test.ts +23 -0
  86. package/src/__tests__/RouterManager.test.ts +125 -0
  87. package/src/__tests__/app.test.tsx +73 -0
  88. package/src/__tests__/authRedirect.test.ts +17 -0
  89. package/src/__tests__/browserChecker.test.ts +61 -0
  90. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  91. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +35 -2
  92. package/src/__tests__/settings-center.test.tsx +270 -6
  93. package/src/acl/ACLProvider.tsx +2 -0
  94. package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
  95. package/src/authRedirect.ts +38 -0
  96. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  97. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  98. package/src/collection-manager/field-validation.ts +1 -1
  99. package/src/components/README.md +7 -1
  100. package/src/components/README.zh-CN.md +6 -1
  101. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  102. package/src/components/category-tabs/index.ts +10 -0
  103. package/src/components/form/JsonTextArea.tsx +4 -0
  104. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  105. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  106. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  107. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  108. package/src/components/form/TypedVariableInput.tsx +452 -101
  109. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  110. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  111. package/src/components/form/filter/CollectionFilterItem.tsx +36 -9
  112. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  113. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +55 -0
  114. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  115. package/src/components/form/filter/index.ts +2 -0
  116. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  117. package/src/components/form/table/Table.tsx +81 -6
  118. package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
  119. package/src/components/index.ts +1 -0
  120. package/src/entry-actions/EntryActionManager.ts +76 -0
  121. package/src/entry-actions/index.ts +10 -0
  122. package/src/flow/FlowPage.tsx +38 -3
  123. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  124. package/src/flow/__tests__/FlowRoute.test.tsx +675 -12
  125. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  126. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  127. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +62 -0
  128. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  129. package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
  130. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
  131. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  132. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  133. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +80 -20
  134. package/src/flow/actions/dataScopeFilter.ts +10 -1
  135. package/src/flow/actions/dateTimeFormat.tsx +2 -2
  136. package/src/flow/actions/index.ts +2 -0
  137. package/src/flow/actions/linkageRules.tsx +144 -26
  138. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  139. package/src/flow/actions/openView.tsx +21 -1
  140. package/src/flow/admin-shell/BaseLayoutModel.tsx +124 -0
  141. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +184 -42
  142. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1016 -119
  143. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +41 -5
  144. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +177 -1
  145. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +20 -0
  146. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
  147. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  148. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  149. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  150. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
  151. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +436 -2
  152. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +48 -0
  153. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  154. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  155. package/src/flow/common/Markdown/Display.tsx +14 -3
  156. package/src/flow/common/Markdown/Edit.tsx +19 -7
  157. package/src/flow/common/Markdown/style.ts +4 -0
  158. package/src/flow/components/BlockItemCard.tsx +2 -2
  159. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  160. package/src/flow/components/FieldAssignValueInput.tsx +72 -584
  161. package/src/flow/components/FlowRoute.tsx +204 -46
  162. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  163. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  164. package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
  165. package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
  166. package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
  167. package/src/flow/components/field-value-variable/dateValue.ts +223 -0
  168. package/src/flow/components/field-value-variable/index.ts +12 -0
  169. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  170. package/src/flow/components/filter/VariableFilterItem.tsx +170 -13
  171. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  172. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  173. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +195 -1
  174. package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
  175. package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
  176. package/src/flow/index.ts +3 -0
  177. package/src/flow/internal/components/Markdown/util.ts +2 -1
  178. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  179. package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
  180. package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
  181. package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
  182. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
  183. package/src/flow/models/base/ActionModel.tsx +10 -8
  184. package/src/flow/models/base/ActionModelCore.tsx +16 -2
  185. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  186. package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
  187. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  188. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  189. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  190. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  191. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  192. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  193. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  194. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  195. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  196. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  197. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
  198. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
  199. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
  200. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
  201. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
  202. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
  203. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +155 -25
  204. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
  205. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
  206. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +395 -1
  207. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
  208. package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
  209. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
  210. package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
  211. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
  212. package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
  213. package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
  214. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  215. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
  216. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  217. package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
  218. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
  219. package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
  220. package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
  221. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  222. package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
  223. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
  224. package/src/flow/models/blocks/table/TableBlockModel.tsx +29 -3
  225. package/src/flow/models/blocks/table/TableColumnModel.tsx +54 -11
  226. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
  227. package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
  228. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  229. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +84 -0
  230. package/src/flow/models/blocks/table/utils.ts +7 -0
  231. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
  232. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +10 -1
  233. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
  234. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
  235. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
  236. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +54 -149
  237. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
  238. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
  239. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  240. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  241. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
  242. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  243. package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
  244. package/src/flow/models/fields/TextareaFieldModel.tsx +42 -19
  245. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  246. package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
  247. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  248. package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
  249. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +32 -1
  250. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  251. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +26 -10
  252. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +51 -14
  253. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
  254. package/src/flow/models/topbar/TopbarActionModel.tsx +78 -3
  255. package/src/flow/routeTransientInputArgs.ts +27 -0
  256. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +42 -0
  257. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  258. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  259. package/src/flow-compat/routeTypes.ts +1 -0
  260. package/src/index.ts +7 -0
  261. package/src/layout-manager/LayoutContentRoute.tsx +2 -1
  262. package/src/layout-manager/LayoutRoute.tsx +2 -1
  263. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
  264. package/src/locale/languageCodes.ts +3 -1
  265. package/src/nocobase-buildin-plugin/index.tsx +19 -1
  266. package/src/settings-center/AdminSettingsLayout.tsx +23 -2
  267. package/src/ui-operation/index.ts +33 -0
  268. package/src/ui-operation/ui-operation-codec.ts +54 -0
  269. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  270. package/src/utils/index.tsx +1 -0
  271. package/src/utils/markdownSanitize.ts +88 -0
@@ -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,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
+ });
@@ -55,6 +55,7 @@ describe('nocobase buildin plugin auth redirect', () => {
55
55
  const originalLocation = globalThis.window.location;
56
56
 
57
57
  beforeEach(() => {
58
+ globalThis.window.localStorage.clear();
58
59
  // These fixtures mount the modern client under the `v2` segment; tell the
59
60
  // runtime-prefix helper so v2-runtime detection matches (server injects it
60
61
  // in production).
@@ -75,6 +76,7 @@ describe('nocobase buildin plugin auth redirect', () => {
75
76
  });
76
77
 
77
78
  afterEach(() => {
79
+ globalThis.window.localStorage.clear();
78
80
  delete (globalThis.window as any).__nocobase_modern_client_prefix__;
79
81
  Object.defineProperty(globalThis.window, 'location', {
80
82
  configurable: true,
@@ -116,6 +118,7 @@ describe('nocobase buildin plugin auth redirect', () => {
116
118
  plugins: [NocoBaseBuildInPlugin as any],
117
119
  router: { type: 'memory', initialEntries: ['/v2/admin'] },
118
120
  });
121
+ app.apiClient.auth.setToken('test-token');
119
122
  app.apiMock.onGet('app:getLang').reply(200, {
120
123
  data: { lang: 'en-US', resources: { client: {} }, cron: {} },
121
124
  });
@@ -130,6 +133,7 @@ describe('nocobase buildin plugin auth redirect', () => {
130
133
  await new Promise((resolve) => setTimeout(resolve, 50));
131
134
  expect(app.router.router.state.location.pathname).toBe('/v2/admin');
132
135
  expect(app.router.router.state.location.search).toBe('');
136
+ expect(app.apiMock.history.post.filter((request) => request.url === 'auth:syncCookies')).toHaveLength(0);
133
137
  });
134
138
 
135
139
  it('should redirect unauthenticated v2 root access to v2 signin via <Navigate />', async () => {
@@ -326,6 +330,7 @@ describe('nocobase buildin plugin auth redirect', () => {
326
330
  data: { lang: 'en-US', resources: { client: {} }, cron: {} },
327
331
  });
328
332
  app.apiMock.onGet('/auth:check').reply(200, { data: { id: 1 } });
333
+ app.apiMock.onPost('auth:syncCookies').reply(200, { data: { synced: true } });
329
334
  const ensureLoaded = vi.spyOn(app.dataSourceManager, 'ensureLoaded').mockResolvedValue(undefined);
330
335
 
331
336
  const Root = app.getRootComponent();
@@ -333,6 +338,29 @@ describe('nocobase buildin plugin auth redirect', () => {
333
338
 
334
339
  expect(await screen.findByText('secure page')).toBeInTheDocument();
335
340
  expect(ensureLoaded).toHaveBeenCalledTimes(1);
341
+ expect(app.apiMock.history.post.filter((request) => request.url === 'auth:syncCookies')).toHaveLength(1);
342
+ });
343
+
344
+ it('should keep authenticated v2 startup working when cookie bootstrap fails', async () => {
345
+ const app = createMockClient({
346
+ publicPath: '/v2/',
347
+ plugins: [NocoBaseBuildInPlugin as any, AuthBootstrapRoutePlugin as any],
348
+ router: { type: 'memory', initialEntries: ['/v2/secure'] },
349
+ });
350
+ app.apiClient.auth.setToken('test-token');
351
+ app.apiMock.onGet('app:getLang').reply(200, {
352
+ data: { lang: 'en-US', resources: { client: {} }, cron: {} },
353
+ });
354
+ app.apiMock.onGet('/auth:check').reply(200, { data: { id: 1 } });
355
+ app.apiMock.onPost('auth:syncCookies').reply(500, { errors: [{ message: 'cookie sync failed' }] });
356
+ const ensureLoaded = vi.spyOn(app.dataSourceManager, 'ensureLoaded').mockResolvedValue(undefined);
357
+
358
+ const Root = app.getRootComponent();
359
+ render(<Root />);
360
+
361
+ expect(await screen.findByText('secure page')).toBeInTheDocument();
362
+ expect(ensureLoaded).toHaveBeenCalledTimes(1);
363
+ expect(app.apiMock.history.post.filter((request) => request.url === 'auth:syncCookies')).toHaveLength(1);
336
364
  });
337
365
 
338
366
  it('should not auth-check or bootstrap authCheck false routes', async () => {
@@ -387,7 +415,7 @@ describe('nocobase buildin plugin auth redirect', () => {
387
415
  });
388
416
 
389
417
  it.each(['/v2/admin/legacy-page/tab/tab-1', '/v2/admin/legacy-page/view/detail'])(
390
- 'should show 404 for authenticated direct v1-style v2 page access: %s',
418
+ 'should explain authenticated direct v1-style v2 page access: %s',
391
419
  async (pathname) => {
392
420
  const app = createMockClient({
393
421
  publicPath: '/v2/',
@@ -413,7 +441,12 @@ describe('nocobase buildin plugin auth redirect', () => {
413
441
  const Root = app.getRootComponent();
414
442
  render(<Root />);
415
443
 
416
- expect(await screen.findByText('404')).toBeInTheDocument();
444
+ expect(await screen.findByText('This page is not supported in the /v2/ branch')).toBeInTheDocument();
445
+ expect(screen.queryByText('404')).not.toBeInTheDocument();
446
+ expect(screen.getByRole('link', { name: 'Open from the original entry' })).toHaveAttribute(
447
+ 'href',
448
+ pathname.replace(/^\/v2/, ''),
449
+ );
417
450
  expect(app.router.router.state.location.pathname).toBe(pathname);
418
451
  },
419
452
  );