@oj-module/cli 0.2.54 → 0.2.57

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 (307) hide show
  1. package/devkit/README.md +7 -4
  2. package/devkit/SKILL.md +7 -4
  3. package/devkit/manual.md +63 -54
  4. package/devkit/module-dev-and-cdn-playground.md +7 -0
  5. package/devkit/publish-walkthrough.md +199 -0
  6. package/package.json +2 -2
  7. package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
  8. package/shell-dist/assets/runtime.js +5 -5
  9. package/shell-dist/index.html +3 -3
  10. package/shell-dist/versions.json +3 -3
  11. package/src/__tests__/layout.test.ts +30 -1
  12. package/src/__tests__/templates-schema-columns.test.ts +99 -0
  13. package/src/__tests__/vendor-devkit.test.ts +87 -0
  14. package/src/alias-paths.ts +11 -12
  15. package/src/build/backend.ts +131 -0
  16. package/src/build/module-build.ts +364 -0
  17. package/src/build/module-def.ts +381 -0
  18. package/src/build/site-merge.ts +33 -0
  19. package/src/build.ts +16 -885
  20. package/src/builtin.ts +10 -3
  21. package/src/bundle.ts +61 -0
  22. package/src/cli.ts +4 -1
  23. package/src/contract/__tests__/emit-client.test.ts +4 -4
  24. package/src/contract/__tests__/market-client.integration.test.ts +2 -2
  25. package/src/contract/__tests__/run-api.test.ts +2 -2
  26. package/src/contract/check.ts +6 -2
  27. package/src/contract/emit-client.ts +10 -3
  28. package/src/contract/run.ts +10 -2
  29. package/src/init.ts +31 -46
  30. package/src/layout.ts +72 -16
  31. package/src/lifecycle/__tests__/bundle-summary.test.ts +106 -0
  32. package/src/lifecycle/__tests__/bundle.test.ts +6 -0
  33. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  34. package/src/market/__tests__/bootstrap.test.ts +1 -1
  35. package/src/site.ts +1 -1
  36. package/src/vendor.ts +34 -0
  37. package/templates/README.md +30 -20
  38. package/templates/init.include.yaml +19 -11
  39. package/templates/openapi.json +102 -86
  40. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +8 -3
  41. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +53 -0
  42. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +4 -1
  43. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +7 -3
  44. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
  45. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
  46. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
  47. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
  48. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
  49. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +3 -0
  50. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +3 -0
  51. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +8 -0
  52. package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +7 -0
  53. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
  54. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
  55. package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
  56. package/templates/src/{system/api → app/api/system}/roles/api.ts +2 -1
  57. package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +5 -2
  58. package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +5 -2
  59. package/templates/src/app/web/notification/pages/center/index.tsx +1 -1
  60. package/templates/src/app/web/notification/routes.ts +1 -1
  61. package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +1 -1
  62. package/templates/src/app/web/personal-center/routes.ts +1 -1
  63. package/templates/src/app/web/system/__client/api.schemas.ts +2 -0
  64. package/templates/src/app/web/system/locales/en-US.json +2 -3
  65. package/templates/src/app/web/system/locales/zh-CN.json +2 -3
  66. package/templates/src/app/web/system/pages/departments/index.tsx +2 -2
  67. package/templates/src/app/web/system/pages/login-methods/index.tsx +1 -1
  68. package/templates/src/app/web/system/pages/oplog/index.tsx +44 -19
  69. package/templates/src/app/web/system/pages/roles/index.tsx +12 -4
  70. package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
  71. package/templates/src/app/web/system/pages/settings/index.tsx +2 -2
  72. package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
  73. package/templates/src/app/web/system/pages/users/index.tsx +25 -17
  74. package/templates/src/app/web/system/pages/users/roles.tsx +1 -1
  75. package/templates/src/app/web/system/routes.ts +1 -1
  76. package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +5 -2
  77. package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
  78. package/templates/tests/auth-ldap.spec.ts +2 -2
  79. package/templates/tests/auth-session.spec.ts +3 -3
  80. package/templates/tests/auth-throttle.spec.ts +1 -1
  81. package/templates/tests/fixture.ts +6 -4
  82. package/templates/tests/guard.spec.ts +1 -1
  83. package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
  84. package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
  85. package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
  86. package/templates/tests/lifecycle/installer.spec.ts +10 -10
  87. package/templates/tests/lifecycle/market-api.spec.ts +14 -14
  88. package/templates/tests/lifecycle/migrate.spec.ts +6 -6
  89. package/templates/tests/lifecycle/preflight.spec.ts +3 -3
  90. package/templates/tests/lifecycle/register.spec.ts +1 -1
  91. package/templates/tests/lifecycle/role-title-translate.spec.ts +171 -0
  92. package/templates/tests/lifecycle/semver.spec.ts +1 -1
  93. package/templates/tests/lifecycle/sha384.spec.ts +1 -1
  94. package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
  95. package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
  96. package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
  97. package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
  98. package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
  99. package/templates/tests/lifecycle/trust.spec.ts +5 -5
  100. package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
  101. package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
  102. package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
  103. package/templates/tests/org-departments.spec.ts +10 -6
  104. package/templates/tests/org-read.spec.ts +7 -7
  105. package/templates/tests/query-utils.spec.ts +1 -1
  106. package/templates/tests/rbac-audit.spec.ts +1 -1
  107. package/templates/tests/rbac-contacts.spec.ts +2 -2
  108. package/templates/tests/rbac-guard.spec.ts +1 -1
  109. package/templates/tests/rbac-roles.spec.ts +19 -2
  110. package/templates/tests/rbac-seed.spec.ts +1 -1
  111. package/templates/tests/rbac-settings.spec.ts +77 -7
  112. package/templates/tests/rbac-solution-perms.spec.ts +1 -1
  113. package/templates/tests/rbac-tables.spec.ts +29 -0
  114. package/templates/tests/rbac-users.spec.ts +55 -3
  115. package/templates/tests/roles-page.spec.ts +40 -0
  116. package/templates/tests/seed-role-accounts.spec.ts +1 -1
  117. package/templates/tests/tenant-access-cache.spec.ts +1 -1
  118. package/templates/tests/tenant-forgery.spec.ts +5 -5
  119. package/templates/tests/tenant-isolation.spec.ts +5 -4
  120. package/templates/tests/username-namespace.spec.ts +3 -3
  121. package/templates/tests/users-page.spec.ts +39 -0
  122. package/templates/tests/wecom-admin.spec.ts +6 -6
  123. package/templates/tests/wecom-client-timeout.spec.ts +1 -1
  124. package/templates/tests/wecom-connector.spec.ts +9 -9
  125. package/templates/tests/wecom-corp.spec.ts +9 -9
  126. package/templates/tests/wecom-crypt.spec.ts +1 -1
  127. package/templates/tests/wecom-endpoints.spec.ts +2 -2
  128. package/templates/tests/wecom-landing.spec.ts +1 -1
  129. package/templates/tests/wecom-org-sync.spec.ts +7 -7
  130. package/templates/tests/wecom-sync.spec.ts +3 -3
  131. package/templates/tsconfig.paths.json +11 -11
  132. package/templates/vitest.config.ts +2 -2
  133. package/templates/web.config.ts +5 -2
  134. package/vendor/host-versions.json +3 -3
  135. package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
  136. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
  137. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
  138. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
  139. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
  140. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
  141. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
  142. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
  143. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
  144. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
  145. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
  146. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
  147. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
  148. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
  149. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
  150. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
  151. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
  152. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
  153. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
  154. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
  155. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
  156. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
  157. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
  158. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
  159. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
  160. package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
  161. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
  162. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
  163. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
  164. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
  165. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
  166. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
  167. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
  168. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
  169. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
  170. package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
  171. package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +0 -0
  172. package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +0 -0
  173. package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
  174. package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
  175. package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
  176. package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
  177. package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
  178. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
  179. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
  180. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
  181. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  182. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
  183. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  184. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
  185. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
  186. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
  187. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
  188. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
  189. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
  190. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  191. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
  192. package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
  193. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  194. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  195. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
  196. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
  197. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
  198. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
  199. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  200. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
  201. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
  202. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  203. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
  204. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
  205. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
  206. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
  207. package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
  208. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
  209. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
  210. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
  211. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
  212. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
  213. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
  214. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
  215. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
  216. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
  217. package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
  218. package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
  219. package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
  220. package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
  221. package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
  222. package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
  223. package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
  224. package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
  225. package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
  226. package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
  227. package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +0 -0
  228. package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
  229. package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
  230. package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
  231. package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
  232. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
  233. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
  234. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
  235. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
  236. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
  237. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
  238. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
  239. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
  240. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
  241. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
  242. package/templates/src/{auth/api → app/api/auth}/api.ts +0 -0
  243. package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +0 -0
  244. package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
  245. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
  246. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
  247. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
  248. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
  249. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
  250. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
  251. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
  252. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
  253. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
  254. package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
  255. package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
  256. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
  257. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
  258. /package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +0 -0
  259. /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
  260. /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
  261. /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
  262. /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
  263. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
  264. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
  265. /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
  266. /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
  267. /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
  268. /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
  269. /package/templates/src/{notification/api → app/api/notification}/api.ts +0 -0
  270. /package/templates/src/{notification/api → app/api/notification}/del/api.ts +0 -0
  271. /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
  272. /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
  273. /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
  274. /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
  275. /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  276. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
  277. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
  278. /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
  279. /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
  280. /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
  281. /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
  282. /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
  283. /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
  284. /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
  285. /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
  286. /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
  287. /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
  288. /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
  289. /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
  290. /package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +0 -0
  291. /package/templates/src/{system/api → app/api/system}/settings/api.ts +0 -0
  292. /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
  293. /package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +0 -0
  294. /package/templates/src/{system/api → app/api/system}/users/password/api.ts +0 -0
  295. /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
  296. /package/templates/src/{system/api → app/api/system}/users/roles/api.ts +0 -0
  297. /package/templates/src/{web/api → app/api/web}/get-async-routes/api.ts +0 -0
  298. /package/templates/src/{web/api → app/api/web}/hello/api.ts +0 -0
  299. /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
  300. /package/templates/src/{web/api → app/api/web}/user-info/api.ts +0 -0
  301. /package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +0 -0
  302. /package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +0 -0
  303. /package/templates/src/app/web/home/__client/{api.ts → index.ts} +0 -0
  304. /package/templates/src/app/web/notification/__client/{api.ts → index.ts} +0 -0
  305. /package/templates/src/app/web/personal-center/__client/{api.ts → index.ts} +0 -0
  306. /package/templates/src/app/web/system/__client/{api.ts → index.ts} +0 -0
  307. /package/templates/src/{web → app/web}/web/__client/api.schemas.ts +0 -0
@@ -1,5 +1,5 @@
1
- import type { SolutionState, StateStore } from "../../src/_platform/api/_lifecycle/state";
2
- import type { InstallContext } from "../../src/_platform/api/_lifecycle/steps/preflight";
1
+ import type { SolutionState, StateStore } from "../../src/app/api/_platform/_lifecycle/state";
2
+ import type { InstallContext } from "../../src/app/api/_platform/_lifecycle/steps/preflight";
3
3
  // BDD:安装预检规则引擎(P1-5,R7/R8/R9/R24,十阶段状态机 stage 1)。
4
4
  // 规则真源:docs/prd/solution/202610171254-lifecycle-manifest-contract.md(C2-01~33 安装列);
5
5
  // 期望真源:packages/cli/src/lifecycle/__tests__/fixtures/(fixture-valid 零违规、fixture-invalid 七码齐报)。
@@ -12,7 +12,7 @@ import { fileURLToPath } from "node:url";
12
12
 
13
13
  import { describe, expect, it } from "vitest";
14
14
  import { parse } from "yaml";
15
- import { preflight } from "../../src/_platform/api/_lifecycle/steps/preflight";
15
+ import { preflight } from "../../src/app/api/_platform/_lifecycle/steps/preflight";
16
16
 
17
17
  const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "../../../src/lifecycle/__tests__/fixtures");
18
18
  const VALID = parse(readFileSync(join(FIXTURES, "fixture-valid/manifest.yaml"), "utf8")) as any;
@@ -8,7 +8,7 @@
8
8
  /* eslint-disable test/prefer-lowercase-title -- 任务/AC 编号是验收锚点,保留大写(DB/P1-5) */
9
9
  import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
10
10
 
11
- import { checkOccupancy, queryAllRegistered, queryRegistered, registerSolution } from "../../src/_platform/api/_lifecycle/steps/register";
11
+ import { checkOccupancy, queryAllRegistered, queryRegistered, registerSolution } from "../../src/app/api/_platform/_lifecycle/steps/register";
12
12
  import { installDb } from "../fixture";
13
13
 
14
14
  let dbo: ReturnType<typeof installDb>;
@@ -0,0 +1,171 @@
1
+ // BDD:solution 角色 title 落库时同步翻译(R1–R6,计划 docs/plans/20261103-0948-role-title-translate)。
2
+ // title = 解析文本(站点默认语言)、title_key = 原文(UI 实时翻译锚);解析失败回退裸 key,
3
+ // 绝不失败安装(R1);升级元数据覆盖 = 重解析(R3);迁移 0018 对存量 solution 角色回填、
4
+ // builtin 不动(R2/AC4);sys_permission 不变(R5);name/code/绑定不变(R6)。
5
+ /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写(register.spec 同约定) */
6
+ import { createRequire } from "node:module";
7
+ import { fileURLToPath } from "node:url";
8
+ import { afterEach, beforeAll, describe, expect, it } from "vitest";
9
+ import { registerSolution, resolveDeclTitles, siteLanguage } from "../../src/app/api/_platform/_lifecycle/steps/register";
10
+ import { installDb } from "../fixture";
11
+
12
+ const req = createRequire(import.meta.url);
13
+ const { DatabaseSync } = req("node:sqlite");
14
+ const { readFileSync } = req("node:fs");
15
+ const { dirname, join } = req("node:path");
16
+
17
+ const HERE = dirname(fileURLToPath(import.meta.url));
18
+ const enc = new TextEncoder();
19
+
20
+ let dbo: ReturnType<typeof installDb>;
21
+ const g = globalThis as any;
22
+
23
+ beforeAll(() => {
24
+ dbo = installDb();
25
+ g.http = {};
26
+ });
27
+
28
+ afterEach(() => {
29
+ g.http = {};
30
+ dbo.exec("DELETE FROM sys_role_permission");
31
+ dbo.exec("DELETE FROM sys_user_role");
32
+ dbo.exec("DELETE FROM sys_permission");
33
+ dbo.exec("DELETE FROM sys_role");
34
+ dbo.exec("DELETE FROM sys_solution_route");
35
+ dbo.exec("DELETE FROM sys_setting");
36
+ dbo.exec("DELETE FROM sys_oplog");
37
+ });
38
+
39
+ function roleRows() {
40
+ return dbo.prepare("SELECT name, title, title_key FROM sys_role ORDER BY name").all() as Array<{ name: string, title: string | null, title_key: string | null }>;
41
+ }
42
+
43
+ /** readFile 假面:包内 locales 按路径精确映射,未命中即「文件不存在」 */
44
+ function fakeReadFile(staging: string, files: Record<string, string>) {
45
+ return async (p: string) => {
46
+ const hit = files[p];
47
+ if (hit === undefined)
48
+ throw new Error(`ENOENT: ${p}`);
49
+ return enc.encode(hit);
50
+ };
51
+ }
52
+
53
+ const STAGING = "/staging";
54
+ const PM_LOCALES = {
55
+ [`${STAGING}/web/modules/pm/locales/zh-CN.json`]: JSON.stringify({ role: { sales: "销售", admin: "方案管理员" } }),
56
+ [`${STAGING}/web/modules/pm/locales/en-US.json`]: JSON.stringify({ role: { sales: "Sales", admin: "Solution Admin" } }),
57
+ };
58
+
59
+ function pmDecl(title: string) {
60
+ return {
61
+ module: "pm",
62
+ version: "1.0.0",
63
+ roles: [{ name: "pm.sales", title, title_key: title, permissions: ["pm.doc.create"] as string[] }],
64
+ permissions: ["pm.doc.create"],
65
+ };
66
+ }
67
+
68
+ describe("R4 语言取值", () => {
69
+ it("缺省 zh-CN(sys_setting 无 site.language 行)", async () => {
70
+ expect(await siteLanguage()).toBe("zh-CN");
71
+ });
72
+
73
+ it("站点设置 site.language 生效(R4:站点默认语言)", async () => {
74
+ await g.db.table("sys_setting").insert({ key: "site.language", module: "framework", group: "site", type: "string", value: "en-US", update_time: Date.now(), deprecated: 0 }).run();
75
+ expect(await siteLanguage()).toBe("en-US");
76
+ });
77
+ });
78
+
79
+ describe("R1 注册时解析(resolveDeclTitles)", () => {
80
+ it("AC1 首装:title_key 解析为站点语言 locale 文本", async () => {
81
+ const resolved = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES));
82
+ expect(resolved.roles[0]!.title).toBe("销售");
83
+ expect(resolved.roles[0]!.title_key).toBe("pm:role.sales");
84
+ });
85
+
86
+ it("R4:语言参数决定读取的 locale 文件", async () => {
87
+ const resolved = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "en-US", fakeReadFile(STAGING, PM_LOCALES));
88
+ expect(resolved.roles[0]!.title).toBe("Sales");
89
+ });
90
+
91
+ it("AC3 缺 key:保留裸 key(title 原样),绝不抛错", async () => {
92
+ const files = { [`${STAGING}/web/modules/pm/locales/zh-CN.json`]: JSON.stringify({ role: { sales: "销售" } }) };
93
+ const resolved = await resolveDeclTitles(pmDecl("pm:role.admin"), STAGING, "zh-CN", fakeReadFile(STAGING, files));
94
+ expect(resolved.roles[0]!.title).toBe("pm:role.admin");
95
+ });
96
+
97
+ it("R1 边界:locale 文件缺失 / 坏 JSON / 非 string 值 / 空 key 形状不符 → 原文保留", async () => {
98
+ const noFile = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, {}));
99
+ expect(noFile.roles[0]!.title).toBe("pm:role.sales");
100
+
101
+ const badJson = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, { [`${STAGING}/web/modules/pm/locales/zh-CN.json`]: "{not-json" }));
102
+ expect(badJson.roles[0]!.title).toBe("pm:role.sales");
103
+
104
+ const nonString = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, { [`${STAGING}/web/modules/pm/locales/zh-CN.json`]: JSON.stringify({ role: { sales: 42 } }) }));
105
+ expect(nonString.roles[0]!.title).toBe("pm:role.sales");
106
+
107
+ // 非 key 形状(中文文本)不做解析尝试——已解析文本二次注册不被误改
108
+ const plain = await resolveDeclTitles(pmDecl("销售"), STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES));
109
+ expect(plain.roles[0]!.title).toBe("销售");
110
+ });
111
+
112
+ it("R1 边界:ns ≠ 模块名不越界解析(跨 ns 无本包 locale)", async () => {
113
+ const decl = { ...pmDecl("wiki:role.sales"), module: "pm" };
114
+ const resolved = await resolveDeclTitles(decl, STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES));
115
+ expect(resolved.roles[0]!.title).toBe("wiki:role.sales");
116
+ });
117
+ });
118
+
119
+ describe("R1–R3 落库(registerSolution 消费解析后的 decl)", () => {
120
+ it("AC1 首装:sys_role 落 (title=销售, title_key=pm:role.sales),权限点照常", async () => {
121
+ const resolved = await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES));
122
+ const r = await registerSolution(resolved);
123
+ expect(r.ok).toBe(true);
124
+ const rows = roleRows();
125
+ expect(rows).toEqual([{ name: "pm.sales", title: "销售", title_key: "pm:role.sales" }]);
126
+ const perms = dbo.prepare("SELECT code FROM sys_permission").all() as Array<{ code: string }>;
127
+ expect(perms.map(p => p.code)).toEqual(["pm.doc.create"]);
128
+ });
129
+
130
+ it("AC2 升级:元数据覆盖写新解析文本 + title_key 刷新(R3 locale 改进随升级传播)", async () => {
131
+ await registerSolution(await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES)));
132
+ const improved = fakeReadFile(STAGING, { [`${STAGING}/web/modules/pm/locales/zh-CN.json`]: JSON.stringify({ role: { sales: "销售冠军" } }) });
133
+ const r = await registerSolution(await resolveDeclTitles(pmDecl("pm:role.sales"), STAGING, "zh-CN", improved));
134
+ expect(r.ok).toBe(true);
135
+ expect(roleRows()).toEqual([{ name: "pm.sales", title: "销售冠军", title_key: "pm:role.sales" }]);
136
+ });
137
+
138
+ it("AC3 缺 key:注册不失败,title = 裸 key(现行回退形态)", async () => {
139
+ const r = await registerSolution(await resolveDeclTitles(pmDecl("pm:role.viewer"), STAGING, "zh-CN", fakeReadFile(STAGING, PM_LOCALES)));
140
+ expect(r.ok).toBe(true);
141
+ expect(roleRows()).toEqual([{ name: "pm.sales", title: "pm:role.viewer", title_key: "pm:role.viewer" }]);
142
+ });
143
+ });
144
+
145
+ describe("R2 迁移 0018(sys_role.title_key 回填)", () => {
146
+ it("AC4 夹具真实迁移后 sys_role 带 title_key 列(列进库 ↔ schema.yaml 一致守卫的库侧半边)", () => {
147
+ const cols = (dbo.prepare("PRAGMA table_info(sys_role)").all() as Array<{ name: string }>).map(c => c.name);
148
+ expect(cols).toContain("title_key");
149
+ });
150
+
151
+ it("AC4 存量 solution 角色按 key 形状回填,builtin 角色不动、title 原文不改", () => {
152
+ const raw = new DatabaseSync(":memory:");
153
+ raw.exec(`
154
+ CREATE TABLE sys_role (
155
+ name TEXT PRIMARY KEY, module TEXT NOT NULL, title TEXT, description TEXT,
156
+ builtin INTEGER NOT NULL DEFAULT 0, deprecated INTEGER NOT NULL DEFAULT 0
157
+ );
158
+ INSERT INTO sys_role (name, module, title, builtin) VALUES
159
+ ('pm.sales', 'pm', 'pm:role.sales', 0),
160
+ ('wiki.admin', 'wiki', 'wiki:role.admin', 0),
161
+ ('admin', 'framework', '系统管理员', 1);
162
+ `);
163
+ raw.exec(readFileSync(join(HERE, "../../src/app/api/_platform/migrations/0018__sys_role_title_key.sql"), "utf8"));
164
+ const rows = raw.prepare("SELECT name, title, title_key FROM sys_role ORDER BY name").all() as Array<{ name: string, title: string, title_key: string | null }>;
165
+ expect(rows).toEqual([
166
+ { name: "admin", title: "系统管理员", title_key: null },
167
+ { name: "pm.sales", title: "pm:role.sales", title_key: "pm:role.sales" },
168
+ { name: "wiki.admin", title: "wiki:role.admin", title_key: "wiki:role.admin" },
169
+ ]);
170
+ });
171
+ });
@@ -3,7 +3,7 @@
3
3
  // 无 prerelease > 有 prerelease;「1.0」「1.0.0」「1.0.0.0」视为相等(安装器容错口径)。
4
4
  import { describe, expect, it } from "vitest";
5
5
 
6
- import { compareVersions } from "../../src/_platform/api/_lifecycle/semver";
6
+ import { compareVersions } from "../../src/app/api/_platform/_lifecycle/semver";
7
7
 
8
8
  const cases: Array<[a: string, b: string, expected: -1 | 0 | 1]> = [
9
9
  // 主版本数值比较(非字典序)
@@ -4,7 +4,7 @@
4
4
  import { Buffer } from "node:buffer";
5
5
  import { createHash } from "node:crypto";
6
6
  import { describe, expect, it } from "vitest";
7
- import { bytesToBase64, sha384B64, sha384Bytes } from "../../src/_platform/api/_lifecycle/sha384";
7
+ import { bytesToBase64, sha384B64, sha384Bytes } from "../../src/app/api/_platform/_lifecycle/sha384";
8
8
 
9
9
  const hex = (bytes: Uint8Array): string => Array.from(bytes, b => b.toString(16).padStart(2, "0")).join("");
10
10
 
@@ -5,7 +5,7 @@
5
5
  /* eslint-disable test/prefer-lowercase-title -- 端点路径是验收锚点 */
6
6
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
7
 
8
- import api from "../../src/_platform/api/solutions/api";
8
+ import api from "../../src/app/api/_platform/solutions/api";
9
9
  import { installDb } from "../fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -4,7 +4,7 @@
4
4
  // 四条负向是 G7 的重点:水位写坏 / 表不存在 / 值非法都必须**拒**,不能静默当「第一次见」——
5
5
  // 静默重置等于把「防回放」降级成「每次都当第一次见」,攻击者先破坏状态再回放即可穿透。
6
6
  import { beforeEach, describe, expect, it } from "vitest";
7
- import { guardAndRecordSourceSeq, readSourceSeq, SOURCE_SEQ_TABLE } from "../../src/_platform/api/_lifecycle/source-seq";
7
+ import { guardAndRecordSourceSeq, readSourceSeq, SOURCE_SEQ_TABLE } from "../../src/app/api/_platform/_lifecycle/source-seq";
8
8
  import { installDb } from "../fixture";
9
9
 
10
10
  const SRC = "https://cdn.example.com/solutions";
@@ -6,11 +6,11 @@
6
6
  // 命令构造为纯函数,argv 逐字断言(与平台 exec 白名单逐字对齐,S1-2 基座);
7
7
  // exec/fetch/sha256 面经 ctx 注入 stub,不真跑网络;合法夹具集成用例的 exec stub 落到真实系统 tar
8
8
  // (与生产同路径:isolate 不内嵌解压,exec.run 系统 tar)。
9
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
10
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
11
- import type { DownloadContext } from "../../src/_platform/api/_lifecycle/steps/download";
12
- import type { ExecFace, ExtractContext } from "../../src/_platform/api/_lifecycle/steps/extract";
13
- import type { VerifyContext } from "../../src/_platform/api/_lifecycle/steps/verify";
9
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
10
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
11
+ import type { DownloadContext } from "../../src/app/api/_platform/_lifecycle/steps/download";
12
+ import type { ExecFace, ExtractContext } from "../../src/app/api/_platform/_lifecycle/steps/extract";
13
+ import type { VerifyContext } from "../../src/app/api/_platform/_lifecycle/steps/verify";
14
14
  import { execFile as execFileCb } from "node:child_process";
15
15
  import { createHash } from "node:crypto";
16
16
  import * as fsp from "node:fs/promises";
@@ -19,10 +19,10 @@ import { join } from "node:path";
19
19
  import { promisify } from "node:util";
20
20
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
21
21
  import { defaultTestKey, nodeVerifyEd25519, signedArchiveFor } from "../../../src/lifecycle/__tests__/helpers";
22
- import { archiveFileOf, download } from "../../src/_platform/api/_lifecycle/steps/download";
23
- import { buildExtractCmd, buildListCmd, checkModuleManifest, extract } from "../../src/_platform/api/_lifecycle/steps/extract";
24
- import { verify } from "../../src/_platform/api/_lifecycle/steps/verify";
25
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
22
+ import { archiveFileOf, download } from "../../src/app/api/_platform/_lifecycle/steps/download";
23
+ import { buildExtractCmd, buildListCmd, checkModuleManifest, extract } from "../../src/app/api/_platform/_lifecycle/steps/extract";
24
+ import { verify } from "../../src/app/api/_platform/_lifecycle/steps/verify";
25
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
26
26
 
27
27
  const execFileP = promisify(execFileCb);
28
28
  const g = globalThis as any;
@@ -5,7 +5,7 @@
5
5
  import * as fsp from "node:fs/promises";
6
6
  import { describe, expect, it } from "vitest";
7
7
 
8
- import { appendAnonymousPaths, bumpInstallSeq, ensureReloadApi, hasAnchor, insertAnchor } from "../../src/_platform/api/_lifecycle/text-edit";
8
+ import { appendAnonymousPaths, bumpInstallSeq, ensureReloadApi, hasAnchor, insertAnchor } from "../../src/app/api/_platform/_lifecycle/text-edit";
9
9
 
10
10
  describe("insertAnchor(R18)", () => {
11
11
  it("带注释的 config.yaml 插入后注释/缩进/空行/键序逐字保留", () => {
@@ -4,8 +4,8 @@
4
4
  // 拒绝:绝对路径、任何 `..` 段、l/h 条目、空成员名、反斜杠路径;`./` 前缀相对合法。
5
5
  import { describe, expect, it } from "vitest";
6
6
 
7
- import { MAX_ENTRIES, MAX_PACKAGE_BYTES, MAX_SINGLE_ENTRY_BYTES, MAX_UNPACKED_BYTES } from "../../src/_platform/api/_lifecycle/limits";
8
- import { accumulateTarVolume, checkTarVolumeLimits, extractTarEntrySize, validateTarListing } from "../../src/_platform/api/_lifecycle/tgz-guard";
7
+ import { MAX_ENTRIES, MAX_PACKAGE_BYTES, MAX_SINGLE_ENTRY_BYTES, MAX_UNPACKED_BYTES } from "../../src/app/api/_platform/_lifecycle/limits";
8
+ import { accumulateTarVolume, checkTarVolumeLimits, extractTarEntrySize, validateTarListing } from "../../src/app/api/_platform/_lifecycle/tgz-guard";
9
9
 
10
10
  describe("validateTarListing(R1)", () => {
11
11
  it("正常成员列表通过", () => {
@@ -2,17 +2,17 @@
2
2
  // store 层:空库/幂等/首次绑定不可改/多 key/吊销/损坏 fail-closed/.bak 回落/并发读不见半截/审计。
3
3
  // 端点层:GET /market/trust、POST /market/trust/key、DELETE /market/trust/key?key_id=(del 动词 + query,system/roles 同款先例)。
4
4
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(market-api.spec 同约定) */
5
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
6
- import type { MarketEnv } from "../../src/_platform/api/market/api";
5
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
6
+ import type { MarketEnv } from "../../src/app/api/_platform/market/api";
7
7
  import { Buffer } from "node:buffer";
8
8
  import { createHash } from "node:crypto";
9
9
  import * as fsp from "node:fs/promises";
10
10
  import { tmpdir } from "node:os";
11
11
  import { join } from "node:path";
12
12
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
13
- import { createTrustStore, isValidKeyId } from "../../src/_platform/api/_lifecycle/trust";
14
- import { invalidateAllUsers } from "../../src/_platform/api/_shared/service/infra/access";
15
- import { createMarketApi, marketEnv } from "../../src/_platform/api/market/api";
13
+ import { createTrustStore, isValidKeyId } from "../../src/app/api/_platform/_lifecycle/trust";
14
+ import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
15
+ import { createMarketApi, marketEnv } from "../../src/app/api/_platform/market/api";
16
16
  import { installDb } from "../fixture";
17
17
 
18
18
  const g = globalThis as any;
@@ -5,17 +5,17 @@
5
5
  // 夹具:本仓范式(node:sqlite 真实迁移建表 + 真实 fs 面 g.fs);market_history 表由平台迁移
6
6
  // 0012(IF NOT EXISTS)随 installDb 重放建立,守卫语义(表不存在静默跳过)单列一例。
7
7
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(market-api.spec 同约定) */
8
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
9
- import type { MarketEnv } from "../../src/_platform/api/market/api";
8
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
9
+ import type { MarketEnv } from "../../src/app/api/_platform/market/api";
10
10
  import * as fsp from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import { dirname, join } from "node:path";
13
13
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
14
- import { readLock, writeLock } from "../../src/_platform/api/_lifecycle/lock";
15
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
16
- import { collectRetainedTables, moduleTablePrefix } from "../../src/_platform/api/_lifecycle/uninstall";
17
- import { invalidateAllUsers } from "../../src/_platform/api/_shared/service/infra/access";
18
- import { createMarketApi } from "../../src/_platform/api/market/api";
14
+ import { readLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock";
15
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
16
+ import { collectRetainedTables, moduleTablePrefix } from "../../src/app/api/_platform/_lifecycle/uninstall";
17
+ import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
18
+ import { createMarketApi } from "../../src/app/api/_platform/market/api";
19
19
  import { installDb } from "../fixture";
20
20
 
21
21
  const g = globalThis as any;
@@ -1,8 +1,8 @@
1
1
  // BDD:深检后端四项 + 端点(P2-7,C2-42)+ SSRF 三钉。
2
2
  // 纯 diff 函数 / 探针分类 / SSRF 钉(出站拦截 / header 清洗 / 不跟随重定向)+ 端点门(401/403/404)+ 编排。
3
3
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(market-api.spec 同约定) */
4
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
5
- import type { SolutionRouteRow } from "../../src/_platform/api/_lifecycle/verify-checks";
4
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
5
+ import type { SolutionRouteRow } from "../../src/app/api/_platform/_lifecycle/verify-checks";
6
6
  import * as fsp from "node:fs/promises";
7
7
  import { tmpdir } from "node:os";
8
8
  import { join } from "node:path";
@@ -18,9 +18,9 @@ import {
18
18
  sha384B64,
19
19
 
20
20
  verifyRouteReachability,
21
- } from "../../src/_platform/api/_lifecycle/verify-checks";
22
- import { siteOrigin } from "../../src/_platform/api/market/api";
23
- import { createSolutionsApi, solutionsEnv } from "../../src/_platform/api/solutions/api";
21
+ } from "../../src/app/api/_platform/_lifecycle/verify-checks";
22
+ import { siteOrigin } from "../../src/app/api/_platform/market/api";
23
+ import { createSolutionsApi, solutionsEnv } from "../../src/app/api/_platform/solutions/api";
24
24
  import { installDb } from "../fixture";
25
25
 
26
26
  const g = globalThis as any;
@@ -1,8 +1,8 @@
1
1
  // BDD:P2-8 深检报告结构 + 留存 N=10 + 可信性项(trust_state)+ i18n 降级项 + 设值不进报告。
2
2
  // 纯模块单测(verify-report.ts)+ 端点编排(persist-on-POST / GET verify/last / 降级项 / secret 不泄漏)。
3
3
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(verify-checks.spec 同约定) */
4
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
5
- import type { VerifyReport } from "../../src/_platform/api/_lifecycle/verify-report";
4
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
5
+ import type { VerifyReport } from "../../src/app/api/_platform/_lifecycle/verify-report";
6
6
  import * as fsp from "node:fs/promises";
7
7
  import { tmpdir } from "node:os";
8
8
  import { join } from "node:path";
@@ -12,8 +12,8 @@ import {
12
12
  persistReport,
13
13
  VERIFY_REPORTS_KEEP,
14
14
  verifyReportsDirOf,
15
- } from "../../src/_platform/api/_lifecycle/verify-report";
16
- import { createSolutionsApi, solutionsEnv } from "../../src/_platform/api/solutions/api";
15
+ } from "../../src/app/api/_platform/_lifecycle/verify-report";
16
+ import { createSolutionsApi, solutionsEnv } from "../../src/app/api/_platform/solutions/api";
17
17
  import { installDb } from "../fixture";
18
18
 
19
19
  const g = globalThis as any;
@@ -1,4 +1,4 @@
1
- import type { OrgDeps } from "../src/_platform/api/_shared/service/domain/org";
1
+ import type { OrgDeps } from "../src/app/api/_platform/_shared/service/domain/org";
2
2
 
3
3
  // BDD:组织架构领域(0016,方案 A1+A2)——CRUD 基线 + 归属权(D13)+ 保底出口(D14)。
4
4
  //
@@ -23,8 +23,8 @@ import {
23
23
  updateDepartment,
24
24
  updateMember,
25
25
  upsertDepartment,
26
- } from "../src/_platform/api/_shared/service/domain/org";
27
- import { deleteUser, listUsers, updateUserProfile } from "../src/_platform/api/_shared/service/domain/users";
26
+ } from "../src/app/api/_platform/_shared/service/domain/org";
27
+ import { deleteUser, listUsers, updateUserProfile } from "../src/app/api/_platform/_shared/service/domain/users";
28
28
  import { ADMIN, installDb } from "./fixture";
29
29
 
30
30
  let dbo: ReturnType<typeof installDb>;
@@ -263,12 +263,16 @@ describe("用户侧(改资料 / 删号)", () => {
263
263
  expect(dbo.prepare("SELECT COUNT(*) c FROM sys_refresh_token WHERE user_id = ?").get(uid)).toMatchObject({ c: 0 });
264
264
  });
265
265
 
266
- it("删号:禁止删自己;最后一个 superadmin 拒删", async () => {
266
+ it("删号:禁止删自己;非 superadmin 删 superadmin → 403(最后超管的实际防线)", async () => {
267
267
  await expect(deleteUser(ADMIN, ADMIN)).rejects.toMatchObject({ status: 400 });
268
268
  const solo = mkUser("u_sup");
269
269
  dbo.prepare("INSERT INTO sys_user_role (user_id, role_name) VALUES (?, 'superadmin')").run(solo);
270
- await expect(deleteUser(ADMIN, solo)).rejects.toMatchObject({ status: 400 });
271
- // 有两个 superadmin 时允许删(兜底只保最后一个)
270
+ // 目标门禁先于「最后一个 superadmin」计数:普通 admin 连碰都碰不到它。
271
+ // 计数兜底(400)因此在真实路径上不可达——操作者本身是 superadmin 时,
272
+ // 库里必然已有 ≥2 个 superadmin——保留它只为挡住未来的新写路径。
273
+ await expect(deleteUser(ADMIN, solo)).rejects.toMatchObject({ status: 403 });
274
+ expect(dbo.prepare("SELECT COUNT(*) c FROM users WHERE id = ?").get(solo)).toMatchObject({ c: 1 });
275
+ // 操作者升为 superadmin 后允许删(第二个超管在场)
272
276
  dbo.prepare("INSERT INTO sys_user_role (user_id, role_name) VALUES (?, 'superadmin')").run(ADMIN);
273
277
  await deleteUser(ADMIN, solo);
274
278
  expect(dbo.prepare("SELECT COUNT(*) c FROM users WHERE id = ?").get(solo)).toMatchObject({ c: 0 });
@@ -8,15 +8,15 @@
8
8
  // 反过来也钉住:返回字段与 scope 无关,消费模块不该按档位分支解析。
9
9
  //
10
10
  // 另外钉住 §9.1 阻断项:通讯录只出 activated=1,同步灌进来的「从未登录」账号不得被检索到。
11
- import type { OrgDeps } from "../src/_platform/api/_shared/service/domain/org";
11
+ import type { OrgDeps } from "../src/app/api/_platform/_shared/service/domain/org";
12
12
 
13
13
  import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
14
- import { contactDetail, searchContacts } from "../src/_platform/api/_shared/service/domain/contacts";
14
+ import { contactDetail, searchContacts } from "../src/app/api/_platform/_shared/service/domain/contacts";
15
15
  import {
16
16
  addMember,
17
17
  createDepartment,
18
18
  deleteDepartment,
19
- } from "../src/_platform/api/_shared/service/domain/org";
19
+ } from "../src/app/api/_platform/_shared/service/domain/org";
20
20
  import {
21
21
  isMemberOf,
22
22
  pageDepartments,
@@ -28,10 +28,10 @@ import {
28
28
  resolveReader,
29
29
  resolveReadFace,
30
30
  resolveUserIds,
31
- } from "../src/_platform/api/_shared/service/domain/org-read";
32
- import { createUser } from "../src/_platform/api/_shared/service/domain/users";
33
- import { invalidateUser } from "../src/_platform/api/_shared/service/infra/access";
34
- import { ensureWecomUser } from "../src/auth/api/_shared/wecom/login";
31
+ } from "../src/app/api/_platform/_shared/service/domain/org-read";
32
+ import { createUser } from "../src/app/api/_platform/_shared/service/domain/users";
33
+ import { invalidateUser } from "../src/app/api/_platform/_shared/service/infra/access";
34
+ import { ensureWecomUser } from "../src/app/api/auth/_shared/wecom/login";
35
35
  import { ADMIN, installDb } from "./fixture";
36
36
 
37
37
  let dbo: ReturnType<typeof installDb>;
@@ -3,7 +3,7 @@
3
3
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
4
4
  import { beforeAll, describe, expect, it } from "vitest";
5
5
 
6
- import { likePattern, paged, pagedFactory } from "../src/_platform/api/_shared/service/infra/query";
6
+ import { likePattern, paged, pagedFactory } from "../src/app/api/_platform/_shared/service/infra/query";
7
7
  import { installDb } from "./fixture";
8
8
 
9
9
  let dbo: ReturnType<typeof installDb>;
@@ -3,7 +3,7 @@
3
3
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
4
4
  import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
5
5
 
6
- import { auditDeny, auditOp, recordOp, resetDenyAudit } from "../src/_platform/api/_shared/service/infra/audit";
6
+ import { auditDeny, auditOp, recordOp, resetDenyAudit } from "../src/app/api/_platform/_shared/service/infra/audit";
7
7
  import { installDb } from "./fixture";
8
8
 
9
9
  let dbo: ReturnType<typeof installDb>;
@@ -5,7 +5,7 @@
5
5
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
6
6
  import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
7
7
 
8
- import { contactDetail, searchContacts } from "../src/_platform/api/_shared/service/domain/contacts";
8
+ import { contactDetail, searchContacts } from "../src/app/api/_platform/_shared/service/domain/contacts";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -84,7 +84,7 @@ describe("详情与限速", () => {
84
84
  it("接口级限速:同用户突发超限 → 429", async () => {
85
85
  const uid = mkUser("c_rate");
86
86
  g.http = { user: { id: uid } };
87
- const { rateLimit } = await import("../src/_platform/api/_shared/service/infra/rate");
87
+ const { rateLimit } = await import("../src/app/api/_platform/_shared/service/infra/rate");
88
88
  // 窗口 60s / 限额 3(测试参数;生产端点各自定额)
89
89
  for (let i = 0; i < 3; i++)
90
90
  await rateLimit(`contacts:${uid}`, 3, 60_000);
@@ -4,7 +4,7 @@
4
4
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
5
5
  import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
6
6
 
7
- import { assertPermission, assertSuperAdmin, assertSystemAdmin, currentAccess, invalidateUser } from "../src/_platform/api/_shared/service/infra/access";
7
+ import { assertPermission, assertSuperAdmin, assertSystemAdmin, currentAccess, invalidateUser } from "../src/app/api/_platform/_shared/service/infra/access";
8
8
  import { installDb } from "./fixture";
9
9
 
10
10
  let dbo: ReturnType<typeof installDb>;
@@ -4,8 +4,8 @@
4
4
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
5
5
  import { afterEach, beforeAll, describe, expect, it } from "vitest";
6
6
 
7
- import { createRole, deleteRole, listPermissions, listRoles, updateBindings } from "../src/_platform/api/_shared/service/domain/roles";
8
- import { assertPermission } from "../src/_platform/api/_shared/service/infra/access";
7
+ import { createRole, deleteRole, listPermissions, listRoles, updateBindings } from "../src/app/api/_platform/_shared/service/domain/roles";
8
+ import { assertPermission } from "../src/app/api/_platform/_shared/service/infra/access";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -156,4 +156,21 @@ describe("列表支撑(C2 页面数据源)", () => {
156
156
  expect(perms.find(p => p.code === "wiki.old")?.deprecated).toBe(true);
157
157
  expect(perms.find(p => p.code === "wiki.new")?.deprecated).toBe(false);
158
158
  });
159
+
160
+ it("listRoles 带出 title_key(R2:solution 角色 UI 实时翻译锚;custom/builtin 为 null)", async () => {
161
+ // solution 角色带 title_key(0018 起);custom 角色管理员手填 title、无 key
162
+ dbo.prepare("INSERT INTO sys_role (name, module, title, title_key, builtin) VALUES ('pm.sales', 'pm', '销售', 'pm:role.sales', 0)").run();
163
+ await createRole({ name: "custom.tk", title: "手填" });
164
+ const roles = await listRoles();
165
+ expect(roles.find(x => x.name === "pm.sales")?.title_key).toBe("pm:role.sales");
166
+ expect(roles.find(x => x.name === "custom.tk")?.title_key).toBe(null);
167
+ });
168
+
169
+ it("listRoles 原样返回 superadmin(只在 /system/roles 展示层隐去)", async () => {
170
+ // 成员资格页(users/roles.tsx)把 superadmin 当可授予项(仅 superadmin 可勾选),
171
+ // 页面数据源里少了它会让「授予超级管理员」直接不可用。过滤只做在展示层。
172
+ dbo.prepare("INSERT INTO sys_role (name, module, builtin) VALUES ('superadmin', 'framework', 1)").run();
173
+ const roles = await listRoles();
174
+ expect(roles.map(r => r.name)).toContain("superadmin");
175
+ });
159
176
  });
@@ -8,7 +8,7 @@ import { beforeAll, describe, expect, it } from "vitest";
8
8
 
9
9
  import { installDb } from "./fixture";
10
10
 
11
- const SEED = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "_platform", "api", "seed.sql");
11
+ const SEED = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "app", "api", "_platform", "seed.sql");
12
12
  let dbo: ReturnType<typeof installDb>;
13
13
 
14
14
  beforeAll(() => {