@oj-module/cli 0.2.64 → 0.2.67

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 (369) hide show
  1. package/bin/.oj-version +1 -0
  2. package/bin/oj +4 -0
  3. package/devkit/README.md +18 -4
  4. package/devkit/SKILL.md +35 -3
  5. package/devkit/framework-dev.md +22 -11
  6. package/devkit/manual.md +161 -68
  7. package/devkit/menu-rules.md +4 -1
  8. package/devkit/publish-walkthrough.md +110 -37
  9. package/devkit/wecom-internal-app.md +7 -2
  10. package/devkit/wecom-multi-corp-links.md +163 -0
  11. package/package.json +3 -2
  12. package/shell-dist/assets/ahooks.2l850ymmh0.js +18 -0
  13. package/shell-dist/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js +1 -0
  14. package/shell-dist/assets/antd-img-crop.196rkty480.js +99 -0
  15. package/shell-dist/assets/antd-locale-en_US.561tozx860.js +1 -0
  16. package/shell-dist/assets/antd-locale-zh_CN.1cmpmgzh70.js +1 -0
  17. package/shell-dist/assets/antd.uw1coq15l0.js +348 -0
  18. package/shell-dist/assets/clsx.jotilrt2e0.js +1 -0
  19. package/shell-dist/assets/contract-errors.2o17vz9u50.js +1 -0
  20. package/shell-dist/assets/contract.3h327o3mj0.js +57 -0
  21. package/shell-dist/assets/cssinjs.atgmhan7m0.js +3 -0
  22. package/shell-dist/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js +1 -0
  23. package/shell-dist/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js +1 -0
  24. package/shell-dist/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js +1 -0
  25. package/shell-dist/assets/dayjs-plugin-localeData.2rwbjp9nk0.js +1 -0
  26. package/shell-dist/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js +1 -0
  27. package/shell-dist/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js +1 -0
  28. package/shell-dist/assets/dayjs-plugin-weekOfYear.gvn9iph080.js +1 -0
  29. package/shell-dist/assets/dayjs-plugin-weekYear.2u78l3ex10.js +1 -0
  30. package/shell-dist/assets/dayjs-plugin-weekday.2d56a4tml0.js +1 -0
  31. package/shell-dist/assets/dayjs.pa60l6wjo0.js +1 -0
  32. package/shell-dist/assets/dnd-kit-core.1xyreig6r0.js +5 -0
  33. package/shell-dist/assets/dnd-kit-sortable.s94qciuft0.js +1 -0
  34. package/shell-dist/assets/dnd-kit-utilities.1umw4xphb0.js +1 -0
  35. package/shell-dist/assets/i18next.33zqofapj0.js +1 -0
  36. package/shell-dist/assets/icons-es-components-Context.365qmflwg0.js +1 -0
  37. package/shell-dist/assets/icons.159wjwals0.js +57 -0
  38. package/shell-dist/assets/{index-xZhmujjM.js → index.6alqyx6ae0.js} +21 -1
  39. package/shell-dist/assets/jsx-dev-runtime.3uibuc82f0.js +14 -0
  40. package/shell-dist/assets/jsx-runtime.h40xu4o5b0.js +14 -0
  41. package/shell-dist/assets/keepalive-for-react.xenu5vam70.js +1 -0
  42. package/shell-dist/assets/kit.37kr8ui390.js +1 -0
  43. package/shell-dist/assets/ky.1cgch7gu60.js +6 -0
  44. package/shell-dist/assets/motion-react.pjsxytvjs0.js +12 -0
  45. package/shell-dist/assets/motion.21tciqutg0.js +4 -0
  46. package/shell-dist/assets/nprogress.2nz76n9ny0.js +7 -0
  47. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js +9 -0
  48. package/shell-dist/assets/pinyin-pro.3v0mnxczn0.js +1 -0
  49. package/shell-dist/assets/pro-components.2kyot1vcl0.js +83 -0
  50. package/shell-dist/assets/react-countup.2x8idnr8y0.js +10 -0
  51. package/shell-dist/assets/react-dom-client.ick0v05ob0.js +43 -0
  52. package/shell-dist/assets/react-dom.2so9uxg3f0.js +22 -0
  53. package/shell-dist/assets/react-error-boundary.2dncdfj3b0.js +1 -0
  54. package/shell-dist/assets/react-i18next.auhvjhqv00.js +1 -0
  55. package/shell-dist/assets/react-jss.1ei6dr9e00.js +29 -0
  56. package/shell-dist/assets/react-query.3rln45ow00.js +1 -0
  57. package/shell-dist/assets/react-router-dom.3c6zld8h40.js +44 -0
  58. package/shell-dist/assets/react-router.2h9rebcv50.js +56 -0
  59. package/shell-dist/assets/react.1ew63mwts0.js +14 -0
  60. package/shell-dist/assets/runtime.2dgstpjsu0.js +21702 -0
  61. package/shell-dist/assets/simplebar-react.2g739uvai0.js +1 -0
  62. package/shell-dist/assets/spin-delay.yjeyj6iwp0.js +9 -0
  63. package/shell-dist/assets/tailwind-merge.125ehiqjw0.js +1 -0
  64. package/shell-dist/assets/use-sync-external-store-shim.1nz1pi9bb0.js +22 -0
  65. package/shell-dist/assets/zustand-middleware.1xaxjueme0.js +6 -0
  66. package/shell-dist/assets/zustand-react-shallow.pybayv1hx0.js +1 -0
  67. package/shell-dist/assets/zustand-react.9fsl9brxy0.js +1 -0
  68. package/shell-dist/assets/zustand-shallow.6cz0yyv9x0.js +1 -0
  69. package/shell-dist/assets/zustand-vanilla-shallow.3424monv50.js +1 -0
  70. package/shell-dist/assets/zustand-vanilla.1mo3ka3g30.js +1 -0
  71. package/shell-dist/assets/zustand.3gjgzlnqo0.js +1 -0
  72. package/shell-dist/index.html +67 -118
  73. package/shell-dist/versions.json +3 -3
  74. package/src/__tests__/publish-keygen.test.ts +157 -0
  75. package/src/__tests__/templates-migration-schema-parity.test.ts +540 -0
  76. package/src/__tests__/templates-schema-columns.test.ts +15 -2
  77. package/src/__tests__/templates-tenant-scope.test.ts +6 -2
  78. package/src/bundle.ts +8 -3
  79. package/src/cli.ts +17 -5
  80. package/src/dev.ts +15 -10
  81. package/src/esm-exports.ts +152 -70
  82. package/src/install.ts +11 -4
  83. package/src/keygen.ts +54 -0
  84. package/src/lifecycle/__tests__/bundle.test.ts +21 -3
  85. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/manifest.yaml +1 -1
  86. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/migrations/0001__init.sql +2 -2
  87. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/schema.yaml +5 -2
  88. package/src/lifecycle/__tests__/fixtures/fixture-valid/manifest.yaml +1 -1
  89. package/src/lifecycle/__tests__/manifest-contract.test.ts +75 -3
  90. package/src/lifecycle/__tests__/smoke.test.ts +2 -2
  91. package/src/manifest-contract.ts +91 -8
  92. package/src/market/bootstrap.ts +26 -2
  93. package/src/market/command.ts +1 -0
  94. package/src/market/publish.ts +5 -1
  95. package/src/market/walkthrough.ts +24 -8
  96. package/src/shared-deps.ts +69 -19
  97. package/src/site-base.ts +4 -0
  98. package/src/site.ts +2 -0
  99. package/src/usage.ts +8 -4
  100. package/src/vendor.ts +5 -1
  101. package/templates/config.yaml +4 -2
  102. package/templates/init.include.yaml +8 -3
  103. package/templates/openapi.json +460 -1
  104. package/templates/src/app/api/_platform/_lifecycle/compensate.ts +136 -0
  105. package/templates/src/app/api/_platform/_lifecycle/installer.ts +71 -22
  106. package/templates/src/app/api/_platform/_lifecycle/lock.ts +23 -0
  107. package/templates/src/app/api/_platform/_lifecycle/steps/deploy-web.ts +28 -0
  108. package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +46 -0
  109. package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +100 -36
  110. package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +7 -15
  111. package/templates/src/app/api/_platform/_lifecycle/text-edit.ts +71 -19
  112. package/templates/src/app/api/_platform/_lifecycle/types.ts +26 -2
  113. package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +93 -14
  114. package/templates/src/app/api/_platform/_shared/service/domain/settings.ts +8 -4
  115. package/templates/src/app/api/_platform/manifest.yaml +1 -0
  116. package/templates/src/app/api/_platform/market/api.ts +235 -10
  117. package/templates/src/app/api/_platform/migrations/0001__baseline.mysql.sql +178 -0
  118. package/templates/src/app/api/_platform/migrations/0001__baseline.pg.sql +173 -0
  119. package/templates/src/app/api/_platform/migrations/0001__baseline.sql +188 -0
  120. package/templates/src/app/api/_platform/migrations/0002__market_release.mysql.sql +17 -0
  121. package/templates/src/app/api/_platform/migrations/0002__market_release.pg.sql +16 -0
  122. package/templates/src/app/api/_platform/migrations/0002__market_release.sql +33 -0
  123. package/templates/src/app/api/_platform/schema.yaml +18 -0
  124. package/templates/src/app/api/_platform/seed.mysql.sql +10 -0
  125. package/templates/src/app/api/_platform/seed.pg.sql +10 -0
  126. package/templates/src/app/api/auth/_shared/wecom/client.ts +24 -4
  127. package/templates/src/app/api/home/migrations/0001__create_home_tables.mysql.sql +14 -0
  128. package/templates/src/app/api/home/migrations/0001__create_home_tables.pg.sql +14 -0
  129. package/templates/src/app/api/home/migrations/0002__tenant.pg.sql +4 -0
  130. package/templates/src/app/api/home/seed.mysql.sql +71 -0
  131. package/templates/src/app/api/home/seed.pg.sql +71 -0
  132. package/templates/src/app/api/notification/_shared/notify.ts +6 -6
  133. package/templates/src/app/api/notification/migrations/0001__create_notifications.mysql.sql +11 -0
  134. package/templates/src/app/api/notification/migrations/0001__create_notifications.pg.sql +11 -0
  135. package/templates/src/app/api/notification/migrations/0002__notifications_user.mysql.sql +8 -0
  136. package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.mysql.sql +21 -0
  137. package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.pg.sql +22 -0
  138. package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.mysql.sql +10 -0
  139. package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.pg.sql +11 -0
  140. package/templates/src/app/api/notification/migrations/0005__tenant.pg.sql +6 -0
  141. package/templates/src/app/api/notification/seed.mysql.sql +8 -0
  142. package/templates/src/app/api/notification/seed.pg.sql +11 -0
  143. package/templates/src/app/api/publish/_shared/identity.ts +69 -0
  144. package/templates/src/app/api/publish/api.ts +349 -0
  145. package/templates/src/app/api/publish/manifest.yaml +8 -0
  146. package/templates/src/app/api/publish/migrations/0001__publish_credential.mysql.sql +23 -0
  147. package/templates/src/app/api/publish/migrations/0001__publish_credential.pg.sql +23 -0
  148. package/templates/src/app/api/publish/migrations/0001__publish_credential.sql +39 -0
  149. package/templates/src/app/api/publish/schema.yaml +33 -0
  150. package/templates/src/app/api/system/login-methods/api.ts +2 -1
  151. package/templates/src/app/web/_platform/__client/api.schemas.ts +15 -0
  152. package/templates/src/app/web/_platform/__client/index.ts +25 -1
  153. package/templates/src/app/web/entry.ts +9 -5
  154. package/templates/src/app/web/home/pages/components/bar-chart.tsx +5 -3
  155. package/templates/src/app/web/home/pages/components/echarts-setup.ts +30 -0
  156. package/templates/src/app/web/home/pages/components/line-chart.tsx +5 -3
  157. package/templates/src/app/web/home/pages/components/pie-chart.tsx +6 -3
  158. package/templates/src/app/web/home/pages/index.tsx +3 -2
  159. package/templates/src/app/web/home/routes.ts +8 -2
  160. package/templates/src/app/web/login/locales/en-US.json +7 -1
  161. package/templates/src/app/web/login/locales/zh-CN.json +7 -1
  162. package/templates/src/app/web/login/pages/login.tsx +79 -86
  163. package/templates/src/app/web/login/routes.ts +54 -1
  164. package/templates/src/app/web/personal-center/locales/en-US.json +1 -0
  165. package/templates/src/app/web/personal-center/locales/zh-CN.json +1 -0
  166. package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +5 -0
  167. package/templates/src/app/web/system/__client/api.schemas.ts +2 -1
  168. package/templates/src/app/web/system/locales/en-US.json +1 -8
  169. package/templates/src/app/web/system/locales/zh-CN.json +1 -8
  170. package/templates/src/app/web/system/pages/settings/index.tsx +17 -4
  171. package/templates/src/app/web/system/routes.ts +0 -6
  172. package/templates/src/oj-market/web/__tests__/CardGrid.test.tsx +33 -0
  173. package/templates/src/oj-market/web/__tests__/DetailDrawer.test.tsx +69 -0
  174. package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +7 -1
  175. package/templates/src/oj-market/web/api/client.ts +3 -0
  176. package/templates/src/oj-market/web/api/defaultClient.ts +5 -0
  177. package/templates/src/oj-market/web/components/CardItem.tsx +44 -1
  178. package/templates/src/oj-market/web/components/DetailDrawer.tsx +17 -1
  179. package/templates/src/oj-market/web/entry.ts +31 -2
  180. package/templates/src/oj-market/web/locales/en-US.json +5 -1
  181. package/templates/src/oj-market/web/locales/zh-CN.json +5 -1
  182. package/templates/src/oj-market/web/publish/__client/api.schemas.ts +61 -0
  183. package/templates/src/oj-market/web/publish/__client/index.ts +164 -0
  184. package/templates/src/oj-market/web/publish/locales/en-US.json +80 -0
  185. package/templates/src/oj-market/web/publish/locales/zh-CN.json +80 -0
  186. package/templates/src/oj-market/web/publish/pages/history.tsx +112 -0
  187. package/templates/src/oj-market/web/publish/pages/index.tsx +196 -0
  188. package/templates/src/oj-market/web/publish/pages/model.ts +52 -0
  189. package/templates/src/oj-market/web/publish/pages/token-modal.tsx +82 -0
  190. package/templates/src/oj-market/web/publish/routes.ts +41 -0
  191. package/templates/src/oj-market/web/types.ts +30 -0
  192. package/templates/tests/lifecycle/compensate.spec.ts +207 -0
  193. package/templates/tests/lifecycle/deploy-smoke.spec.ts +38 -14
  194. package/templates/tests/lifecycle/installer.spec.ts +28 -9
  195. package/templates/tests/lifecycle/market-api.spec.ts +164 -12
  196. package/templates/tests/lifecycle/preflight.spec.ts +68 -0
  197. package/templates/tests/lifecycle/role-title-translate.spec.ts +2 -32
  198. package/templates/tests/lifecycle/text-edit.spec.ts +45 -27
  199. package/templates/tests/lifecycle/uninstall.spec.ts +36 -0
  200. package/templates/tests/publish-credential.spec.ts +354 -0
  201. package/templates/tests/publish-identity.spec.ts +44 -0
  202. package/templates/tests/wecom-client-timeout.spec.ts +33 -3
  203. package/templates/tests/wecom-landing.spec.ts +63 -1
  204. package/templates/tsconfig.paths.json +1 -0
  205. package/templates/web.config.ts +6 -4
  206. package/vendor/host-versions.json +3 -3
  207. package/shell-dist/assets/ahooks.js +0 -6909
  208. package/shell-dist/assets/antd-es-locale-zh_CN.js +0 -257
  209. package/shell-dist/assets/antd-img-crop.js +0 -1495
  210. package/shell-dist/assets/antd-locale-en_US.js +0 -374
  211. package/shell-dist/assets/antd-locale-zh_CN.js +0 -379
  212. package/shell-dist/assets/antd.js +0 -106874
  213. package/shell-dist/assets/clsx.js +0 -33
  214. package/shell-dist/assets/contract-errors.js +0 -30
  215. package/shell-dist/assets/contract.js +0 -18900
  216. package/shell-dist/assets/cssinjs.js +0 -2595
  217. package/shell-dist/assets/dayjs-plugin-advancedFormat.js +0 -98
  218. package/shell-dist/assets/dayjs-plugin-customParseFormat.js +0 -173
  219. package/shell-dist/assets/dayjs-plugin-isoWeek.js +0 -77
  220. package/shell-dist/assets/dayjs-plugin-localeData.js +0 -126
  221. package/shell-dist/assets/dayjs-plugin-quarterOfYear.js +0 -77
  222. package/shell-dist/assets/dayjs-plugin-relativeTime.js +0 -92
  223. package/shell-dist/assets/dayjs-plugin-weekOfYear.js +0 -73
  224. package/shell-dist/assets/dayjs-plugin-weekYear.js +0 -63
  225. package/shell-dist/assets/dayjs-plugin-weekday.js +0 -63
  226. package/shell-dist/assets/dayjs.js +0 -326
  227. package/shell-dist/assets/dnd-kit-core.js +0 -3601
  228. package/shell-dist/assets/dnd-kit-sortable.js +0 -717
  229. package/shell-dist/assets/dnd-kit-utilities.js +0 -340
  230. package/shell-dist/assets/i18next.js +0 -2321
  231. package/shell-dist/assets/icons-es-components-Context.js +0 -20
  232. package/shell-dist/assets/icons.js +0 -34961
  233. package/shell-dist/assets/jsx-dev-runtime.js +0 -75
  234. package/shell-dist/assets/jsx-runtime.js +0 -97
  235. package/shell-dist/assets/keepalive-for-react.js +0 -283
  236. package/shell-dist/assets/kit.js +0 -151
  237. package/shell-dist/assets/ky.js +0 -1427
  238. package/shell-dist/assets/motion-react.js +0 -13589
  239. package/shell-dist/assets/motion.js +0 -9959
  240. package/shell-dist/assets/nprogress.js +0 -314
  241. package/shell-dist/assets/ojm--ant-design-icons-es-icons-BarsOutlined.js +0 -7
  242. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CalendarOutlined.js +0 -7
  243. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownFilled.js +0 -7
  244. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownOutlined.js +0 -7
  245. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretUpOutlined.js +0 -7
  246. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckCircleFilled.js +0 -7
  247. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckOutlined.js +0 -7
  248. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ClockCircleOutlined.js +0 -7
  249. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseCircleFilled.js +0 -7
  250. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseOutlined.js +0 -7
  251. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CopyOutlined.js +0 -7
  252. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DeleteOutlined.js +0 -7
  253. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleLeftOutlined.js +0 -7
  254. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleRightOutlined.js +0 -7
  255. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownOutlined.js +0 -7
  256. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownloadOutlined.js +0 -7
  257. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EditOutlined.js +0 -7
  258. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EllipsisOutlined.js +0 -7
  259. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EnterOutlined.js +0 -7
  260. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ExclamationCircleFilled.js +0 -7
  261. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeInvisibleOutlined.js +0 -7
  262. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeOutlined.js +0 -7
  263. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileOutlined.js +0 -7
  264. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileTextOutlined.js +0 -7
  265. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FilterFilled.js +0 -7
  266. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOpenOutlined.js +0 -7
  267. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOutlined.js +0 -7
  268. package/shell-dist/assets/ojm--ant-design-icons-es-icons-HolderOutlined.js +0 -7
  269. package/shell-dist/assets/ojm--ant-design-icons-es-icons-InfoCircleFilled.js +0 -7
  270. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LeftOutlined.js +0 -7
  271. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LoadingOutlined.js +0 -7
  272. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusOutlined.js +0 -7
  273. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusSquareOutlined.js +0 -7
  274. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PaperClipOutlined.js +0 -7
  275. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PictureOutlined.js +0 -7
  276. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusOutlined.js +0 -7
  277. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusSquareOutlined.js +0 -7
  278. package/shell-dist/assets/ojm--ant-design-icons-es-icons-QuestionCircleOutlined.js +0 -7
  279. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ReloadOutlined.js +0 -7
  280. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RightOutlined.js +0 -7
  281. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateLeftOutlined.js +0 -7
  282. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateRightOutlined.js +0 -7
  283. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SearchOutlined.js +0 -7
  284. package/shell-dist/assets/ojm--ant-design-icons-es-icons-StarFilled.js +0 -7
  285. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapOutlined.js +0 -7
  286. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapRightOutlined.js +0 -7
  287. package/shell-dist/assets/ojm--ant-design-icons-es-icons-UpOutlined.js +0 -7
  288. package/shell-dist/assets/ojm--ant-design-icons-es-icons-VerticalAlignTopOutlined.js +0 -7
  289. package/shell-dist/assets/ojm--ant-design-icons-es-icons-WarningFilled.js +0 -7
  290. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomInOutlined.js +0 -7
  291. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomOutOutlined.js +0 -7
  292. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.js +0 -81
  293. package/shell-dist/assets/pinyin-pro.js +0 -25175
  294. package/shell-dist/assets/pro-components.js +0 -44627
  295. package/shell-dist/assets/react-countup.js +0 -525
  296. package/shell-dist/assets/react-dom-client.js +0 -12134
  297. package/shell-dist/assets/react-dom.js +0 -266
  298. package/shell-dist/assets/react-error-boundary.js +0 -141
  299. package/shell-dist/assets/react-i18next.js +0 -1773
  300. package/shell-dist/assets/react-jss.js +0 -4089
  301. package/shell-dist/assets/react-query.js +0 -3072
  302. package/shell-dist/assets/react-router-dom.js +0 -9927
  303. package/shell-dist/assets/react-router.js +0 -14584
  304. package/shell-dist/assets/react.js +0 -590
  305. package/shell-dist/assets/runtime.js +0 -28311
  306. package/shell-dist/assets/simplebar-react.js +0 -1119
  307. package/shell-dist/assets/spin-delay.js +0 -111
  308. package/shell-dist/assets/tailwind-merge.js +0 -3364
  309. package/shell-dist/assets/use-sync-external-store-shim.js +0 -130
  310. package/shell-dist/assets/zustand-middleware.js +0 -513
  311. package/shell-dist/assets/zustand-react-shallow.js +0 -27
  312. package/shell-dist/assets/zustand-react.js +0 -39
  313. package/shell-dist/assets/zustand-shallow.js +0 -22
  314. package/shell-dist/assets/zustand-vanilla-shallow.js +0 -71
  315. package/shell-dist/assets/zustand-vanilla.js +0 -40
  316. package/shell-dist/assets/zustand.js +0 -32
  317. package/templates/seed.mysql.sql +0 -6
  318. package/templates/seed.pg.sql +0 -6
  319. package/templates/src/app/api/_platform/migrations/0001__create_users.mysql.sql +0 -9
  320. package/templates/src/app/api/_platform/migrations/0001__create_users.pg.sql +0 -9
  321. package/templates/src/app/api/_platform/migrations/0001__create_users.sql +0 -7
  322. package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.mysql.sql +0 -3
  323. package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.sql +0 -2
  324. package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.mysql.sql +0 -5
  325. package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.sql +0 -5
  326. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.mysql.sql +0 -70
  327. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.pg.sql +0 -67
  328. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.sql +0 -67
  329. package/templates/src/app/api/_platform/migrations/0005__users_must_change.mysql.sql +0 -3
  330. package/templates/src/app/api/_platform/migrations/0005__users_must_change.sql +0 -2
  331. package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.mysql.sql +0 -3
  332. package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.sql +0 -3
  333. package/templates/src/app/api/_platform/migrations/0007__users_email.sql +0 -3
  334. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.mysql.sql +0 -10
  335. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.pg.sql +0 -10
  336. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.sql +0 -11
  337. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.mysql.sql +0 -12
  338. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.pg.sql +0 -12
  339. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.sql +0 -13
  340. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.mysql.sql +0 -17
  341. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.pg.sql +0 -16
  342. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.sql +0 -18
  343. package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.mysql.sql +0 -2
  344. package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.sql +0 -4
  345. package/templates/src/app/api/_platform/migrations/0012__market_history.mysql.sql +0 -13
  346. package/templates/src/app/api/_platform/migrations/0012__market_history.pg.sql +0 -12
  347. package/templates/src/app/api/_platform/migrations/0012__market_history.sql +0 -14
  348. package/templates/src/app/api/_platform/migrations/0013__users_profile.sql +0 -4
  349. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.mysql.sql +0 -9
  350. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.pg.sql +0 -7
  351. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.sql +0 -8
  352. package/templates/src/app/api/_platform/migrations/0015__tenant.mysql.sql +0 -12
  353. package/templates/src/app/api/_platform/migrations/0015__tenant.pg.sql +0 -11
  354. package/templates/src/app/api/_platform/migrations/0015__tenant.sql +0 -44
  355. package/templates/src/app/api/_platform/migrations/0016__org.mysql.sql +0 -41
  356. package/templates/src/app/api/_platform/migrations/0016__org.pg.sql +0 -40
  357. package/templates/src/app/api/_platform/migrations/0016__org.sql +0 -62
  358. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +0 -7
  359. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +0 -4
  360. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +0 -8
  361. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +0 -3
  362. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +0 -3
  363. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +0 -8
  364. package/templates/src/app/web/system/pages/login-methods/index.tsx +0 -30
  365. /package/shell-dist/assets/{nprogress-css.js → nprogress-css.1cq2pzkre0.js} +0 -0
  366. /package/shell-dist/assets/{ojm-antd-es-button.js → ojm-antd-es-button.2uriphio50.js} +0 -0
  367. /package/shell-dist/assets/{ojm-antd-es-modal.js → ojm-antd-es-modal.bf3tgdh8v0.js} +0 -0
  368. /package/shell-dist/assets/{ojm-antd-es-slider.js → ojm-antd-es-slider.xyvptuhid0.js} +0 -0
  369. /package/shell-dist/assets/{ojm-antd-es-upload.js → ojm-antd-es-upload.13gx45e0g0.js} +0 -0
@@ -26,7 +26,7 @@ peerPlatform: '>=0.1.0' # C2-11(P2 起对宿主 API_VERSION 强制;本仓 AP
26
26
  api:
27
27
  module: demo-solution # C2-12(存在时必须 === name)
28
28
  prefix: /api/demo-solution # C2-13(默认形态显式写出)
29
- tables: [note] # 与 schema.yaml 表集一致(oj 冗余校验同源)
29
+ tables: [demo_solution_note] # 与 schema.yaml 表集一致(oj 冗余校验同源);表名带 C2-12c 前缀
30
30
  deps: # 键 = 模块名(C2-01 语法或 _platform);值 = 范围算子语法
31
31
  _platform: '>=0.1.0'
32
32
  anonymous: # C2-26:首段 === 模块段;无登录语义端点
@@ -14,7 +14,7 @@ import { parse } from "yaml";
14
14
  // 跨端引用(双端一致钉):被测实现同仓分居两运行时(计划 §1「两运行时取舍」),
15
15
  // 测试直接互调,比「两套件各读同一夹具」更强——漂移即本文件红。
16
16
  import { preflight } from "../../../templates/src/app/api/_platform/_lifecycle/steps/preflight";
17
- import { checkLocalesComplete, checkTablesAgainstSchema, parseManifest, RESERVED_NAMES, reservedNameSuggestions, validateContract } from "../../manifest-contract";
17
+ import { checkLocalesComplete, checkSchemaTablePrefix, checkTablesAgainstSchema, parseManifest, RESERVED_NAMES, reservedNameSuggestions, validateContract } from "../../manifest-contract";
18
18
 
19
19
  const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "fixtures");
20
20
  const readText = (p: string) => readFileSync(join(FIXTURES, p), "utf8");
@@ -214,19 +214,91 @@ describe("rEG-CS7 保留名扩项(红队 F5)", () => {
214
214
  describe("checkTablesAgainstSchema(C2-12a:构建 ✅ 对照 schema.yaml)", () => {
215
215
  const SCHEMA = readText("fixture-valid/api/src/demo-solution/schema.yaml");
216
216
 
217
- it("fixture-valid tables [note] 与 schema.yaml 表集一致 → 零违规", () => {
217
+ it("fixture-valid tables 与 schema.yaml 表集一致 → 零违规", () => {
218
218
  expect(checkTablesAgainstSchema(structuredClone(VALID), SCHEMA)).toEqual([]);
219
219
  });
220
220
 
221
221
  it("tables 与 schema.yaml 表集不一致 → C2-12a 且指明差额", () => {
222
222
  const m = structuredClone(VALID);
223
- (m.api as any).tables = ["note", "ghost"];
223
+ (m.api as any).tables = ["demo_solution_note", "ghost"];
224
224
  const v = checkTablesAgainstSchema(m, SCHEMA);
225
225
  expect(v[0]).toContain("C2-12a");
226
226
  expect(v[0]).toContain("ghost");
227
227
  });
228
228
  });
229
229
 
230
+ describe("checkSchemaTablePrefix(C2-12c:schema.yaml 表前缀)", () => {
231
+ const SCHEMA = readText("fixture-valid/api/src/demo-solution/schema.yaml");
232
+
233
+ it("fixture-valid 声明 prefix 且表名带前缀 → 零违规", () => {
234
+ expect(checkSchemaTablePrefix(SCHEMA)).toEqual([]);
235
+ });
236
+
237
+ it("允许自选短前缀(长模块名可用 crm_ 之类,不强制由模块名派生)", () => {
238
+ expect(checkSchemaTablePrefix("prefix: dm_\ntables:\n dm_note: {}\n")).toEqual([]);
239
+ });
240
+
241
+ it("缺 prefix → C2-12c", () => {
242
+ const v = checkSchemaTablePrefix("tables:\n demo_solution_note: {}\n");
243
+ expect(v[0]).toContain("C2-12c");
244
+ expect(v[0]).toContain("prefix");
245
+ });
246
+
247
+ it("prefix 不以 _ 结尾 → C2-12c 语法违规", () => {
248
+ const v = checkSchemaTablePrefix("prefix: demo_solution\ntables:\n demo_solution_note: {}\n");
249
+ expect(v[0]).toContain("C2-12c");
250
+ expect(v[0]).toContain("_ 结尾");
251
+ });
252
+
253
+ it("prefix 超过 32 字符 → C2-12c(吃掉表名可用空间)", () => {
254
+ const long = `${"a".repeat(33)}_`;
255
+ const v = checkSchemaTablePrefix(`prefix: ${long}\ntables:\n ${long}note: {}\n`);
256
+ expect(v[0]).toContain("C2-12c");
257
+ expect(v[0]).toContain("超过上限 32");
258
+ });
259
+
260
+ it("表名未带前缀 → C2-12c 并给出改名建议", () => {
261
+ const v = checkSchemaTablePrefix("prefix: demo_solution_\ntables:\n note: {}\n");
262
+ expect(v[0]).toContain("C2-12c");
263
+ expect(v[0]).toContain("demo_solution_note");
264
+ });
265
+
266
+ it("表名总长边界:63 合法、64 拒绝(PostgreSQL 上限 63 字节)", () => {
267
+ const p = "ab_";
268
+ const name63 = `${p}${"c".repeat(60)}`; // 3 + 60 = 63
269
+ const name64 = `${p}${"c".repeat(61)}`; // 3 + 61 = 64
270
+ expect(name63.length).toBe(63);
271
+ expect(checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${name63}: {}\n`)).toEqual([]);
272
+ const v = checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${name64}: {}\n`);
273
+ expect(v[0]).toContain("C2-12c");
274
+ expect(v[0]).toContain("63 字符");
275
+ });
276
+
277
+ it("引用式方案(api.tables 缺省)也做前缀检查", () => {
278
+ const m = structuredClone(VALID);
279
+ delete (m.api as any).tables;
280
+ const v = checkTablesAgainstSchema(m, "tables:\n note: {}\n");
281
+ expect(v[0]).toContain("C2-12c");
282
+ });
283
+
284
+ it("prefix 落在第一方保留段(oj_ / ojm_ 开头)→ C2-12c,整段禁用含派生形式", () => {
285
+ for (const p of ["oj_", "ojm_", "oj_crm_", "ojm_tools_"]) {
286
+ const v = checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${p}note: {}\n`);
287
+ expect(v.some(x => x.includes("C2-12c") && x.includes("保留段"))).toBe(true);
288
+ }
289
+ });
290
+
291
+ it("自有前缀不落在保留段 → 零违规(保留段检查的对照)", () => {
292
+ expect(checkSchemaTablePrefix("prefix: crm_\ntables:\n crm_note: {}\n")).toEqual([]);
293
+ });
294
+
295
+ it("_oj_ 因下划线开头只报语法违规,不重复报保留段", () => {
296
+ const v = checkSchemaTablePrefix("prefix: _oj_\ntables:\n _oj_note: {}\n");
297
+ expect(v[0]).toContain("必须匹配");
298
+ expect(v.every(x => !x.includes("保留段"))).toBe(true);
299
+ });
300
+ });
301
+
230
302
  describe("checkLocalesComplete(C2-25:构建期缺 key fail-fast)", () => {
231
303
  const ZH = readText("fixture-valid/web/src/demo-solution/locales/zh-CN.json");
232
304
  const EN = readText("fixture-valid/web/src/demo-solution/locales/en-US.json");
@@ -37,11 +37,11 @@ describe.skipIf(!smokeReady)("真机冒烟(需 OJM_SMOKE_URL/OJM_SMOKE_USER/OJ
37
37
  const apiDist = join(project, "dist/api/demo-solution-0.1.0");
38
38
  const { mkdirSync, writeFileSync } = await import("node:fs");
39
39
  mkdirSync(join(apiDist, "ping"), { recursive: true });
40
- // 迁移 SQL 随夹具 src 真源(0001__init.sql 建 note 表);B-11 硬验收:appliedSeq > 0 可断言
40
+ // 迁移 SQL 随夹具 src 真源(0001__init.sql 建 demo_solution_note 表);B-11 硬验收:appliedSeq > 0 可断言
41
41
  cpSync(join(FIXTURE_VALID, "api/src/demo-solution/migrations"), join(apiDist, "migrations"), { recursive: true });
42
42
  // schema.yaml 同随夹具真源(列集与迁移 SQL 一致):oj migrate 在 apply 后跑 schema
43
43
  // reconcile(migrate_cmd.rs migrate_one),声明缺列而实库多列 = Err——合成空列集会炸
44
- writeFileSync(join(apiDist, "manifest.yaml"), "name: demo-solution\ndesc: manifest.yaml 契约全字段合法夹具\nversion: 0.1.0\ntables:\n - note\n");
44
+ writeFileSync(join(apiDist, "manifest.yaml"), "name: demo-solution\ndesc: manifest.yaml 契约全字段合法夹具\nversion: 0.1.0\ntables:\n - demo_solution_note\n");
45
45
  cpSync(join(FIXTURE_VALID, "api/src/demo-solution/schema.yaml"), join(apiDist, "schema.yaml"));
46
46
  writeFileSync(join(apiDist, "routes.js"), "export {};\n");
47
47
  writeFileSync(join(apiDist, "ping/api.js"), "export async function ping() {}\n");
@@ -297,8 +297,8 @@ export function validateContract(m: unknown): string[] {
297
297
  }
298
298
  if (api.tables !== undefined) {
299
299
  const t = api.tables;
300
- if (!Array.isArray(t) || !t.every(x => typeof x === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(x))) {
301
- v0.push(v("C2-12a", "api.tables 每项必须匹配 ^[a-z][a-z0-9_]{0,63}$ 的字符串(与 api/src/<name>/schema.yaml 表集逐一相等另行对照)"));
300
+ if (!Array.isArray(t) || !t.every(x => typeof x === "string" && /^[a-z][a-z0-9_]{0,62}$/.test(x))) {
301
+ v0.push(v("C2-12a", "api.tables 每项必须匹配 ^[a-z][a-z0-9_]{0,62}$ 的字符串(≤63 字符:PostgreSQL 标识符上限 63 字节、MySQL 64 字符,取最严值;与 api/src/<name>/schema.yaml 表集逐一相等另行对照)"));
302
302
  }
303
303
  }
304
304
  if (api.deps !== undefined) {
@@ -682,30 +682,113 @@ export function checkLocalesComplete(m: unknown, zhText: string, enText: string)
682
682
  return out;
683
683
  }
684
684
 
685
+ /** C2-12c prefix 语法:小写字母开头,仅小写字母/数字/下划线,且以 `_` 结尾 */
686
+ const TABLE_PREFIX_RE = /^[a-z][a-z0-9_]*_$/;
687
+
688
+ /**
689
+ * 第一方保留的表前缀段:oj 引擎 / ojm CLI 的命名空间。
690
+ *
691
+ * 框架表(_platform / auth / home / notification 下的 users / sys_* / wecom_* 等)保持裸名,
692
+ * 是保留的框架命名空间;方案表名一律带作者自选前缀,与框架表名天然不重叠。把 `oj_` / `ojm_`
693
+ * 整段留给第一方,防止方案先占用、日后框架想用时反被占(S002 归属图以表名为全局主键)。
694
+ * 按「段」保留(startsWith),不只禁精确值——整个命名空间都归第一方。
695
+ * 注:下划线开头的 `_oj_`(oj 内建账本 `_oj_migrations`)本就不满足 TABLE_PREFIX_RE,无需列举。
696
+ */
697
+ const RESERVED_TABLE_PREFIX_SEGMENTS = ["oj_", "ojm_"];
698
+
699
+ /**
700
+ * 跨库可移植的表名上限:PostgreSQL 标识符上限 63 **字节**(NAMEDATALEN-1),
701
+ * MySQL 是 64 字符、SQLite 无实际限制——取最严的 63。表名字符集限定 ASCII,
702
+ * 字符数 = 字节数,因此 63 字符在三种库上都安全。
703
+ */
704
+ const MAX_TABLE_NAME_LEN = 63;
705
+
706
+ /** 前缀上限:给表名留 ≥31 字符(63 - 32),避免前缀把表名的可用空间吃光 */
707
+ const MAX_PREFIX_LEN = 32;
708
+
709
+ /**
710
+ * C2-12c `schema.yaml` 的 `prefix` 与逐表前缀校验。
711
+ *
712
+ * 表名在**全库共享**(oj 归属图以表名为主键),前缀把模块归属编码进名字,
713
+ * 让两个独立发布的方案不会因为都叫 `note` 而在安装期 S002 拒启。
714
+ * 前缀由作者选择(短、可读、尽量全局唯一),不强制由模块名派生——长模块名
715
+ * 可以用 `crm_` 之类短前缀,避免表名被模块名拖到数据库上限之外。
716
+ *
717
+ * 入参为 schema.yaml 文本(fs 在调用方)。YAML 解析失败返回 []——那是 C2-12a
718
+ * 的判罚面,同一事故不重复报两遍。
719
+ */
720
+ export function checkSchemaTablePrefix(schemaYamlText: string): string[] {
721
+ let doc: unknown;
722
+ try {
723
+ doc = parse(schemaYamlText);
724
+ }
725
+ catch {
726
+ return [];
727
+ }
728
+ if (!isObj(doc) || !isObj(doc.tables))
729
+ return []; // 缺 tables / 非映射由 C2-12a 报
730
+
731
+ const keys = Object.keys(doc.tables);
732
+ const out: string[] = [];
733
+ const prefix = doc.prefix;
734
+
735
+ if (prefix === undefined) {
736
+ out.push(v("C2-12c", "schema.yaml 缺 prefix:必须声明表前缀(如 prefix: crm_;小写字母开头、以 _ 结尾、≤32 字符,建议与方案名相关且全局唯一);表名在全库共享,前缀把归属编码进名字,避免方案之间撞表"));
737
+ }
738
+ else if (typeof prefix !== "string" || !TABLE_PREFIX_RE.test(prefix)) {
739
+ out.push(v("C2-12c", `schema.yaml 的 prefix「${String(prefix)}」必须匹配 ^[a-z][a-z0-9_]*_$(小写字母开头、仅小写字母/数字/下划线、以 _ 结尾)`));
740
+ }
741
+ if (typeof prefix === "string" && prefix.length > MAX_PREFIX_LEN) {
742
+ out.push(v("C2-12c", `schema.yaml 的 prefix「${prefix}」长 ${prefix.length} 字符,超过上限 ${MAX_PREFIX_LEN}——表名总长须 ≤${MAX_TABLE_NAME_LEN},前缀过长会吃掉表名可用空间;请改用更短的方案前缀(如 crm_)`));
743
+ }
744
+ if (typeof prefix === "string" && TABLE_PREFIX_RE.test(prefix)) {
745
+ const seg = RESERVED_TABLE_PREFIX_SEGMENTS.find(s => prefix.startsWith(s));
746
+ if (seg !== undefined) {
747
+ out.push(v("C2-12c", `schema.yaml 的 prefix「${prefix}」落在第一方保留段「${seg}*」——oj 引擎 / ojm CLI 的表命名空间由框架独占,方案不得占用,请改用自有前缀(如 crm_、acme_)`));
748
+ }
749
+ }
750
+
751
+ if (typeof prefix === "string" && prefix !== "") {
752
+ const offenders = keys.filter(k => !k.startsWith(prefix));
753
+ if (offenders.length > 0) {
754
+ out.push(v("C2-12c", `表名必须以本方案前缀「${prefix}」开头:${offenders.map(k => `「${k}」→「${prefix}${k}」`).join("、")}`));
755
+ }
756
+ }
757
+ // 表名语法/总长(引用式方案没有 api.tables 语法列兜底,这里一并查)
758
+ const malformed = keys.filter(k => !/^[a-z][a-z0-9_]{0,62}$/.test(k));
759
+ if (malformed.length > 0) {
760
+ out.push(v("C2-12c", `schema.yaml 表名必须匹配 ^[a-z][a-z0-9_]{0,62}$(小写字母开头,仅小写字母/数字/下划线,≤${MAX_TABLE_NAME_LEN} 字符——PostgreSQL 上限 63 字节、MySQL 64 字符,取最严值):${malformed.join("、")}`));
761
+ }
762
+ return out;
763
+ }
764
+
685
765
  /**
686
- * C2-12a api.tables 与 `api/src/<name>/schema.yaml` 表集逐一相等(oj 冗余校验同源,不一致 fail-fast)。
766
+ * C2-12a api.tables 与 `api/src/<name>/schema.yaml` 表集逐一相等(oj 冗余校验同源,不一致 fail-fast);
767
+ * 同时执行 C2-12c 表前缀校验——引用式方案(api.tables 缺省)只要 schema.yaml 存在也必须带前缀。
687
768
  * 入参为 schema.yaml 文本(fs 在调用方)。
688
769
  */
689
770
  export function checkTablesAgainstSchema(m: unknown, schemaYamlText: string): string[] {
771
+ const prefixViolations = checkSchemaTablePrefix(schemaYamlText);
772
+
690
773
  if (!isObj(m) || !isObj(m.api) || m.api.tables === undefined)
691
- return [];
774
+ return prefixViolations;
692
775
  const declared = m.api.tables;
693
776
  if (!Array.isArray(declared))
694
- return []; // 语法违规归 validateContract(C2-12a 语法列),此处只对照表集
777
+ return prefixViolations; // 语法违规归 validateContract(C2-12a 语法列),此处只对照表集
695
778
  let schemaTables: unknown;
696
779
  try {
697
780
  const doc = parse(schemaYamlText);
698
781
  schemaTables = isObj(doc) ? doc.tables : undefined;
699
782
  }
700
783
  catch {
701
- return [v("C2-12a", "schema.yaml 不是合法 YAML,无法对照 api.tables 表集")];
784
+ return [...prefixViolations, v("C2-12a", "schema.yaml 不是合法 YAML,无法对照 api.tables 表集")];
702
785
  }
703
786
  if (!isObj(schemaTables)) {
704
- return [v("C2-12a", "schema.yaml 缺 tables 映射,无法对照 api.tables 表集(oj 冗余校验同源)")];
787
+ return [...prefixViolations, v("C2-12a", "schema.yaml 缺 tables 映射,无法对照 api.tables 表集(oj 冗余校验同源)")];
705
788
  }
706
789
  const schemaKeys = new Set(Object.keys(schemaTables));
707
790
  const declaredKeys = new Set(declared.filter(x => typeof x === "string"));
708
- const out: string[] = [];
791
+ const out: string[] = [...prefixViolations];
709
792
  const missingInSchema = [...declaredKeys].filter(k => !schemaKeys.has(k));
710
793
  const undeclaredInManifest = [...schemaKeys].filter(k => !declaredKeys.has(k));
711
794
  if (missingInSchema.length > 0) {
@@ -69,6 +69,14 @@ export interface IsolatedSite {
69
69
  cleanup: () => Promise<void>
70
70
  /** 销毁清单(--keep 时给人看这里到底有什么) */
71
71
  teardownReport: () => string[]
72
+ /**
73
+ * 重启隔离站点 oj serve(停当前进程,按同一派生 config 重新起站)。
74
+ * 用途:无守护形态安装会落 partial(vars/auth 等冻结段热改属 restart_pending,需进程
75
+ * 重启才激活)。walkthrough 在每次安装后置重启,模拟真实「部署 → 重启」时序,让模块真正
76
+ * 上线(路由/匿名端点注册),使后续深检/升级/重装断言落到活跃态而非空洞的 partial。
77
+ * token 仍有效(jwt_secret 落在 config 文件,重启不变);DB 在同根,会话延续。
78
+ */
79
+ restart: () => Promise<void>
72
80
  }
73
81
 
74
82
  export interface BootstrapOptions {
@@ -178,8 +186,13 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
178
186
  // 隔离站点直接预置被测方案的探活路径——与 scripts/verify-solution-smoke/bootstrap-host.mjs 同手法,
179
187
  // 让 trial 验的是安装机制本身,而不是重启时序。
180
188
  const healthPath = `/${parseFixtureSpec(opts.fixture).name}/ping`;
181
- if (!cfg.includes(`- ${healthPath}`))
182
- cfg = cfg.replace(/^(\s*)- \/system\/login-methods$/m, `$1- /system/login-methods\n$1- ${healthPath}`);
189
+ // 锚点行可能带行内注释与缩进(模板 config.yaml 的 /system/login-methods 即如此),
190
+ // 故用 lookahead 容忍尾注释;命中第一个 anonymous_paths 列表项(auth 守卫读取的那个,
191
+ // 与安装器 appendAnonymousPaths 同目标)即可——旧写法用 `$` 硬锚尾,注释使其失配导致预置
192
+ // 静默失效,安装器随后自己补匿名路径 → auth 进 restart_pending → 无守护形态判 partial。
193
+ const loginAnchor = /^(\s*)- \/system\/login-methods(?=\s*(?:#.*)?$)/m;
194
+ if (loginAnchor.test(cfg) && !cfg.includes(`- ${healthPath}`))
195
+ cfg = cfg.replace(loginAnchor, `$1- /system/login-methods\n$1- ${healthPath}`);
183
196
  // fs 段:模板自 0.2.13 起已自带(安装器落盘面);旧模板没有时才补,避免重复键
184
197
  if (!/^fs:/m.test(cfg))
185
198
  cfg = `${cfg.trimEnd()}\nfs:\n root: .\n readonly: false\n`;
@@ -237,6 +250,16 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
237
250
  process.on("SIGINT", onSignal);
238
251
  process.on("SIGTERM", onSignal);
239
252
 
253
+ // 重启:停当前进程后按同一派生 config 重新起站(激活冻结段);cleanup 持有的是本作用域的
254
+ // `oj` 变量引用,故重启后 cleanup 停的是新进程(闭包捕获变量,非值)。
255
+ const restart = async () => {
256
+ log("[ojm] 隔离站点重启 oj serve(激活冻结段 auth/vars)…");
257
+ await oj.stop();
258
+ oj = starter(configPath, "/api", resolveLayout(root).apiDist);
259
+ await oj.ready;
260
+ log(`[ojm] 隔离站点重启就绪:http://127.0.0.1:${port}(根 ${root})`);
261
+ };
262
+
240
263
  return {
241
264
  root,
242
265
  port,
@@ -249,6 +272,7 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
249
272
  admin: { user: SEED_ADMIN_USER, pass: SEED_ADMIN_PASS },
250
273
  cleanup,
251
274
  teardownReport: () => teardownReport(root, port),
275
+ restart,
252
276
  };
253
277
  }
254
278
 
@@ -109,6 +109,7 @@ export async function marketCommand(sub: string, argv: string[]): Promise<void>
109
109
  key: flags.key,
110
110
  check: flags.check,
111
111
  registryId: flags.registryId,
112
+ rebuildIndex: flags.rebuildIndex === true,
112
113
  });
113
114
  if (flags.rebuildIndex === true)
114
115
  await buildIndex({ id: flags.id, key: flags.key, check: flags.check });
@@ -38,6 +38,8 @@ export interface MarketPublishOptions {
38
38
  check?: boolean
39
39
  /** 签发方 registry 身份(REG-CS3 v2 防跨仓库重放;透传 signCommand --registry-id) */
40
40
  registryId?: string
41
+ /** 命令层是否紧接着重建索引(仅用于措辞:true 时不打印「索引未重建」) */
42
+ rebuildIndex?: boolean
41
43
  }
42
44
 
43
45
  export interface MarketPublishResult {
@@ -88,7 +90,9 @@ export async function publishPackage(opts: MarketPublishOptions = {}): Promise<M
88
90
  const size = fs.statSync(tgzPath).size;
89
91
  console.log(`[ojm] 包已落 CDN → ${path.relative(projectRoot, tgzPath)}(${size} 字节)`);
90
92
  console.log(`[ojm] 发布方 ${signed.keyId};指纹 ${signed.fingerprint}`);
91
- console.log("[ojm] 索引未重建:多人场景请由维护者跑 ojm market build-index");
93
+ // 带了 --rebuild-index 时由命令层紧接着重建索引,这里不能再报「未重建」(否则前后两句自相矛盾)
94
+ if (opts.rebuildIndex !== true)
95
+ console.log("[ojm] 索引未重建:多人场景请由维护者跑 ojm market build-index");
92
96
 
93
97
  return {
94
98
  name,
@@ -70,6 +70,17 @@ export interface WalkthroughResult {
70
70
  port: number
71
71
  }
72
72
 
73
+ /**
74
+ * 安装成功终态:oj 0.1.122 起,无守护形态安装会落 partial(vars/auth 等冻结段热改属
75
+ * restart_pending,需进程重启才完全生效)——这是正确终态(P0 §3.6 / 验收 15),不是失败。
76
+ * 真正的失败 partial(锁后异常 + 补偿回滚)不会带 restartRequired,故以它区分二者。
77
+ * 本 walkthrough 在每次安装后置重启(IsolatedSite.restart),模拟真实「部署 → 重启」时序,
78
+ * 让模块真正上线。
79
+ */
80
+ function installSucceeded(task: TaskRecord): boolean {
81
+ return task.status === "done" || (task.status === "partial" && task.restartRequired === true);
82
+ }
83
+
73
84
  /**
74
85
  * neg-* 专用版本:与主链路(0.1.0/0.2.0)完全隔离,收尾整包摘除。
75
86
  * 必须低于主链路 latest(0.2.0),否则发布后它会成为 latest,摘除时 REG-CS2 单调性守卫
@@ -96,8 +107,8 @@ const OPS: Op[] = [
96
107
  title: "安装(11 段)",
97
108
  run: async (ctx) => {
98
109
  const task = await install(ctx, ctx.version);
99
- const done = task.status === "done";
100
- return { ok: done, detail: done ? `${task.stages.length} 段全 ok` : `status=${task.status} stage=${task.stage ?? "?"}:${task.message}` };
110
+ const ok = installSucceeded(task);
111
+ return { ok, detail: ok ? `${task.stages.length} 段全 ok(终态 ${task.status}${task.restartRequired ? " + restartRequired" : ""},已重启激活)` : `status=${task.status} stage=${task.stage ?? "?"}:${task.message}` };
101
112
  },
102
113
  },
103
114
  {
@@ -106,7 +117,7 @@ const OPS: Op[] = [
106
117
  run: async (ctx) => {
107
118
  await ensureInstalled(ctx);
108
119
  const task = await install(ctx, ctx.version);
109
- const ok = task.status === "done";
120
+ const ok = installSucceeded(task);
110
121
  return { ok, detail: ok ? "重装成功(未报方案名占用)" : `status=${task.status}:${task.message}` };
111
122
  },
112
123
  },
@@ -159,7 +170,7 @@ const OPS: Op[] = [
159
170
  await ensureInstalled(ctx); // 前置:0.1.0 在列(账本 head=1)
160
171
  await publishVersion(ctx, "0.2.0");
161
172
  const task = await install(ctx, "0.2.0");
162
- if (task.status !== "done")
173
+ if (!installSucceeded(task))
163
174
  return { ok: false, detail: `升级安装未 done:status=${task.status} stage=${task.stage ?? "?"}:${task.message}` };
164
175
  const card = await indexCard(ctx);
165
176
  const distDir = resolveLayout(ctx.site.root).apiDist;
@@ -189,7 +200,7 @@ const OPS: Op[] = [
189
200
  if (card?.installed !== "0.2.0") {
190
201
  await publishVersion(ctx, "0.2.0");
191
202
  const pre = await install(ctx, "0.2.0");
192
- if (pre.status !== "done")
203
+ if (!installSucceeded(pre))
193
204
  return { ok: false, detail: `前置安装 0.2.0 未 done:status=${pre.status}:${pre.message}` };
194
205
  card = await indexCard(ctx);
195
206
  }
@@ -320,7 +331,7 @@ const OPS: Op[] = [
320
331
  return { ok: false, detail: `未停 needs_trust:status=${first.status}:${first.message}` };
321
332
  await callApi(fetch, ctx.base, ctx.token, "POST", "/api/_platform/market/trust", { task_id: first.id });
322
333
  const second = await installOnce(ctx, NEG_VERSION, NEG_NAME);
323
- if (second.status !== "done")
334
+ if (!installSucceeded(second))
324
335
  return { ok: false, detail: `确认后重装未 done:status=${second.status}:${second.message}` };
325
336
  // 收尾(F3):显式 revoke 陌生 key,快照 diff 断言无残留——不靠 uninstall 兜底(它本就不摘信任)
326
337
  const del = await callApi<{ revoked?: boolean }>(fetch, ctx.base, ctx.token, "DELETE", `/api/_platform/market/trust/key?key_id=${encodeURIComponent("stranger.publisher")}`);
@@ -417,13 +428,18 @@ export async function walkthrough(opts: WalkthroughOptions = {}): Promise<Walkth
417
428
  }
418
429
  }
419
430
 
420
- /** 安装 + 轮询;needs_trust 时自动确认(隔离站点,无外部信任可言) */
431
+ /**
432
+ * 安装 + 轮询;needs_trust 时自动确认(隔离站点,无外部信任可言)。
433
+ * 成功(done 或 restartRequired 的 partial)后置重启,激活冻结段,模拟真实部署时序。
434
+ */
421
435
  async function install(ctx: Ctx, version: string): Promise<TaskRecord> {
422
436
  let task = await installOnce(ctx, version);
423
437
  if (task.status === "needs_trust") {
424
438
  await callApi(fetch, ctx.base, ctx.token, "POST", "/api/_platform/market/trust", { task_id: task.id });
425
439
  task = await installOnce(ctx, version);
426
440
  }
441
+ if (installSucceeded(task))
442
+ await ctx.site.restart();
427
443
  return task;
428
444
  }
429
445
 
@@ -462,7 +478,7 @@ async function ensureInstalled(ctx: Ctx): Promise<void> {
462
478
  if (card?.installed === ctx.version)
463
479
  return;
464
480
  const task = await install(ctx, ctx.version);
465
- if (task.status !== "done")
481
+ if (!installSucceeded(task))
466
482
  throw new Error(`前置安装失败:status=${task.status}:${task.message}`);
467
483
  }
468
484
 
@@ -1,3 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+
1
3
  /**
2
4
  * 共享依赖表 —— 单一常量源(设计文档 §4.3,解决 B11 / B12)。
3
5
  *
@@ -21,15 +23,18 @@ export interface SharedDepEntry {
21
23
  /** 硬共享:模块不得自带 */
22
24
  hard: boolean
23
25
  /**
24
- * 软共享但「不进宿主首屏」:宿主 shell 不预构建该资产、importmap 不列该键,
25
- * 模块若 import 它则自行打包进模块产物(设计文档 §4.3 的「软共享缺席→模块自带
26
- * 副本兜底」语义)。用于**仅模块使用、宿主与共享 chunk 永不 import**的重型依赖
27
- * (编辑器 / 图表 / Markdown / 拖拽 …),避免把它们 5~6MB 的体量无谓铺进
28
- * shell-dist 并要求公开链路多传一堆文件。宿主侧有真实 import 的共享依赖
29
- * (antd / pro-components / icons 内部依赖的 motion / react-jss / dayjs /
30
- * zustand 等)绝不能标 bundle —— 否则会被 antd 等共享 chunk 经 importmap 解析
31
- * 不到而 404。判定依据:宿主 host.tsx + runtime.js 的传递 import 图,以及
32
- * antd/pro-components/icons 的传递依赖图,都不含这些说明符。
26
+ * 模块自打包:宿主 shell 不预构建该资产、importmap 不列该键,模块若 import 它
27
+ * 则自行打包进模块产物(设计文档 §4.3 的「软共享缺席→模块自带副本兜底」语义)。
28
+ * 用于**仅模块使用、宿主与共享 chunk 永不 import**的重型依赖(编辑器 / 图表 /
29
+ * Markdown),避免把它们 5~6MB 的体量无谓铺进 shell-dist 并要求公开链路多传
30
+ * 一堆文件。宿主侧有真实 import 的共享依赖(antd / pro-components / icons 内部
31
+ * 依赖的 motion / react-jss / dayjs / zustand / @dnd-kit 等)绝不能标 bundle ——
32
+ * 否则会被 antd 等共享 chunk 经 importmap 解析不到而 404。判定依据:宿主
33
+ * host.tsx + runtime.js 的传递 import 图,以及 antd/pro-components/icons 的
34
+ * 传递依赖图,都不含这些说明符。
35
+ *
36
+ * ⚠️ 本档位**不计入宿主版本一致性保障**:模块把副本打进产物,多副本不会报错,
37
+ * 只会重复体积。体积怎么控,见 docs/devkit/manual.md §2.6。
33
38
  */
34
39
  bundle?: boolean
35
40
  }
@@ -66,9 +71,10 @@ export const SHARED_DEPS: SharedDepEntry[] = [
66
71
  * - 无单例语义。kit 必须唯一是因为多副本 = 两份 antd 上下文树;编辑器资产
67
72
  * 把 @tiptap/*、@plane/ui 等**全部内联**在自身里,跨边界穿过的只有 React
68
73
  * 家族(那是独立的硬共享条目),多一份编辑器副本不会拆散任何 context。
69
- * - 不是每个模块都用编辑器。软共享的语义正是「默认由宿主提供,允许 importmap
70
- * scopes 多版本共存兜底(C7)」——缺席时模块可自带副本而不硬失败。
71
- * 代价:多副本会重复约 5.4 MB 资产,故 scopes 只作兜底、默认仍走宿主。
74
+ * - 不是每个模块都用编辑器。软共享的语义是「允许缺席」——宿主**不预构建**
75
+ * 这份资产(bundle: true,不进 shell-dist、不进 importmap),模块 import 它
76
+ * 就自己打进产物。代价是每个用编辑器的模块各带一份约 5.4 MB,换来宿主
77
+ * 分发体积不被没人用的重型依赖撑大。
72
78
  */
73
79
  { specifier: "@plane/editor", asset: "editor", hard: false, bundle: true },
74
80
  // 注:@rc-component/form(antd 6 Form 底层)曾尝试单例化以修 my-profile
@@ -120,6 +126,9 @@ export const SHARED_DEPS: SharedDepEntry[] = [
120
126
  { specifier: "dayjs/plugin/weekday", asset: "dayjs-plugin-weekday", hard: false },
121
127
  { specifier: "dayjs/plugin/weekOfYear", asset: "dayjs-plugin-weekOfYear", hard: false },
122
128
  { specifier: "dayjs/plugin/weekYear", asset: "dayjs-plugin-weekYear", hard: false },
129
+ // echarts 家族:模块自打包。整包 index(`import * as echarts from "echarts"`)会让
130
+ // 单个模块产物从 ~180KB 涨到 1.7MB(实测 echarts 918KB + zrender 221KB 占 87%),
131
+ // 故脚手架改用 `echarts/core` 按需注册;接入方写法见 docs/devkit/manual.md §2.6。
123
132
  { specifier: "echarts", asset: "echarts", hard: false, bundle: true },
124
133
  { specifier: "echarts/charts", asset: "echarts-charts", hard: false, bundle: true },
125
134
  { specifier: "echarts/features.js", asset: "echarts-features", hard: false, bundle: true },
@@ -147,18 +156,21 @@ export const SHARED_DEPS: SharedDepEntry[] = [
147
156
  { specifier: "tailwind-merge", asset: "tailwind-merge", hard: false },
148
157
  /**
149
158
  * Markdown 编辑器(Vditor v4,M5 回升自下游 oj-wiki):npm 包是 webpack **UMD**
150
- * 单产物(main=dist/index.js,无 ESM 出口、仅 default 导出),走 starShim + esbuild
151
- * 的既有 CJS/UMD interop 链路打包成 ESM 资产(与 react 家族同路,无需新机制)。
159
+ * 单产物(main=dist/index.js,无 ESM 出口、仅 default 导出)。bundle: true 表示
160
+ * 宿主不预构建 —— CJS/UMD interop 交给模块自己的 rolldown 打包链路处理。
152
161
  *
153
- * 软共享:无单例语义(多副本只是重复体积,不拆 context),且不是每个模块都用。
154
- * 体积大(gzip ≈700 KiB),故**惰性生效**:importmap 只做映射,宿主 shell 自身
155
- * 不 import vditor ⇒ /assets/vditor.js 仅在模块产物真正 import 它时才被拉取,
156
- * 不进宿主首屏。
162
+ * 为什么不预构建:体积大(gzip ≈700 KiB)且不是每个模块都用。宿主为它留一份
163
+ * /assets/vditor.js,等于给所有站点无条件加 700 KiB 分发体积,而只有编辑器页
164
+ * 才用得上。改由模块自带后,代价转给「真正 import 它的那个模块」。
165
+ *
166
+ * 副作用(写文档时容易说错):模块产物是单文件(codeSplitting: false),
167
+ * 所以 `import("vditor")` 会被内联回去,**动态导入并不能把它从首屏摘掉**;
168
+ * 要省这笔体积只能把编辑器页拆成独立模块/方案。
157
169
  *
158
170
  * 深路径只登记 CSS 主样式(nprogress 同款样式垫片)。vditor 其余 dist 深路径
159
171
  * (lute / i18n / icons / 主题 CSS…)是**运行期经 cdn 选项按需拉取的资源**而非
160
172
  * import 说明符,由模块侧自行处理(自注入或指向宿主可服务的路径);若模块代码
161
- * 直接 import 它们,会命中前缀 external 但无 importmap 键 → 构建期深路径报错,
173
+ * 直接 import 它们,会命中前缀 external 但无共享表键 → 构建期深路径报错,
162
174
  * 属预期拦截(与 dayjs/plugin/* 同规则)。
163
175
  */
164
176
  { specifier: "vditor", asset: "vditor", hard: false, bundle: true },
@@ -182,6 +194,10 @@ export function generateShellEntries(): { name: string, pkg: string }[] {
182
194
  /**
183
195
  * 由共享表生成 importmap:说明符 → /assets/<asset>.js。
184
196
  * baseUrl 供宿主部署在子路径时使用。
197
+ *
198
+ * 注意:这里给出的是**逻辑名**。shell 构建落盘时会把每个资产改名成
199
+ * `<asset>.<hash>.js`(见 `hashedAssetFileName`),importmap 由构建脚本同步改写,
200
+ * 运行时拿到的仍是 `/assets/<asset>.<hash>.js`。
185
201
  */
186
202
  export function generateImportmap(baseUrl = ""): Record<string, string> {
187
203
  const map: Record<string, string> = {};
@@ -196,3 +212,37 @@ export function generateImportmap(baseUrl = ""): Record<string, string> {
196
212
  export function isPrebuiltDep(id: string): boolean {
197
213
  return SHARED_DEPS.some(dep => !dep.bundle && (id === dep.specifier || id.startsWith(`${dep.specifier}/`)));
198
214
  }
215
+
216
+ /**
217
+ * 共享资产的内容哈希段:10 位 base36,且**保证**同时含数字与字母。
218
+ *
219
+ * 为什么必须保证:oj serve ≥ 0.1.116 的默认缓存策略按**文件名**判定
220
+ * (`serve/src/static_files.rs::default_cache_control`)——文件名 stem 按 `-` `_` `.`
221
+ * 切段,末段 ≥8 位且同时含数字与字母才发 `public, max-age=31536000, immutable`,
222
+ * 其余一律 `no-cache`。纯 base36 有十万分之一量级会全数字(vite 的 base64url 更糟:
223
+ * 8 位全字母约 19%),漏判就白丢一年缓存。这里按「缺哪类补哪类」定长补齐,
224
+ * 让每个资产 100% 命中长缓存,而不是赌概率。
225
+ *
226
+ * 9 位 base36 ≈ 46.5 bit,65 个资产量级碰撞概率可忽略。
227
+ */
228
+ export function assetHash(content: string | Uint8Array): string {
229
+ const digest = createHash("sha256").update(content).digest();
230
+ const body = digest.readBigUInt64BE(0).toString(36).slice(0, 9).padStart(9, "0");
231
+ // 已有字母 → 补数字;全数字 → 补字母。两类必在其一,补齐另一类即可。
232
+ return /[a-z]/.test(body) ? `${body}0` : `${body}z`;
233
+ }
234
+
235
+ /** 内容寻址文件名:`<base>.<hash>.js`(js 与 map 同为 .js 后缀,map 保持原名不动) */
236
+ export function hashedAssetFileName(base: string, content: string | Uint8Array): string {
237
+ return `${base}.${assetHash(content)}.js`;
238
+ }
239
+
240
+ /**
241
+ * 与 oj serve 的 `default_cache_control` 同口径:末段 ≥8 位且数字字母都有 → 可发 immutable。
242
+ * 构建期用它断言产物名,避免哪天哈希算法换掉后长缓存静默失效。
243
+ */
244
+ export function isImmutableAssetFileName(fileName: string): boolean {
245
+ const stem = fileName.replace(/\.[^.]*$/, "");
246
+ const seg = stem.split(/[-_.]/).pop() ?? "";
247
+ return seg.length >= 8 && /\d/.test(seg) && /[a-z]/i.test(seg);
248
+ }
package/src/site-base.ts CHANGED
@@ -102,6 +102,10 @@ export function rewriteIndexHtml(html: string, siteBase: string | undefined, api
102
102
  // 上一步已处理,二者不重叠。
103
103
  out = out.replace(/(\b(?:href|src|data-ojm-entry)=")(\/[^/"][^"]*|\/")/g, (_m, attr: string, url: string) =>
104
104
  `${attr}${withSiteBase(url, base)}`);
105
+ // data-ojm-preload 是**逗号分隔的 URL 列表**(构建期算好的首屏静态闭包),
106
+ // 上面那条单值正则匹配不到列表,漏改会让子路径部署下预取全 404(退化回瀑布,不白屏但慢)
107
+ out = out.replace(/(\bdata-ojm-preload=")([^"]*)"/g, (_m, attr: string, list: string) =>
108
+ `${attr}${list.split(",").map(url => url.startsWith("/") ? withSiteBase(url, base) : url).join(",")}"`);
105
109
  globals.push(`window.__OJ_BASE__=${JSON.stringify(base)};`);
106
110
  }
107
111
  if (api) {
package/src/site.ts CHANGED
@@ -43,6 +43,8 @@ export interface TaskRecord {
43
43
  appliedSeq?: number
44
44
  startedAt: number
45
45
  finishedAt?: number
46
+ /** 无守护形态部分生效:冻结段(auth/vars)热改属 restart_pending,需宿主重启才完全生效 */
47
+ restartRequired?: boolean
46
48
  }
47
49
 
48
50
  /** 模板 serve 缺省端口(templates/config.yaml server.port: 9778;host 显式钉 127.0.0.1) */