@nocobase/app 2.3.0-beta.3 → 3.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/__tests__/browserChecker.test.ts +113 -0
  2. package/__tests__/clientDevProxy.test.ts +34 -0
  3. package/__tests__/settingsDevProxy.test.ts +96 -0
  4. package/__tests__/settingsPluginImports.test.ts +106 -0
  5. package/client/public/browser-checker.js +11 -4
  6. package/client/rsbuild.config.ts +39 -18
  7. package/client-settings/generatePluginImports.ts +36 -0
  8. package/client-settings/index.html +11 -0
  9. package/client-settings/rsbuild.config.ts +270 -0
  10. package/client-settings/src/.plugins/index.ts +4 -0
  11. package/client-settings/src/SettingsPresetPlugin.ts +56 -0
  12. package/client-settings/src/__tests__/runtimePublicPath.test.ts +23 -0
  13. package/client-settings/src/__tests__/runtimeScope.test.ts +29 -0
  14. package/client-settings/src/env.d.ts +22 -0
  15. package/client-settings/src/main.tsx +78 -0
  16. package/client-settings/src/runtimePublicPath.ts +21 -0
  17. package/client-settings/src/runtimeScope.ts +49 -0
  18. package/client-settings/tsconfig.json +19 -0
  19. package/client-v2/public/browser-checker.js +28 -1
  20. package/client-v2/rsbuild.config.ts +37 -13
  21. package/clientDevProxy.ts +32 -0
  22. package/dist/client/assets/{index-6ad30c04.js → index-ef10bfe1.js} +363 -352
  23. package/dist/client/browser-checker.js +11 -4
  24. package/dist/client/index.html +1 -1
  25. package/dist/client/index.html.tpl +1 -1
  26. package/dist/client/settings/assets/1052-2ed99808.js +1 -0
  27. package/dist/client/settings/assets/108-76a3fdea.js +82 -0
  28. package/dist/client/settings/assets/108-76a3fdea.js.LICENSE.txt +17 -0
  29. package/dist/client/settings/assets/1101-53bb815a.js +1 -0
  30. package/dist/client/settings/assets/1179-1ec4826c.js +1 -0
  31. package/dist/client/settings/assets/1310-c0031389.js +59 -0
  32. package/dist/client/settings/assets/1329-0e47aabd.js +1 -0
  33. package/dist/client/settings/assets/1425-69534606.js +42 -0
  34. package/dist/client/settings/assets/1434-d603f255.js +1 -0
  35. package/dist/client/settings/assets/1463-11b9abe3.js +1 -0
  36. package/dist/client/settings/assets/1469-477cfa59.js +1 -0
  37. package/dist/client/settings/assets/1481-59e4188c.js +1 -0
  38. package/dist/client/settings/assets/1507-9a211af0.js +7 -0
  39. package/dist/client/settings/assets/158-5273dfbb.js +23 -0
  40. package/dist/client/settings/assets/1586-73831f88.js +1 -0
  41. package/dist/client/settings/assets/1667-88ad86eb.js +1 -0
  42. package/dist/client/settings/assets/1675-3cbafc50.js +1 -0
  43. package/dist/client/settings/assets/1676-5db7433e.js +9 -0
  44. package/dist/client/settings/assets/1725-d9ce2582.js +83 -0
  45. package/dist/client/settings/assets/1786-e6c321bb.js +16 -0
  46. package/dist/client/settings/assets/183-963396ee.js +1 -0
  47. package/dist/client/settings/assets/1846-0deb6fc0.js +1 -0
  48. package/dist/client/settings/assets/1848-0ef98f01.js +1 -0
  49. package/dist/client/settings/assets/1863-9160dd98.js +1 -0
  50. package/dist/client/settings/assets/1887-bf619cb3.js +1 -0
  51. package/dist/client/settings/assets/192-c347bb96.js +1 -0
  52. package/dist/client/settings/assets/1951-84be8ebc.js +55 -0
  53. package/dist/client/settings/assets/1979-a893b852.js +1 -0
  54. package/dist/client/settings/assets/1986-01c03982.js +1 -0
  55. package/dist/client/settings/assets/2017-29a9879a.js +26 -0
  56. package/dist/client/settings/assets/2110-961b6f8e.js +1 -0
  57. package/dist/client/settings/assets/212-0f8a26a2.js +18 -0
  58. package/dist/client/settings/assets/2203-9aa79271.js +4 -0
  59. package/dist/client/settings/assets/2258-f667d700.js +1 -0
  60. package/dist/client/settings/assets/2270-ff70d8cb.js +1 -0
  61. package/dist/client/settings/assets/2301-2db05359.js +1 -0
  62. package/dist/client/settings/assets/2322-b9f077c3.js +1 -0
  63. package/dist/client/settings/assets/2335-669f21bd.js +22 -0
  64. package/dist/client/settings/assets/242-c3e15c94.js +1 -0
  65. package/dist/client/settings/assets/2446-ac5fccd3.js +1 -0
  66. package/dist/client/settings/assets/2512-5343dabb.js +1185 -0
  67. package/dist/client/settings/assets/2512-5343dabb.js.LICENSE.txt +9 -0
  68. package/dist/client/settings/assets/2555-4ca49e21.js +31 -0
  69. package/dist/client/settings/assets/2565-ad63e92f.js +1 -0
  70. package/dist/client/settings/assets/2606-c4dcd1e1.js +1 -0
  71. package/dist/client/settings/assets/2613-98648bd2.js +1 -0
  72. package/dist/client/settings/assets/2622-3389992f.js +1 -0
  73. package/dist/client/settings/assets/2702-5ce93acd.js +5 -0
  74. package/dist/client/settings/assets/2710-e7286996.js +36 -0
  75. package/dist/client/settings/assets/2726-4d19faa1.js +23 -0
  76. package/dist/client/settings/assets/2861-48106fd8.js +3 -0
  77. package/dist/client/settings/assets/2898-7656f7f9.js +17 -0
  78. package/dist/client/settings/assets/2913-6b4e6c17.js +1 -0
  79. package/dist/client/settings/assets/2958-93de29a6.js +27 -0
  80. package/dist/client/settings/assets/300-ca09229f.js +1 -0
  81. package/dist/client/settings/assets/3005-1449fc4e.js +56 -0
  82. package/dist/client/settings/assets/3018-fc630932.js +3 -0
  83. package/dist/client/settings/assets/3028-efe6f61f.js +1 -0
  84. package/dist/client/settings/assets/3039-7f273921.js +1 -0
  85. package/dist/client/settings/assets/3102-b985963e.js +1 -0
  86. package/dist/client/settings/assets/3117-cc936eae.js +33 -0
  87. package/dist/client/settings/assets/32-88d3c82f.js +2 -0
  88. package/dist/client/settings/assets/3260-3b3c7cd3.js +1 -0
  89. package/dist/client/settings/assets/3279-8d73fd19.js +1 -0
  90. package/dist/client/settings/assets/3283-654f42f7.js +1 -0
  91. package/dist/client/settings/assets/3309-172e3f79.js +1 -0
  92. package/dist/client/settings/assets/335-da9c4d81.js +1 -0
  93. package/dist/client/settings/assets/338-2f192edd.js +3 -0
  94. package/dist/client/settings/assets/3396-4422ee26.js +1 -0
  95. package/dist/client/settings/assets/3458-2958f051.js +1 -0
  96. package/dist/client/settings/assets/3475-be655098.js +1 -0
  97. package/dist/client/settings/assets/3523-5313a211.js +1 -0
  98. package/dist/client/settings/assets/3526-17f4e8e7.js +1 -0
  99. package/dist/client/settings/assets/3641-c2439c22.js +1 -0
  100. package/dist/client/settings/assets/3646-aa6a2478.js +1 -0
  101. package/dist/client/settings/assets/3660-280439df.js +6 -0
  102. package/dist/client/settings/assets/3662-8dff1ffe.js +28 -0
  103. package/dist/client/settings/assets/3690-4a8bdf43.js +12 -0
  104. package/dist/client/settings/assets/3727-1cfef422.js +44 -0
  105. package/dist/client/settings/assets/3859-fa4f6d5d.js +1 -0
  106. package/dist/client/settings/assets/3898-6236af03.js +1 -0
  107. package/dist/client/settings/assets/3916-3b34f216.js +6 -0
  108. package/dist/client/settings/assets/3937-af967d62.js +3 -0
  109. package/dist/client/settings/assets/3977-3feadc2c.js +1 -0
  110. package/dist/client/settings/assets/3978-29c2602c.js +1 -0
  111. package/dist/client/settings/assets/3989-c8fcfd6c.js +27 -0
  112. package/dist/client/settings/assets/406-497829bb.js +1 -0
  113. package/dist/client/settings/assets/4076-f68c25bd.js +1 -0
  114. package/dist/client/settings/assets/4096-0050f491.js +1 -0
  115. package/dist/client/settings/assets/4171-0710965d.js +2 -0
  116. package/dist/client/settings/assets/4171-0710965d.js.LICENSE.txt +6 -0
  117. package/dist/client/settings/assets/4195-2a00794d.js +1 -0
  118. package/dist/client/settings/assets/4222-29a8147d.js +5 -0
  119. package/dist/client/settings/assets/4223-3821a30e.js +1 -0
  120. package/dist/client/settings/assets/4313-ab774fd9.js +28 -0
  121. package/dist/client/settings/assets/4358-0a504478.js +1 -0
  122. package/dist/client/settings/assets/4419-3ab9c709.js +1 -0
  123. package/dist/client/settings/assets/4456-49679669.js +1 -0
  124. package/dist/client/settings/assets/448-57d6a00d.js +1 -0
  125. package/dist/client/settings/assets/4594-4e72041f.js +1 -0
  126. package/dist/client/settings/assets/4672-573e9a61.js +1 -0
  127. package/dist/client/settings/assets/4686-afbf676f.js +1 -0
  128. package/dist/client/settings/assets/474-ce4285d1.js +1 -0
  129. package/dist/client/settings/assets/4767-8f9d30dc.js +1 -0
  130. package/dist/client/settings/assets/4784-c69aaa68.js +1 -0
  131. package/dist/client/settings/assets/4801-06781ed7.js +1 -0
  132. package/dist/client/settings/assets/492-937af2ba.js +1 -0
  133. package/dist/client/settings/assets/4930-41adc6b7.js +1 -0
  134. package/dist/client/settings/assets/5064-93d8e29e.js +10 -0
  135. package/dist/client/settings/assets/5087-c505a60e.js +1 -0
  136. package/dist/client/settings/assets/5108-a028f8af.js +1 -0
  137. package/dist/client/settings/assets/5122-649463de.js +1 -0
  138. package/dist/client/settings/assets/5123-0bd0a6f2.js +1 -0
  139. package/dist/client/settings/assets/5139-3039fde4.js +1 -0
  140. package/dist/client/settings/assets/5459-c1f208e3.js +1 -0
  141. package/dist/client/settings/assets/5501-387a31be.js +17 -0
  142. package/dist/client/settings/assets/5506-a1f27be3.js +1 -0
  143. package/dist/client/settings/assets/553-1c3126df.js +1 -0
  144. package/dist/client/settings/assets/557-950114e1.js +1 -0
  145. package/dist/client/settings/assets/5640-f846c2a0.js +1 -0
  146. package/dist/client/settings/assets/5642-5c0bab20.js +1 -0
  147. package/dist/client/settings/assets/5674-e33e384f.js +1 -0
  148. package/dist/client/settings/assets/5701-8fe417d1.js +1 -0
  149. package/dist/client/settings/assets/5721-af25e047.js +18 -0
  150. package/dist/client/settings/assets/5757-81a89522.js +1 -0
  151. package/dist/client/settings/assets/5759-2b3bdcc6.js +47 -0
  152. package/dist/client/settings/assets/5761-857b6bbb.js +28 -0
  153. package/dist/client/settings/assets/5791-7c59ba08.js +1 -0
  154. package/dist/client/settings/assets/58-77bfd6c9.js +1 -0
  155. package/dist/client/settings/assets/5815-04fc432a.js +1 -0
  156. package/dist/client/settings/assets/5819-225de680.js +1 -0
  157. package/dist/client/settings/assets/5861-ac056534.js +1 -0
  158. package/dist/client/settings/assets/5870-0c3ecc40.js +1 -0
  159. package/dist/client/settings/assets/5881-b4c942f1.js +1 -0
  160. package/dist/client/settings/assets/5905-a5c3fce6.js +1 -0
  161. package/dist/client/settings/assets/5933-fbe50cd6.js +1 -0
  162. package/dist/client/settings/assets/6062-0e469079.js +1 -0
  163. package/dist/client/settings/assets/6093-641fcafc.js +1 -0
  164. package/dist/client/settings/assets/6157-8e763248.js +1 -0
  165. package/dist/client/settings/assets/6241-55a319df.js +1 -0
  166. package/dist/client/settings/assets/6269-a8a1ea3f.js +1 -0
  167. package/dist/client/settings/assets/630-110e12f4.js +1 -0
  168. package/dist/client/settings/assets/6345-187e76cf.js +1 -0
  169. package/dist/client/settings/assets/6346-94bdf034.js +1 -0
  170. package/dist/client/settings/assets/6373-9d047c6b.js +13 -0
  171. package/dist/client/settings/assets/6437-3865fd7e.js +12 -0
  172. package/dist/client/settings/assets/6581-51c928bf.js +21 -0
  173. package/dist/client/settings/assets/6660-54e08a8d.js +1 -0
  174. package/dist/client/settings/assets/6738-c6598897.js +20 -0
  175. package/dist/client/settings/assets/6870-6a6dba63.js +1 -0
  176. package/dist/client/settings/assets/6914-00d913e7.js +1 -0
  177. package/dist/client/settings/assets/70-f4dc53ad.js +1 -0
  178. package/dist/client/settings/assets/700-e75b7faa.js +15 -0
  179. package/dist/client/settings/assets/7021-4975d54d.js +45 -0
  180. package/dist/client/settings/assets/7095-4f425b85.js +1 -0
  181. package/dist/client/settings/assets/7141-b8e2a787.js +1 -0
  182. package/dist/client/settings/assets/7201-5b49ce84.js +14 -0
  183. package/dist/client/settings/assets/7211-b8c71ed2.js +9 -0
  184. package/dist/client/settings/assets/7318-6e1c7877.js +1 -0
  185. package/dist/client/settings/assets/7386-2f5752d4.js +1 -0
  186. package/dist/client/settings/assets/7413-241d463c.js +1 -0
  187. package/dist/client/settings/assets/7492-5c593f52.js +5 -0
  188. package/dist/client/settings/assets/7507-a015d8cf.js +1 -0
  189. package/dist/client/settings/assets/754-41b763b9.js +17 -0
  190. package/dist/client/settings/assets/7555-f42cdb72.js +1 -0
  191. package/dist/client/settings/assets/7608-2b3f2db0.js +1 -0
  192. package/dist/client/settings/assets/7669-8bd94b73.js +1 -0
  193. package/dist/client/settings/assets/7761-637067c6.js +123 -0
  194. package/dist/client/settings/assets/7876-673cbc3c.js +6 -0
  195. package/dist/client/settings/assets/791-9cfa98c8.js +1 -0
  196. package/dist/client/settings/assets/7946-e07199df.js +13 -0
  197. package/dist/client/settings/assets/7959-f7ef21c6.js +40 -0
  198. package/dist/client/settings/assets/8053-fa93540e.js +1 -0
  199. package/dist/client/settings/assets/8087-90829087.js +1 -0
  200. package/dist/client/settings/assets/8138-e90c290e.js +24 -0
  201. package/dist/client/settings/assets/8217-de1369d7.js +1 -0
  202. package/dist/client/settings/assets/8242-3f880357.js +108 -0
  203. package/dist/client/settings/assets/8252-1153ffdd.js +1 -0
  204. package/dist/client/settings/assets/8262-7a324b59.js +9 -0
  205. package/dist/client/settings/assets/8269-52ea88cd.js +1 -0
  206. package/dist/client/settings/assets/8276-b3dfc452.js +1 -0
  207. package/dist/client/settings/assets/828-55028e33.js +1 -0
  208. package/dist/client/settings/assets/8294-9c81768d.js +1 -0
  209. package/dist/client/settings/assets/8295-447d3bb3.js +1 -0
  210. package/dist/client/settings/assets/8349-82eb3ecd.js +1 -0
  211. package/dist/client/settings/assets/8434-9140d106.js +25 -0
  212. package/dist/client/settings/assets/8438-d2ff6bc1.js +37 -0
  213. package/dist/client/settings/assets/8473-b1118703.js +1 -0
  214. package/dist/client/settings/assets/8480-c16f7ee4.js +1 -0
  215. package/dist/client/settings/assets/8562-ffa35312.js +17 -0
  216. package/dist/client/settings/assets/8624-847569d9.js +1 -0
  217. package/dist/client/settings/assets/8631-b2fd9aac.js +1 -0
  218. package/dist/client/settings/assets/8635-27980c94.js +1 -0
  219. package/dist/client/settings/assets/8676-987fe455.js +1 -0
  220. package/dist/client/settings/assets/8729-9adea5fe.js +1 -0
  221. package/dist/client/settings/assets/8759-4d7f7a69.js +1 -0
  222. package/dist/client/settings/assets/896-d07bab4d.js +1 -0
  223. package/dist/client/settings/assets/9021-ec1b8442.js +1 -0
  224. package/dist/client/settings/assets/9065-5df7b63d.js +1 -0
  225. package/dist/client/settings/assets/9170-efbfcc1e.js +1 -0
  226. package/dist/client/settings/assets/9204-6cb7a01b.js +1 -0
  227. package/dist/client/settings/assets/9217-f27c2dcb.js +1 -0
  228. package/dist/client/settings/assets/9248-ff8e06d0.js +1 -0
  229. package/dist/client/settings/assets/9274-83aa1da2.js +1 -0
  230. package/dist/client/settings/assets/929-a37c623d.js +1 -0
  231. package/dist/client/settings/assets/93-dc732e8a.js +1 -0
  232. package/dist/client/settings/assets/9343-196a2ff7.js +474 -0
  233. package/dist/client/settings/assets/9343-196a2ff7.js.LICENSE.txt +2567 -0
  234. package/dist/client/settings/assets/9343-db3cd5c2.css +1 -0
  235. package/dist/client/settings/assets/9518-4c8b8805.js +1 -0
  236. package/dist/client/settings/assets/9550-2daf603c.js +1 -0
  237. package/dist/client/settings/assets/9565-2158d75f.js +1 -0
  238. package/dist/client/settings/assets/9578-f64d8970.js +1 -0
  239. package/dist/client/settings/assets/9584-969e1d2b.js +1 -0
  240. package/dist/client/settings/assets/9592-523a3fa6.js +1 -0
  241. package/dist/client/settings/assets/9827-d1f02095.js +9 -0
  242. package/dist/client/settings/assets/9836-fcbb81f6.js +22 -0
  243. package/dist/client/settings/assets/9847-bc9f1ba4.js +1 -0
  244. package/dist/client/settings/assets/9953-407a1cde.js +1 -0
  245. package/dist/client/settings/assets/index-d6fb11f5.js +2007 -0
  246. package/dist/client/settings/assets/index-d6fb11f5.js.LICENSE.txt +1 -0
  247. package/dist/client/settings/assets/runtime-6b2f3ebe.js +1 -0
  248. package/dist/client/settings/browser-checker.js +234 -0
  249. package/dist/client/settings/favicon/favicon.ico +0 -0
  250. package/dist/client/settings/file-placeholder/ai-200-200.png +0 -0
  251. package/dist/client/settings/file-placeholder/audio-200-200.png +0 -0
  252. package/dist/client/settings/file-placeholder/docx-200-200.png +0 -0
  253. package/dist/client/settings/file-placeholder/eps-200-200.png +0 -0
  254. package/dist/client/settings/file-placeholder/gif-200-200.png +0 -0
  255. package/dist/client/settings/file-placeholder/jpeg-200-200.png +0 -0
  256. package/dist/client/settings/file-placeholder/pdf-200-200.png +0 -0
  257. package/dist/client/settings/file-placeholder/png-200-200.png +0 -0
  258. package/dist/client/settings/file-placeholder/pptx-200-200.png +0 -0
  259. package/dist/client/settings/file-placeholder/psd-200-200.png +0 -0
  260. package/dist/client/settings/file-placeholder/svg-200-200.png +0 -0
  261. package/dist/client/settings/file-placeholder/unknown-200-200.png +0 -0
  262. package/dist/client/settings/file-placeholder/video-200-200.png +0 -0
  263. package/dist/client/settings/file-placeholder/xlsx-200-200.png +0 -0
  264. package/dist/client/settings/file-placeholder/zip-200-200.png +0 -0
  265. package/dist/client/settings/global.css +63 -0
  266. package/dist/client/settings/index.html +22 -0
  267. package/dist/client/settings/nocobase.png +0 -0
  268. package/dist/client/settings/robots.txt +2 -0
  269. package/dist/client/v/assets/{index-5ba21f48.js → index-489daf33.js} +191 -180
  270. package/dist/client/v/assets/index-489daf33.js.LICENSE.txt +1 -0
  271. package/dist/client/v/browser-checker.js +28 -1
  272. package/dist/client/v/index.html +1 -1
  273. package/package.json +7 -7
  274. package/settingsDevProxy.ts +52 -0
  275. /package/dist/client/assets/{index-6ad30c04.js.LICENSE.txt → index-ef10bfe1.js.LICENSE.txt} +0 -0
  276. /package/dist/client/{v/assets/index-5ba21f48.js.LICENSE.txt → settings/assets/32-88d3c82f.js.LICENSE.txt} +0 -0
@@ -0,0 +1,113 @@
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 { readFileSync } from 'node:fs';
11
+ import { resolve } from 'node:path';
12
+ import vm from 'node:vm';
13
+ import { describe, expect, it } from 'vitest';
14
+
15
+ const browserCheckerSource = readFileSync(resolve(__dirname, '../client-v2/public/browser-checker.js'), 'utf8');
16
+
17
+ interface RunBrowserCheckerOptions {
18
+ appClientEntryMode?: string;
19
+ currentScriptSrc?: string;
20
+ hash?: string;
21
+ modernPrefix?: string;
22
+ pathname: string;
23
+ publicPath?: string;
24
+ search?: string;
25
+ }
26
+
27
+ function runBrowserChecker(options: RunBrowserCheckerOptions) {
28
+ const replacements: string[] = [];
29
+ const windowObject: Record<string, unknown> = {
30
+ __nocobase_app_client_entry_mode__: options.appClientEntryMode,
31
+ __nocobase_modern_client_prefix__: options.modernPrefix,
32
+ __nocobase_public_path__: options.publicPath ?? '/v/',
33
+ console: {
34
+ log: () => undefined,
35
+ },
36
+ devicePixelRatio: 1,
37
+ location: {
38
+ hash: options.hash ?? '',
39
+ origin: 'https://example.test',
40
+ pathname: options.pathname,
41
+ replace: (url: string) => replacements.push(url),
42
+ search: options.search ?? '',
43
+ },
44
+ outerHeight: 720,
45
+ outerWidth: 1280,
46
+ };
47
+ const documentElement = {
48
+ className: 'no-js',
49
+ clientHeight: 720,
50
+ clientWidth: 1280,
51
+ };
52
+
53
+ vm.runInNewContext(browserCheckerSource, {
54
+ console: {
55
+ debug: () => undefined,
56
+ log: () => undefined,
57
+ },
58
+ document: {
59
+ currentScript: {
60
+ src: options.currentScriptSrc ?? 'https://example.test/v/browser-checker.js',
61
+ },
62
+ documentElement,
63
+ },
64
+ navigator: {
65
+ userAgent:
66
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
67
+ },
68
+ window: windowObject,
69
+ });
70
+
71
+ return {
72
+ className: documentElement.className,
73
+ replacements,
74
+ };
75
+ }
76
+
77
+ describe('v2 browser checker', () => {
78
+ it.each([undefined, 'modern-default', 'modern-only', 'settings-default'] as const)(
79
+ 'lets the modern client handle its root for entry mode %s',
80
+ (appClientEntryMode) => {
81
+ expect(
82
+ runBrowserChecker({
83
+ appClientEntryMode,
84
+ pathname: '/v/',
85
+ }).replacements,
86
+ ).toEqual([]);
87
+ },
88
+ );
89
+
90
+ it.each([undefined, 'modern-default', 'modern-only', 'settings-default'] as const)(
91
+ 'lets scoped modern clients handle their roots for entry mode %s',
92
+ (appClientEntryMode) => {
93
+ expect(
94
+ runBrowserChecker({
95
+ appClientEntryMode,
96
+ pathname: '/v/apps/demo',
97
+ search: '?from=admin',
98
+ hash: '#portal',
99
+ }).replacements,
100
+ ).toEqual([]);
101
+ },
102
+ );
103
+
104
+ it('keeps Settings scoped trailing slash normalization independent from entry mode', () => {
105
+ expect(
106
+ runBrowserChecker({
107
+ currentScriptSrc: 'https://example.test/settings/browser-checker.js',
108
+ pathname: '/settings/apps/demo',
109
+ publicPath: '/',
110
+ }).replacements,
111
+ ).toEqual(['https://example.test/settings/apps/demo/']);
112
+ });
113
+ });
@@ -0,0 +1,34 @@
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 } from 'vitest';
11
+ import { isClientDevProxyPath, rewriteClientDevProxyRootPath } from '../clientDevProxy';
12
+
13
+ describe('client dev proxy', () => {
14
+ it.each([
15
+ ['/x', '/x/', true],
16
+ ['/x/', '/x/', true],
17
+ ['/x/test2/assets/index.js', '/x/', true],
18
+ ['/x-other', '/x/', false],
19
+ ['/console/v?from=portal', '/console/v/', true],
20
+ ['/v', '/console/v/', false],
21
+ ])('matches complete client base paths: %s', (url, basePath, expected) => {
22
+ expect(isClientDevProxyPath(url, basePath)).toBe(expected);
23
+ });
24
+
25
+ it.each([
26
+ ['/v', '/v/', '/v/'],
27
+ ['/v?from=portal', '/v/', '/v/?from=portal'],
28
+ ['/console/v#section', '/console/v/', '/console/v/#section'],
29
+ ['/v/admin', '/v/', '/v/admin'],
30
+ ['/v/', '/v/', '/v/'],
31
+ ])('normalizes only a slashless client root: %s', (url, basePath, expected) => {
32
+ expect(rewriteClientDevProxyRootPath(url, basePath)).toBe(expected);
33
+ });
34
+ });
@@ -0,0 +1,96 @@
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 } from 'vitest';
11
+ import { createSettingsDevProxyOptions, isSettingsDevPath, rewriteSettingsDevProxyPath } from '../settingsDevProxy';
12
+
13
+ describe('settings dev proxy', () => {
14
+ it.each([
15
+ ['/settings', true],
16
+ ['/settings/signin', true],
17
+ ['/settings/signup', true],
18
+ ['/settings/forgot-password', true],
19
+ ['/settings/reset-password?resetToken=test-token', true],
20
+ ['/settings/2fa?redirect=%2Fsettings%2Fworkflow', true],
21
+ ['/settings/workflow/workflows/1?tab=nodes', true],
22
+ ['/settings/apps/demo/signin', true],
23
+ ['/settings/apps/demo', true],
24
+ ['/settings/apps/demo/workflow/workflows/1', true],
25
+ ['/settings/_app/demo/reset-password?resetToken=test-token', true],
26
+ ['/settings/_app/demo/ai/knowledge-base/detail/k1', true],
27
+ ['/nocobase/settings/assets/index.js', false],
28
+ ['/admin/settings', false],
29
+ ['/v/admin/settings', false],
30
+ ['/apps/demo/settings', false],
31
+ ['/_app/demo/settings', false],
32
+ ['/apps/demo/admin/settings', false],
33
+ ])('matches only standalone Settings paths: %s', (pathname, expected) => {
34
+ expect(isSettingsDevPath(pathname, '/')).toBe(expected);
35
+ });
36
+
37
+ it.each([
38
+ ['/nocobase/settings', true],
39
+ ['/nocobase/settings/signin', true],
40
+ ['/nocobase/settings/2fa?redirect=%2Fnocobase%2Fsettings', true],
41
+ ['/nocobase/settings/workflow/workflows/1', true],
42
+ ['/nocobase/settings/apps/demo/forgot-password', true],
43
+ ['/nocobase/settings/apps/demo', true],
44
+ ['/nocobase/settings/_app/demo/reset-password?resetToken=test-token', true],
45
+ ['/nocobase/settings/_app/demo/ai/knowledge-base/detail/k1', true],
46
+ ['/settings', false],
47
+ ['/nocobase/admin/settings', false],
48
+ ['/nocobase/apps/demo/settings', false],
49
+ ])('honors APP_PUBLIC_PATH: %s', (pathname, expected) => {
50
+ expect(isSettingsDevPath(pathname, '/nocobase/')).toBe(expected);
51
+ });
52
+
53
+ it.each([
54
+ ['/settings', '/', '/settings/'],
55
+ ['/settings?from=admin', '/', '/settings/?from=admin'],
56
+ ['/settings/apps/demo', '/', '/settings/apps/demo'],
57
+ ['/settings/_app/demo?from=admin', '/', '/settings/_app/demo?from=admin'],
58
+ ['/nocobase/settings', '/nocobase/', '/nocobase/settings/'],
59
+ ['/nocobase/settings/apps/demo?from=admin', '/nocobase/', '/nocobase/settings/apps/demo?from=admin'],
60
+ ['/nocobase/settings/_app/demo#portal', '/nocobase/', '/nocobase/settings/_app/demo#portal'],
61
+ ])('normalizes a Settings root to the dev-server base: %s', (pathname, publicPath, expected) => {
62
+ expect(rewriteSettingsDevProxyPath(pathname, publicPath)).toBe(expected);
63
+ });
64
+
65
+ it('preserves application-scoped documents for the Settings dev server', () => {
66
+ expect(rewriteSettingsDevProxyPath('/settings/apps/demo/workflow/workflows/1?tab=nodes', '/')).toBe(
67
+ '/settings/apps/demo/workflow/workflows/1?tab=nodes',
68
+ );
69
+ expect(rewriteSettingsDevProxyPath('/nocobase/settings/apps/demo/a#hash', '/nocobase/')).toBe(
70
+ '/nocobase/settings/apps/demo/a#hash',
71
+ );
72
+ expect(rewriteSettingsDevProxyPath('/settings/assets/index.js', '/')).toBe('/settings/assets/index.js');
73
+ expect(rewriteSettingsDevProxyPath('/settings/__rspack_hmr', '/')).toBe('/settings/__rspack_hmr');
74
+ });
75
+
76
+ it('creates a websocket-capable proxy for the Settings port', () => {
77
+ const options = createSettingsDevProxyOptions('/nocobase/', 13004);
78
+
79
+ expect(options).toMatchObject({
80
+ target: 'http://127.0.0.1:13004',
81
+ changeOrigin: true,
82
+ ws: true,
83
+ xfwd: true,
84
+ });
85
+ expect(options.context?.('/nocobase/settings/__rspack_hmr')).toBe(true);
86
+ expect(options.context?.('/nocobase/settings/signin')).toBe(true);
87
+ expect(options.context?.('/nocobase/settings/apps/demo/workflow')).toBe(true);
88
+ expect(options.context?.('/nocobase/settings/apps/demo/2fa')).toBe(true);
89
+ expect(options.context?.('/nocobase/settings/_app/demo/ai')).toBe(true);
90
+ expect(options.context?.('/nocobase/apps/demo/settings/workflow')).toBe(false);
91
+ expect(options.context?.('/nocobase/admin/settings')).toBe(false);
92
+ expect(options.pathRewrite?.('/nocobase/settings/apps/demo/workflow')).toBe(
93
+ '/nocobase/settings/apps/demo/workflow',
94
+ );
95
+ });
96
+ });
@@ -0,0 +1,106 @@
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 { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import path from 'node:path';
13
+ import { afterEach, describe, expect, it } from 'vitest';
14
+ import { generateSettingsPluginImports } from '../client-settings/generatePluginImports';
15
+
16
+ const temporaryDirectories: string[] = [];
17
+
18
+ function createTemporaryDirectory() {
19
+ const directory = mkdtempSync(path.join(tmpdir(), 'nocobase-settings-plugin-imports-'));
20
+ temporaryDirectories.push(directory);
21
+ return directory;
22
+ }
23
+
24
+ function createV2Plugin(pluginRoot: string) {
25
+ const pluginDirectory = path.join(pluginRoot, '@example', 'plugin-demo');
26
+ mkdirSync(pluginDirectory, { recursive: true });
27
+ writeFileSync(
28
+ path.join(pluginDirectory, 'package.json'),
29
+ JSON.stringify({ name: '@example/plugin-demo', version: '1.0.0' }),
30
+ );
31
+ writeFileSync(path.join(pluginDirectory, 'client-v2.js'), 'module.exports = {};');
32
+ mkdirSync(path.join(pluginDirectory, 'src', 'client-v2'), { recursive: true });
33
+ writeFileSync(path.join(pluginDirectory, 'src', 'client-v2', 'index.ts'), 'export default class DemoPlugin {}');
34
+ }
35
+
36
+ afterEach(() => {
37
+ for (const directory of temporaryDirectories.splice(0)) {
38
+ rmSync(directory, { recursive: true, force: true });
39
+ }
40
+ });
41
+
42
+ describe('Settings plugin imports', () => {
43
+ it('generates the existing V2 plugin lane into a Settings-owned directory', () => {
44
+ const root = createTemporaryDirectory();
45
+ const pluginRoot = path.join(root, 'plugins');
46
+ const settingsOutput = path.join(root, 'client-settings', 'src', '.plugins');
47
+ const clientV2Output = path.join(root, 'client-v2', 'src', '.plugins');
48
+ createV2Plugin(pluginRoot);
49
+ mkdirSync(clientV2Output, { recursive: true });
50
+ writeFileSync(path.join(clientV2Output, 'index.ts'), 'export const clientV2DevManifest = true;');
51
+
52
+ const localPluginsOnly = process.env.NOCOBASE_DEV_LOCAL_PLUGINS_ONLY;
53
+ process.env.NOCOBASE_DEV_LOCAL_PLUGINS_ONLY = 'true';
54
+ try {
55
+ generateSettingsPluginImports(settingsOutput, [pluginRoot]);
56
+ } finally {
57
+ if (localPluginsOnly === undefined) {
58
+ delete process.env.NOCOBASE_DEV_LOCAL_PLUGINS_ONLY;
59
+ } else {
60
+ process.env.NOCOBASE_DEV_LOCAL_PLUGINS_ONLY = localPluginsOnly;
61
+ }
62
+ }
63
+
64
+ expect(JSON.parse(readFileSync(path.join(settingsOutput, 'packageMap.json'), 'utf8'))).toEqual({
65
+ '@example/plugin-demo': 'example_plugin_demo.ts',
66
+ });
67
+ expect(readFileSync(path.join(clientV2Output, 'index.ts'), 'utf8')).toBe(
68
+ 'export const clientV2DevManifest = true;',
69
+ );
70
+ expect(existsSync(path.join(settingsOutput, 'packages', 'example_plugin_demo.ts'))).toBe(true);
71
+ });
72
+
73
+ it('keeps a production Settings manifest isolated from the Client V2 development manifest', () => {
74
+ const root = createTemporaryDirectory();
75
+ const settingsOutput = path.join(root, 'client-settings', 'src', '.plugins');
76
+ const clientV2Output = path.join(root, 'client-v2', 'src', '.plugins');
77
+ mkdirSync(clientV2Output, { recursive: true });
78
+ writeFileSync(path.join(clientV2Output, 'index.ts'), 'export const clientV2DevManifest = true;');
79
+
80
+ const nodeEnv = process.env.NODE_ENV;
81
+ process.env.NODE_ENV = 'production';
82
+ try {
83
+ generateSettingsPluginImports(settingsOutput, []);
84
+ } finally {
85
+ if (nodeEnv === undefined) {
86
+ delete process.env.NODE_ENV;
87
+ } else {
88
+ process.env.NODE_ENV = nodeEnv;
89
+ }
90
+ }
91
+
92
+ expect(readFileSync(path.join(settingsOutput, 'index.ts'), 'utf8')).toContain('return Promise.resolve(null)');
93
+ expect(readFileSync(path.join(clientV2Output, 'index.ts'), 'utf8')).toBe(
94
+ 'export const clientV2DevManifest = true;',
95
+ );
96
+ });
97
+
98
+ it('wires the Settings config and entry to the Settings-owned generated directory', () => {
99
+ const configSource = readFileSync(path.resolve(__dirname, '../client-settings/rsbuild.config.ts'), 'utf8');
100
+ const entrySource = readFileSync(path.resolve(__dirname, '../client-settings/src/main.tsx'), 'utf8');
101
+
102
+ expect(configSource).not.toContain('generateV2Plugins');
103
+ expect(configSource).toContain("generateSettingsPluginImports(path.resolve(__dirname, 'src/.plugins'))");
104
+ expect(entrySource).toContain("from './.plugins'");
105
+ });
106
+ });
@@ -54,7 +54,7 @@ if (basename !== '/' && currentPath === basenameWithoutTrailingSlash) {
54
54
  // This client-side redirect is still needed because legacy `index.html` is
55
55
  // not always served through the node gateway. In nginx/static delivery paths
56
56
  // the browser may already be running the legacy shell by the time entry-mode
57
- // logic is evaluated, so the last hop into the modern entry has to be
57
+ // logic is evaluated, so the last hop into the configured default entry has to be
58
58
  // recoverable in the browser as well.
59
59
  const normalizedPath = normalizePathname(currentPath);
60
60
  const relativePath =
@@ -66,15 +66,22 @@ if (basename !== '/' && currentPath === basenameWithoutTrailingSlash) {
66
66
  ? normalizePathname(normalizedPath.slice(basename.length - 1))
67
67
  : null;
68
68
  const modernBase = `${trimTrailingSlash(basename)}/${modernClientPrefix}/`.replace(/\/{2,}/g, '/');
69
+ const settingsBase = `${basename}settings/`;
69
70
  const isModernDefault = appClientEntryMode === 'modern-default';
70
71
  const isModernOnly = appClientEntryMode === 'modern-only';
72
+ const isSettingsDefault = appClientEntryMode === 'settings-default';
73
+ const targetBase = isSettingsDefault ? settingsBase : modernBase;
71
74
  if (
72
75
  relativePath &&
73
76
  isClientDocumentEntryPath(relativePath) &&
74
- (isModernDefault || isModernOnly) &&
75
- !normalizedPath.startsWith(modernBase)
77
+ (isModernDefault || isModernOnly || isSettingsDefault) &&
78
+ !normalizedPath.startsWith(targetBase)
76
79
  ) {
77
- const targetPath = isModernDefault
80
+ const targetPath = isSettingsDefault
81
+ ? relativePath === '/' || relativePath === '/index.html'
82
+ ? settingsBase
83
+ : null
84
+ : isModernDefault
78
85
  ? relativePath === '/' || relativePath === '/index.html'
79
86
  ? relativePath === '/index.html'
80
87
  ? `${modernBase}index.html`
@@ -16,6 +16,8 @@ import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill';
16
16
  import { pluginReact } from '@rsbuild/plugin-react';
17
17
  import { pluginSvgr } from '@rsbuild/plugin-svgr';
18
18
  import { generatePlugins, getRsbuildBrowserAlias } from '@nocobase/devtools/rsbuildConfig';
19
+ import { isClientDevProxyPath, rewriteClientDevProxyRootPath } from '../clientDevProxy';
20
+ import { createSettingsDevProxyOptions } from '../settingsDevProxy';
19
21
 
20
22
  const __filename = fileURLToPath(import.meta.url);
21
23
  const __dirname = path.dirname(__filename);
@@ -46,11 +48,19 @@ function toDefineLiteral(value: string | undefined) {
46
48
  return value === undefined ? 'undefined' : JSON.stringify(value);
47
49
  }
48
50
 
49
- function createRuntimeHeadScript(appPublicPath: string, isBuild: boolean) {
50
- const modernClientPrefix =
51
- String(process.env.APP_MODERN_CLIENT_PREFIX || 'v')
51
+ function normalizeModernClientPrefix(value: string | undefined) {
52
+ const normalized =
53
+ String(value || 'v')
52
54
  .trim()
53
55
  .replace(/^\/+|\/+$/g, '') || 'v';
56
+ if (normalized === 'settings') {
57
+ throw new Error('APP_MODERN_CLIENT_PREFIX "settings" is reserved for the standalone Settings application.');
58
+ }
59
+ return normalized;
60
+ }
61
+
62
+ function createRuntimeHeadScript(appPublicPath: string, isBuild: boolean) {
63
+ const modernClientPrefix = normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX);
54
64
  const appClientEntryMode = process.env.APP_CLIENT_ENTRY_MODE;
55
65
  if (!isBuild) {
56
66
  return [
@@ -109,16 +119,15 @@ export default defineConfig(({ command }) => {
109
119
  const localStorageBasePath = ensurePublicPath(`${resolvedAppPublicPath}storage/uploads/`, '/storage/uploads/');
110
120
  const staticBasePath = ensurePublicPath(`${resolvedAppPublicPath}static/`, '/static/');
111
121
  const wsBasePath = ensurePublicPath(process.env.WS_PATH, '/ws/');
112
- const modernClientPrefix =
113
- String(process.env.APP_MODERN_CLIENT_PREFIX || 'v')
114
- .trim()
115
- .replace(/^\/+|\/+$/g, '') || 'v';
122
+ const modernClientPrefix = normalizeModernClientPrefix(process.env.APP_MODERN_CLIENT_PREFIX);
116
123
  const v2BasePath = ensurePublicPath(
117
124
  `${resolvedAppPublicPath.replace(/\/$/, '')}/${modernClientPrefix}/`,
118
125
  `/${modernClientPrefix}/`,
119
126
  );
127
+ const portalBasePath = ensurePublicPath(`${resolvedAppPublicPath.replace(/\/$/, '')}/x/`, '/x/');
120
128
  const clientPort = toNumber(process.env.APP_PORT, 13001);
121
129
  const v2Port = toNumber(process.env.APP_V2_PORT, clientPort + 2);
130
+ const settingsPort = toNumber(process.env.APP_SETTINGS_PORT, clientPort + 3);
122
131
  const hmrPath = `${resolvedAppPublicPath.replace(/\/$/, '')}/__rspack_hmr`;
123
132
  const proxyTargetUrl = process.env.PROXY_TARGET_URL || `http://127.0.0.1:${clientPort + 1}`;
124
133
  const hmrClientHost = process.env.RSPACK_HMR_CLIENT_HOST;
@@ -209,8 +218,10 @@ export default defineConfig(({ command }) => {
209
218
  publicDir: {
210
219
  name: path.resolve(__dirname, 'public'),
211
220
  },
212
- proxy: {
213
- [apiBasePath]: {
221
+ proxy: [
222
+ createSettingsDevProxyOptions(resolvedAppPublicPath, settingsPort),
223
+ {
224
+ context: apiBasePath,
214
225
  target: proxyTargetUrl,
215
226
  changeOrigin: true,
216
227
  ws: true,
@@ -228,34 +239,44 @@ export default defineConfig(({ command }) => {
228
239
  }
229
240
  },
230
241
  },
231
- [localStorageBasePath]: {
242
+ {
243
+ context: localStorageBasePath,
232
244
  target: proxyTargetUrl,
233
245
  changeOrigin: true,
234
246
  },
235
- [fileBasePath]: {
247
+ {
248
+ context: fileBasePath,
236
249
  target: proxyTargetUrl,
237
250
  changeOrigin: true,
238
251
  },
239
- [staticBasePath]: {
252
+ {
253
+ context: staticBasePath,
240
254
  target: proxyTargetUrl,
241
255
  changeOrigin: true,
242
256
  },
243
- [wsBasePath]: {
257
+ {
258
+ context: wsBasePath,
244
259
  target: proxyTargetUrl,
245
260
  changeOrigin: true,
246
261
  ws: true,
247
262
  xfwd: true,
248
263
  },
249
- [v2BasePath]: {
264
+ {
265
+ context: (pathname) => isClientDevProxyPath(pathname, v2BasePath),
250
266
  target: `http://127.0.0.1:${v2Port}`,
251
267
  changeOrigin: true,
252
268
  ws: true,
253
- pathRewrite: {
254
- [`^${v2BasePath}`]: v2BasePath,
255
- },
269
+ pathRewrite: (pathname) => rewriteClientDevProxyRootPath(pathname, v2BasePath),
256
270
  xfwd: true,
257
271
  },
258
- },
272
+ {
273
+ context: (pathname) => isClientDevProxyPath(pathname, portalBasePath),
274
+ target: proxyTargetUrl,
275
+ changeOrigin: true,
276
+ ws: true,
277
+ xfwd: true,
278
+ },
279
+ ],
259
280
  historyApiFallback: {
260
281
  disableDotRule: true,
261
282
  index: `${resolvedAppPublicPath}index.html`,
@@ -0,0 +1,36 @@
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 path from 'node:path';
11
+ import { IndexGenerator } from '../../devtools/common.js';
12
+
13
+ type V2PluginIndexGenerator = new (
14
+ outputPath: string,
15
+ pluginPaths: string[],
16
+ options: {
17
+ clientModuleName: string;
18
+ clientRootFile: string;
19
+ clientSourceDir: string;
20
+ },
21
+ ) => { generate(): void };
22
+
23
+ function getPluginDirectories() {
24
+ return (process.env.PLUGIN_PATH || 'packages/plugins/,packages/samples/,packages/pro-plugins/')
25
+ .split(',')
26
+ .map((directory) => path.resolve(process.cwd(), directory));
27
+ }
28
+
29
+ export function generateSettingsPluginImports(outputPath: string, pluginPaths = getPluginDirectories()) {
30
+ const Generator = IndexGenerator as unknown as V2PluginIndexGenerator;
31
+ new Generator(outputPath, pluginPaths, {
32
+ clientModuleName: 'client-v2',
33
+ clientRootFile: 'client-v2.js',
34
+ clientSourceDir: 'client-v2',
35
+ }).generate();
36
+ }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6
+ <title>Loading...</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ </body>
11
+ </html>