@oj-module/cli 0.2.53 → 0.2.56

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 (330) hide show
  1. package/devkit/SKILL.md +4 -4
  2. package/devkit/manual.md +79 -65
  3. package/package.json +2 -2
  4. package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
  5. package/shell-dist/assets/runtime.js +7 -4
  6. package/shell-dist/index.html +3 -3
  7. package/shell-dist/versions.json +3 -3
  8. package/src/__tests__/layout.test.ts +30 -1
  9. package/src/__tests__/templates-schema-columns.test.ts +99 -0
  10. package/src/__tests__/templates-tenant-scope.test.ts +3 -2
  11. package/src/alias-paths.ts +11 -12
  12. package/src/builtin.ts +10 -3
  13. package/src/config.ts +9 -0
  14. package/src/contract/__tests__/client-path.test.ts +30 -0
  15. package/src/contract/__tests__/emit-client.test.ts +20 -2
  16. package/src/contract/__tests__/handler-scan.test.ts +23 -0
  17. package/src/contract/__tests__/market-client.integration.test.ts +2 -2
  18. package/src/contract/__tests__/run-api.test.ts +2 -2
  19. package/src/contract/check.ts +11 -7
  20. package/src/contract/client-path.ts +30 -0
  21. package/src/contract/emit-client.ts +27 -6
  22. package/src/contract/handler-scan.ts +40 -3
  23. package/src/contract/run.ts +15 -5
  24. package/src/init.ts +29 -3
  25. package/src/layout.ts +72 -16
  26. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  27. package/src/market/__tests__/bootstrap.test.ts +1 -1
  28. package/src/site.ts +1 -1
  29. package/templates/README.md +49 -43
  30. package/templates/init.include.yaml +31 -19
  31. package/templates/openapi.json +1488 -120
  32. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
  33. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
  34. package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +29 -1
  35. package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +25 -1
  36. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
  37. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
  38. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
  39. package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +5 -0
  40. package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +60 -1
  41. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
  42. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
  43. package/templates/src/{auth/api → app/api/auth}/api.ts +59 -0
  44. package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +37 -1
  45. package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +23 -0
  46. package/templates/src/{notification/api → app/api/notification}/api.ts +17 -0
  47. package/templates/src/{notification/api → app/api/notification}/del/api.ts +2 -0
  48. package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
  49. package/templates/src/{system/api → app/api/system}/roles/api.ts +3 -0
  50. package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +3 -0
  51. package/templates/src/{system/api → app/api/system}/settings/api.ts +3 -0
  52. package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +3 -0
  53. package/templates/src/{system/api → app/api/system}/users/password/api.ts +3 -0
  54. package/templates/src/{system/api → app/api/system}/users/roles/api.ts +3 -0
  55. package/templates/src/app/api/web/get-async-routes/api.ts +23 -0
  56. package/templates/src/app/api/web/hello/api.ts +22 -0
  57. package/templates/src/{web/api → app/api/web}/user-info/api.ts +21 -1
  58. package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +99 -35
  59. package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +108 -61
  60. package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +133 -13
  61. package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +134 -33
  62. package/templates/src/app/web/entry.ts +77 -0
  63. package/templates/src/{home/web/__client/api.ts → app/web/home/__client/index.ts} +5 -2
  64. package/templates/src/app/web/home/routes.ts +41 -0
  65. package/templates/src/app/web/login/routes.ts +71 -0
  66. package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
  67. package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts} +67 -20
  68. package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +1 -1
  69. package/templates/src/app/web/notification/routes.ts +70 -0
  70. package/templates/src/{personal-center/web/__client/api.ts → app/web/personal-center/__client/index.ts} +5 -2
  71. package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +1 -1
  72. package/templates/src/app/web/personal-center/routes.ts +49 -0
  73. package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +29 -22
  74. package/templates/src/{system/web/__client/api.ts → app/web/system/__client/index.ts} +103 -58
  75. package/templates/src/{system/web → app/web/system}/locales/en-US.json +2 -3
  76. package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +2 -3
  77. package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +2 -2
  78. package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +1 -1
  79. package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +44 -19
  80. package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +5 -3
  81. package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
  82. package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +2 -2
  83. package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
  84. package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +23 -15
  85. package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +1 -1
  86. package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
  87. package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +25 -36
  88. package/templates/src/app/web/web/__client/api.schemas.ts +36 -0
  89. package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +38 -11
  90. package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
  91. package/templates/tests/auth-ldap.spec.ts +2 -2
  92. package/templates/tests/auth-session.spec.ts +3 -3
  93. package/templates/tests/auth-throttle.spec.ts +1 -1
  94. package/templates/tests/fixture.ts +6 -4
  95. package/templates/tests/guard.spec.ts +1 -1
  96. package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
  97. package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
  98. package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
  99. package/templates/tests/lifecycle/installer.spec.ts +10 -10
  100. package/templates/tests/lifecycle/market-api.spec.ts +14 -14
  101. package/templates/tests/lifecycle/migrate.spec.ts +6 -6
  102. package/templates/tests/lifecycle/preflight.spec.ts +3 -3
  103. package/templates/tests/lifecycle/register.spec.ts +1 -1
  104. package/templates/tests/lifecycle/semver.spec.ts +1 -1
  105. package/templates/tests/lifecycle/sha384.spec.ts +1 -1
  106. package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
  107. package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
  108. package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
  109. package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
  110. package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
  111. package/templates/tests/lifecycle/trust.spec.ts +5 -5
  112. package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
  113. package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
  114. package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
  115. package/templates/tests/org-departments.spec.ts +10 -6
  116. package/templates/tests/org-read.spec.ts +7 -7
  117. package/templates/tests/query-utils.spec.ts +1 -1
  118. package/templates/tests/rbac-audit.spec.ts +1 -1
  119. package/templates/tests/rbac-contacts.spec.ts +2 -2
  120. package/templates/tests/rbac-guard.spec.ts +1 -1
  121. package/templates/tests/rbac-roles.spec.ts +10 -2
  122. package/templates/tests/rbac-seed.spec.ts +1 -1
  123. package/templates/tests/rbac-settings.spec.ts +77 -7
  124. package/templates/tests/rbac-solution-perms.spec.ts +1 -1
  125. package/templates/tests/rbac-tables.spec.ts +29 -0
  126. package/templates/tests/rbac-users.spec.ts +55 -3
  127. package/templates/tests/roles-page.spec.ts +40 -0
  128. package/templates/tests/seed-role-accounts.spec.ts +1 -1
  129. package/templates/tests/tenant-access-cache.spec.ts +1 -1
  130. package/templates/tests/tenant-forgery.spec.ts +5 -5
  131. package/templates/tests/tenant-isolation.spec.ts +5 -4
  132. package/templates/tests/username-namespace.spec.ts +3 -3
  133. package/templates/tests/users-page.spec.ts +39 -0
  134. package/templates/tests/wecom-admin.spec.ts +6 -6
  135. package/templates/tests/wecom-client-timeout.spec.ts +1 -1
  136. package/templates/tests/wecom-connector.spec.ts +9 -9
  137. package/templates/tests/wecom-corp.spec.ts +9 -9
  138. package/templates/tests/wecom-crypt.spec.ts +1 -1
  139. package/templates/tests/wecom-endpoints.spec.ts +2 -2
  140. package/templates/tests/wecom-landing.spec.ts +1 -1
  141. package/templates/tests/wecom-org-sync.spec.ts +7 -7
  142. package/templates/tests/wecom-sync.spec.ts +3 -3
  143. package/templates/tsconfig.paths.json +11 -11
  144. package/templates/vitest.config.ts +2 -2
  145. package/templates/web.config.ts +24 -15
  146. package/vendor/host-versions.json +3 -3
  147. package/templates/src/home/web/entry.ts +0 -49
  148. package/templates/src/login/web/entry.ts +0 -79
  149. package/templates/src/notification/web/entry.ts +0 -84
  150. package/templates/src/personal-center/web/entry.ts +0 -60
  151. package/templates/src/web/api/get-async-routes/api.ts +0 -8
  152. package/templates/src/web/api/hello/api.ts +0 -10
  153. package/templates/src/web/web/__client/api.schemas.ts +0 -8
  154. package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
  155. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
  156. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
  157. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +0 -0
  158. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
  159. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
  160. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
  161. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
  162. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
  163. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
  164. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
  165. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
  166. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
  167. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
  168. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
  169. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +0 -0
  170. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
  171. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
  172. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
  173. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
  174. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
  175. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
  176. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
  177. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
  178. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
  179. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
  180. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
  181. package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
  182. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
  183. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
  184. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
  185. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +0 -0
  186. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
  187. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +0 -0
  188. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
  189. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
  190. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
  191. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
  192. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
  193. package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
  194. package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
  195. package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
  196. package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
  197. package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
  198. package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
  199. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
  200. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
  201. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
  202. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  203. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
  204. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  205. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
  206. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
  207. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
  208. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
  209. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
  210. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
  211. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  212. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
  213. package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
  214. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  215. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  216. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
  217. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
  218. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
  219. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
  220. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  221. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
  222. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
  223. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  224. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
  225. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
  226. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
  227. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
  228. package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
  229. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
  230. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
  231. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
  232. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
  233. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
  234. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
  235. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
  236. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
  237. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
  238. package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
  239. package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
  240. package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
  241. package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
  242. package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
  243. package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
  244. package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
  245. package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
  246. package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
  247. package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
  248. package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
  249. package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
  250. package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
  251. package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
  252. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
  253. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
  254. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
  255. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
  256. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
  257. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
  258. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
  259. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
  260. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
  261. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
  262. package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
  263. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
  264. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
  265. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
  266. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
  267. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
  268. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
  269. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
  270. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
  271. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
  272. package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
  273. package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
  274. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
  275. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
  276. /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
  277. /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
  278. /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
  279. /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
  280. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
  281. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
  282. /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
  283. /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
  284. /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
  285. /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
  286. /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
  287. /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
  288. /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
  289. /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
  290. /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  291. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
  292. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
  293. /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
  294. /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
  295. /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
  296. /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
  297. /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
  298. /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
  299. /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
  300. /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
  301. /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
  302. /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
  303. /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
  304. /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
  305. /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
  306. /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
  307. /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
  308. /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
  309. /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
  310. /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
  311. /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
  312. /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
  313. /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
  314. /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
  315. /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
  316. /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
  317. /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
  318. /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
  319. /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
  320. /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
  321. /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
  322. /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
  323. /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
  324. /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
  325. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
  326. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
  327. /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
  328. /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
  329. /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
  330. /package/templates/src/{system/web → app/web/system}/pages/forbidden.tsx +0 -0
@@ -191,7 +191,7 @@ export default function WecomAdminPage() {
191
191
  queryKey: ["system", "wecom-tenant-admin", corps.map(c => c.corpId).join(",")],
192
192
  queryFn: async () => {
193
193
  const out: Record<string, TenantAdminState> = {};
194
- await Promise.all(corps.map(async c => {
194
+ await Promise.all(corps.map(async (c) => {
195
195
  out[c.corpId] = await fetchTenantAdmin(c.corpId);
196
196
  }));
197
197
  return out;
@@ -244,7 +244,13 @@ export default function WecomAdminPage() {
244
244
  title: t("system:wecom.tenantAdmin.action"),
245
245
  key: "action",
246
246
  render: (_: unknown, row: { corpId: string, name: string | null }) => (
247
- <Button size="small" onClick={() => { setResetTarget(row); setResetPwd(""); }}>
247
+ <Button
248
+ size="small"
249
+ onClick={() => {
250
+ setResetTarget(row);
251
+ setResetPwd("");
252
+ }}
253
+ >
248
254
  {t("system:wecom.tenantAdmin.reset")}
249
255
  </Button>
250
256
  ),
@@ -433,7 +439,10 @@ export default function WecomAdminPage() {
433
439
  <Modal
434
440
  open={resetTarget != null}
435
441
  title={t("system:wecom.tenantAdmin.resetTitle")}
436
- onCancel={() => { setResetTarget(null); setResetPwd(""); }}
442
+ onCancel={() => {
443
+ setResetTarget(null);
444
+ setResetPwd("");
445
+ }}
437
446
  onOk={() => resetMut.mutate({ corpId: resetTarget!.corpId, password: resetPwd })}
438
447
  confirmLoading={resetMut.isPending}
439
448
  okButtonProps={{ disabled: resetPwd.length < 6 }}
@@ -1,11 +1,10 @@
1
- import type { AppRouteRecordRaw } from "@oj-module/runtime";
1
+ import type { AppRouteRecordRaw, ModuleContext } from "@oj-module/runtime";
2
2
 
3
3
  import { SettingOutlined } from "@ant-design/icons";
4
- import { defineModule } from "@oj-module/runtime";
5
4
 
6
5
  import { createElement, lazy } from "react";
7
6
 
8
- import { createSystemClient } from "./__client/api";
7
+ import { createSystemClient } from "./__client";
9
8
 
10
9
  const Users = lazy(() => import("./pages/users"));
11
10
  const UserRoles = lazy(() => import("./pages/users/roles"));
@@ -16,7 +15,24 @@ const Settings = lazy(() => import("./pages/settings"));
16
15
  const LoginMethods = lazy(() => import("./pages/login-methods"));
17
16
  const WecomAdmin = lazy(() => import("./pages/wecom"));
18
17
 
19
- const routes: AppRouteRecordRaw[] = [
18
+ /**
19
+ * `/system` 根的角色门禁(对齐后端 assertSystemAdmin,access.ts:87)——
20
+ * superadmin **或** admin 均可,两者必须同时保留:D12(2026-10-22)起账号名=角色名,
21
+ * admin 与 superadmin 是两个独立账号(seed.sql),只留 ["admin"] 会把 superadmin
22
+ * 挡在门外(注册期过滤 → 路由消失 → 登录后 404 兜底)。改这两个值前先跑
23
+ * tests/system-guard.regression.spec.ts。
24
+ */
25
+ const SYSTEM_ROLES = ["admin", "superadmin"];
26
+
27
+ /**
28
+ * 系统管理根 `/system`:框架管理面(用户/成员资格/角色权限/操作日志/站点设置/登录方式),
29
+ * 数据源为 oj 后端 /api/system/*(模板 api 模块)。__client 由 `ojm api` 生成。
30
+ *
31
+ * 路由级门禁(B16)挂在根上:非系统管理员整棵子树不注入、菜单不可见(等价于原先的
32
+ * 模块级 requiredRoles——聚合后模块级会把 /home 一起挡掉,故必须下移到根路由);
33
+ * API 级 403 由 assertSystemAdmin + service 层硬 enforcement 兜底。
34
+ */
35
+ export const routes: AppRouteRecordRaw[] = [
20
36
  {
21
37
  path: "/system",
22
38
  handle: {
@@ -25,6 +41,7 @@ const routes: AppRouteRecordRaw[] = [
25
41
  // 负 order = 底部固定区:系统管理 pin 在侧边栏底部(2026-10-20 序轴约定)
26
42
  order: -100,
27
43
  layout: "container",
44
+ roles: SYSTEM_ROLES,
28
45
  },
29
46
  children: [
30
47
  {
@@ -74,35 +91,7 @@ const routes: AppRouteRecordRaw[] = [
74
91
  },
75
92
  ];
76
93
 
77
- /**
78
- * system 模块:框架管理面(用户/成员资格/角色权限/操作日志/站点设置/登录方式),
79
- * 数据源为 oj 后端 /api/system/*(模板 api 模块)。__client 不随模板分发——
80
- * init 后 `oj openapi -o openapi.json` + `ojm api` 生成(生成前本文件 ts 报红属预期,
81
- * devkit manual §2.2)。
82
- */
83
- export default defineModule({
84
- name: "system",
85
- description: "系统管理模块(框架管理面)",
86
- version: "0.1.0",
87
- peerRuntime: ">=0.0.0",
88
- // 路由级门禁(B16):非系统管理员不注入 /system 路由、菜单不可见;
89
- // API 级 403 由 assertSystemAdmin + service 层硬 enforcement 兜底。
90
- // 角色口径对齐后端 assertSystemAdmin(access.ts:87)——superadmin **或**
91
- // admin 均可,两者必须同时保留:D12(2026-10-22)起账号名=角色名,admin 与
92
- // superadmin 是两个独立账号(seed.sql),只留 ["admin"] 会把 superadmin 挡在
93
- // 门外(注册期过滤 → 路由消失 → 登录后 404 兜底)。改这两个值前先跑
94
- // tests/system-guard.regression.spec.ts(只留 ["admin"] 会立刻变红)。
95
- config: { requiredRoles: ["admin", "superadmin"] },
96
- routes,
97
- i18n: {
98
- "zh-CN": () => import("./locales/zh-CN.json"),
99
- "en-US": () => import("./locales/en-US.json"),
100
- },
101
- lifecycle: {
102
- async onInit(ctx) {
103
- // 构造即接线:登记 /system 前缀 + 绑定 scoped request(D11 前缀收敛),
104
- // 页面直接 import 生成的端点函数(模块级单槽 req 已由本调用绑定)。
105
- createSystemClient(ctx);
106
- },
107
- },
108
- });
94
+ /** 构造即接线:登记 /system 前缀 + 绑定 scoped request(D11 前缀收敛)。 */
95
+ export async function onInit(ctx: ModuleContext) {
96
+ createSystemClient(ctx);
97
+ }
@@ -0,0 +1,36 @@
1
+ /* eslint-disable */
2
+ // 生成物:ojm api 从契约生成,勿手改(改动请改契约文件后重跑 ojm api)
3
+ // AC-D15:仅供 DEV 校验动态 import,生产构建不进产物
4
+ import { z } from "@oj-module/runtime";
5
+
6
+ export const schemas = {
7
+ "webGetAsyncRoutesGet": {
8
+ data: z.array(z.object({
9
+ "path": z.string().optional(),
10
+ "component": z.string().optional(),
11
+ "children": z.array(z.object({
12
+
13
+ })).optional()
14
+ })),
15
+ },
16
+ "webHelloGet": {
17
+ data: z.object({
18
+ "message": z.string(),
19
+ "user": z.number().nullable()
20
+ }),
21
+ },
22
+ "webUserInfoGet": {
23
+ data: z.object({
24
+ "id": z.string(),
25
+ "avatar": z.string(),
26
+ "username": z.string(),
27
+ "displayName": z.string(),
28
+ "email": z.string(),
29
+ "phoneNumber": z.string(),
30
+ "description": z.string(),
31
+ "roles": z.array(z.string()),
32
+ "mustChange": z.boolean(),
33
+ "permissions": z.array(z.string())
34
+ }),
35
+ },
36
+ };
@@ -17,6 +17,9 @@ export const API_PREFIX = "/web";
17
17
  /** 认领身份前缀(契约 apiPrefix,字面等于模块目录名):跨模块认领护栏对比基准 */
18
18
  export const MODULE_PREFIX = "/web";
19
19
 
20
+ /** 认领方模块(web.config clientOwners 指定):跨模块认领护栏对比基准 */
21
+ export const MODULE_OWNER = "/app";
22
+
20
23
  /**
21
24
  * 模块 entry 的 onInit 里一行创建(构造即 install):完成 apiPrefix 登记 +
22
25
  * scoped request 绑定,返回绑好前缀的端点函数集合。一模块一 client;
@@ -28,8 +31,8 @@ export function createWebClient(ctx: ModuleContext) {
28
31
  // 单槽、卸载互相耦合;跨模块需求应由本模块创建后经 provider 暴露。
29
32
  // 注意对比基准是 MODULE_PREFIX(目录名)而非 API_PREFIX——D-M8 映射时线上前缀
30
33
  // 属于别的命名空间,认领身份仍按目录名。
31
- if (import.meta.env.DEV && ctx.module.name !== MODULE_PREFIX.slice(1)) {
32
- console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_PREFIX.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
34
+ if (import.meta.env.DEV && ctx.module.name !== MODULE_OWNER.slice(1)) {
35
+ console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_OWNER.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
33
36
  }
34
37
  ctx.register.apiPrefix(API_PREFIX);
35
38
  bindRequest(ctx.utils.request);
@@ -64,14 +67,22 @@ async function toApiError(e: unknown): Promise<unknown> {
64
67
  return e;
65
68
  }
66
69
 
67
- export async function webGetAsyncRoutesGet(): Promise<unknown> {
70
+ export type WebGetAsyncRoutesGetData = z.infer<(typeof schemas)["webGetAsyncRoutesGet"]["data"]>;
71
+
72
+ export async function webGetAsyncRoutesGet(): Promise<WebGetAsyncRoutesGetData> {
68
73
  const client = ensureReq();
69
74
  try {
70
- const env = await client.get(`web/get-async-routes`).json<OjEnvelope<unknown>>();
75
+ const env = await client.get(`web/get-async-routes`).json<OjEnvelope<WebGetAsyncRoutesGetData>>();
71
76
  // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
72
77
  if (typeof env.code === "number" && env.code !== 0)
73
78
  throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
74
- const data = env.data as unknown;
79
+ const data = env.data as WebGetAsyncRoutesGetData;
80
+ if (import.meta.env.DEV) {
81
+ const { schemas } = await import("./api.schemas");
82
+ const r = schemas["webGetAsyncRoutesGet"].data.safeParse(data);
83
+ if (!r.success)
84
+ throw new ContractApiError(-1, `[契约违例] webGetAsyncRoutesGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
85
+ }
75
86
  return data;
76
87
  }
77
88
  catch (e) {
@@ -79,14 +90,22 @@ export async function webGetAsyncRoutesGet(): Promise<unknown> {
79
90
  }
80
91
  }
81
92
 
82
- export async function webHelloGet(): Promise<unknown> {
93
+ export type WebHelloGetData = z.infer<(typeof schemas)["webHelloGet"]["data"]>;
94
+
95
+ export async function webHelloGet(): Promise<WebHelloGetData> {
83
96
  const client = ensureReq();
84
97
  try {
85
- const env = await client.get(`web/hello`).json<OjEnvelope<unknown>>();
98
+ const env = await client.get(`web/hello`).json<OjEnvelope<WebHelloGetData>>();
86
99
  // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
87
100
  if (typeof env.code === "number" && env.code !== 0)
88
101
  throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
89
- const data = env.data as unknown;
102
+ const data = env.data as WebHelloGetData;
103
+ if (import.meta.env.DEV) {
104
+ const { schemas } = await import("./api.schemas");
105
+ const r = schemas["webHelloGet"].data.safeParse(data);
106
+ if (!r.success)
107
+ throw new ContractApiError(-1, `[契约违例] webHelloGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
108
+ }
90
109
  return data;
91
110
  }
92
111
  catch (e) {
@@ -94,14 +113,22 @@ export async function webHelloGet(): Promise<unknown> {
94
113
  }
95
114
  }
96
115
 
97
- export async function webUserInfoGet(): Promise<unknown> {
116
+ export type WebUserInfoGetData = z.infer<(typeof schemas)["webUserInfoGet"]["data"]>;
117
+
118
+ export async function webUserInfoGet(): Promise<WebUserInfoGetData> {
98
119
  const client = ensureReq();
99
120
  try {
100
- const env = await client.get(`web/user-info`).json<OjEnvelope<unknown>>();
121
+ const env = await client.get(`web/user-info`).json<OjEnvelope<WebUserInfoGetData>>();
101
122
  // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
102
123
  if (typeof env.code === "number" && env.code !== 0)
103
124
  throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
104
- const data = env.data as unknown;
125
+ const data = env.data as WebUserInfoGetData;
126
+ if (import.meta.env.DEV) {
127
+ const { schemas } = await import("./api.schemas");
128
+ const r = schemas["webUserInfoGet"].data.safeParse(data);
129
+ if (!r.success)
130
+ throw new ContractApiError(-1, `[契约违例] webUserInfoGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
131
+ }
105
132
  return data;
106
133
  }
107
134
  catch (e) {
@@ -28,7 +28,7 @@ import {
28
28
  platformMarketTrustPost,
29
29
  platformMarketUninstallImpactGet,
30
30
  platformMarketUninstallPost,
31
- } from "../../../_platform/web/__client/api";
31
+ } from "../../../app/web/_platform/__client";
32
32
 
33
33
  // 绑定运行时 request 到生成 client 的模块级单槽(生成 client 是静态单例,必须由某处
34
34
  // bindRequest;此处兜底确保市场子应用无论挂载顺序都能拿到请求实例)。与 _platform 模块
@@ -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 { esc, firstAttr } from "../src/auth/api/_shared/ldap";
8
- import { createLdapLogin } from "../src/auth/api/_shared/ldap-login";
7
+ import { esc, firstAttr } from "../src/app/api/auth/_shared/ldap";
8
+ import { createLdapLogin } from "../src/app/api/auth/_shared/ldap-login";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -1,11 +1,11 @@
1
1
  // BDD:登录链——B1 登录顺序(D14 统一 401)、B2 首登改密(AC1-27)、B3 refresh 轮换/reuse 检测(AC1-13)、B4 JWT 仅 sub。
2
2
  // deps 全注入(Node 侧 crypto 只读不可 stub,设计 §9.1);fake bcrypt 以 `hashed:<pw>` 为口令哈希约定。
3
3
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
4
- import type { SessionDeps } from "../src/auth/api/_shared/session";
4
+ import type { SessionDeps } from "../src/app/api/auth/_shared/session";
5
5
 
6
6
  import { afterEach, beforeAll, describe, expect, it } from "vitest";
7
- import { assertPermission } from "../src/_platform/api/_shared/service/infra/access";
8
- import { createSession } from "../src/auth/api/_shared/session";
7
+ import { assertPermission } from "../src/app/api/_platform/_shared/service/infra/access";
8
+ import { createSession } from "../src/app/api/auth/_shared/session";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  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 } from "vitest";
7
7
 
8
- import { loginBlocked, loginFailed, loginSucceeded, resetLoginThrottle } from "../src/_platform/api/_shared/service/infra/rate";
8
+ import { loginBlocked, loginFailed, loginSucceeded, resetLoginThrottle } from "../src/app/api/_platform/_shared/service/infra/rate";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -1,6 +1,6 @@
1
1
  // 后端 BDD 夹具:node:sqlite 内存库 + **真实迁移文件**建表,装到 globalThis.db——
2
2
  // 与 oj 运行时注入的全局同名同面,service 代码不改一行即可被测。
3
- // 夹具直接读 src/<模块>/api/migrations/*.sql,与迁移不可能漂移(沿用 oj-wiki 先例)。
3
+ // 夹具直接读 src/app/api/<模块>/migrations/*.sql,与迁移不可能漂移(沿用 oj-wiki 先例)。
4
4
  //
5
5
  // **构造器垫片(db.table / listTables)**:oj 的构造器是二进制内的 Rust 实现,Node 侧无可复用
6
6
  // 模块,故这里按手册契约(oj-api-dev api-manual「查询构造器」)写一层**最小 JS 渲染层**——
@@ -33,6 +33,8 @@ const { join, dirname } = req("node:path");
33
33
  const { fileURLToPath } = req("node:url");
34
34
 
35
35
  const SRC = join(dirname(fileURLToPath(import.meta.url)), "..", "src");
36
+ /** 后端模块根(0.2.55 布局:`src/app/api/<模块>`);迁移按目录发现,不在夹具里抄模块清单。 */
37
+ const API_ROOT = join(SRC, "app", "api");
36
38
 
37
39
  export const ADMIN = 1;
38
40
 
@@ -465,9 +467,9 @@ function listTables(dbo: any, prefix?: string): string[] {
465
467
  /** 建内存库 + 注入全局;返回 DatabaseSync 供用例直插数据/断言。 */
466
468
  export function installDb(): any {
467
469
  const dbo = new DatabaseSync(":memory:");
468
- // 重放 src 下全部模块的迁移(方言专属文件除外)
469
- for (const mod of readdirSync(SRC)) {
470
- const dir = join(SRC, mod, "api", "migrations");
470
+ // 重放 src/app/api 下全部模块的迁移(方言专属文件除外)
471
+ for (const mod of readdirSync(API_ROOT)) {
472
+ const dir = join(API_ROOT, mod, "migrations");
471
473
  if (!existsSync(dir))
472
474
  continue;
473
475
  for (const f of readdirSync(dir).filter((f: string) => f.endsWith(".sql") && !/\.(?:mysql|pg)\.sql$/.test(f)).sort())
@@ -3,7 +3,7 @@
3
3
  // (真实迁移建表 / 运行时全局 db 同面 / 事务往返)——S0 spike 的「一条真实判权 AC + 形态证明」。
4
4
  import { beforeAll, describe, expect, it } from "vitest";
5
5
 
6
- import { decideAccess, moduleOf } from "../src/_platform/api/_shared/service/infra/guard";
6
+ import { decideAccess, moduleOf } from "../src/app/api/_platform/_shared/service/infra/guard";
7
7
  import { ADMIN, installDb } from "./fixture";
8
8
 
9
9
  const as = (roles: string[], bindings: Record<string, string[]> = {}) => ({ roles, bindings });
@@ -4,14 +4,14 @@
4
4
  // 只保证读回自己写出的内容;任何不符格式的字节一律视为损坏 → 空状态,损坏上下文并入
5
5
  // 读结果(corruptNote 出口,禁 console——release isolate console 抛 BadResource,Finding A)。
6
6
  // fs 依赖经 globalThis.fs 注入(平台 isolate 注入 FsApi;测试注入 node:fs/promises 适配)。
7
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
7
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
8
8
  import * as fsp from "node:fs/promises";
9
9
  import { tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
11
 
12
12
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
- import { atomicWriteText } from "../../src/_platform/api/_lifecycle/atomic";
14
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
13
+ import { atomicWriteText } from "../../src/app/api/_platform/_lifecycle/atomic";
14
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
15
15
 
16
16
  const g = globalThis as any;
17
17
 
@@ -5,14 +5,14 @@
5
5
  // 与 PEM 私钥块实体(header 后跟 ≥100 字符 base64 体;tasks.ts redact 正则与测试假体
6
6
  // 体长 <100 不误伤)双口径扫描。
7
7
  // ③ 集成(market-api.spec):信任库为空 + bootstrapKey 注入 → cdn 安装直达 done 不落 needs_trust。
8
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
8
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
9
9
  import * as fsp from "node:fs/promises";
10
10
  import { tmpdir } from "node:os";
11
11
  import { dirname, join } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
14
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
15
- import { BOOTSTRAP_KEY, seedBootstrapKey } from "../../src/_platform/api/market/bootstrap";
14
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
15
+ import { BOOTSTRAP_KEY, seedBootstrapKey } from "../../src/app/api/_platform/market/bootstrap";
16
16
 
17
17
  const g = globalThis as any;
18
18
 
@@ -8,19 +8,19 @@
8
8
  // 快照逐数组 ⊇ 声明,少 = 注册丢失判失败)collect-all 全败齐报。
9
9
  // entry 浏览器加载不在安装器验(Node import 必假绿——设计 §4.1),注释钉口径。
10
10
  // fetch/sha384/query/reload 面经 ctx 注入 stub(同 steps-fs 范式),fs 为 globalThis.fs 面。
11
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
12
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
13
- import type { DeployWebContext, ModuleEntry } from "../../src/_platform/api/_lifecycle/steps/deploy-web";
14
- import type { ReloadContext, ReloadFace, ReloadResult } from "../../src/_platform/api/_lifecycle/steps/reload";
15
- import type { SmokeContext } from "../../src/_platform/api/_lifecycle/steps/smoke";
11
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
12
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
13
+ import type { DeployWebContext, ModuleEntry } from "../../src/app/api/_platform/_lifecycle/steps/deploy-web";
14
+ import type { ReloadContext, ReloadFace, ReloadResult } from "../../src/app/api/_platform/_lifecycle/steps/reload";
15
+ import type { SmokeContext } from "../../src/app/api/_platform/_lifecycle/steps/smoke";
16
16
  import { createHash } from "node:crypto";
17
17
  import * as fsp from "node:fs/promises";
18
18
  import { tmpdir } from "node:os";
19
19
  import { join } from "node:path";
20
20
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
21
- import { deployWeb, posixPath, upsertModulesJson } from "../../src/_platform/api/_lifecycle/steps/deploy-web";
22
- import { reloadAndAssert } from "../../src/_platform/api/_lifecycle/steps/reload";
23
- import { probeUrlOf, smoke } from "../../src/_platform/api/_lifecycle/steps/smoke";
21
+ import { deployWeb, posixPath, upsertModulesJson } from "../../src/app/api/_platform/_lifecycle/steps/deploy-web";
22
+ import { reloadAndAssert } from "../../src/app/api/_platform/_lifecycle/steps/reload";
23
+ import { probeUrlOf, smoke } from "../../src/app/api/_platform/_lifecycle/steps/smoke";
24
24
 
25
25
  const g = globalThis as any;
26
26
 
@@ -12,22 +12,22 @@
12
12
  // 过期(30min)可接管。setEnabled 四处同写(D12/AC-P1-6):锁摘除/恢复 + sidecar + route + modules。
13
13
  // 步骤面全 stub 注入(DIP):fetch/exec/tar/reload 不真跑;register/smoke/audit 走真实内存库夹具。
14
14
  /* eslint-disable test/prefer-lowercase-title -- 任务/AC 编号是验收锚点,保留大写(register.spec 同约定) */
15
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
16
- import type { InstallRequest } from "../../src/_platform/api/_lifecycle/installer";
17
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
18
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
15
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
16
+ import type { InstallRequest } from "../../src/app/api/_platform/_lifecycle/installer";
17
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
18
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
19
19
  import { createHash } from "node:crypto";
20
20
  import * as fsp from "node:fs/promises";
21
21
  import { tmpdir } from "node:os";
22
22
  import { join } from "node:path";
23
23
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
24
  import { defaultTestKey, nodeVerifyEd25519, signedArchiveFor } from "../../../src/lifecycle/__tests__/helpers";
25
- import { atomicWriteText } from "../../src/_platform/api/_lifecycle/atomic";
26
- import { gcVersions, reconcile, runInstall, setEnabled, STAGES } from "../../src/_platform/api/_lifecycle/installer";
27
- import { acquireTaskLock, commitLockEntry, parseLock, readLock, serializeLock, writeLock } from "../../src/_platform/api/_lifecycle/lock";
28
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
29
- import { getTask, readTasks, redact, sweepTimedOut, writeTask } from "../../src/_platform/api/_lifecycle/tasks";
30
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
25
+ import { atomicWriteText } from "../../src/app/api/_platform/_lifecycle/atomic";
26
+ import { gcVersions, reconcile, runInstall, setEnabled, STAGES } from "../../src/app/api/_platform/_lifecycle/installer";
27
+ import { acquireTaskLock, commitLockEntry, parseLock, readLock, serializeLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock";
28
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
29
+ import { getTask, readTasks, redact, sweepTimedOut, writeTask } from "../../src/app/api/_platform/_lifecycle/tasks";
30
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
31
31
  import { installDb } from "../fixture";
32
32
 
33
33
  const g = globalThis as any;
@@ -7,11 +7,11 @@
7
7
  // 简报 `task/:id` 段参数形态在模板 http 面无先例(contacts/detail?id= 同款 query 惯例),备案于任务报告。
8
8
  // 步骤面全 stub 注入(DIP):fetch/exec/reload 不真跑;register/smoke/audit 走真实内存库夹具。
9
9
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(installer.spec 同约定) */
10
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
11
- import type { FetchFace } from "../../src/_platform/api/_lifecycle/steps/download";
12
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
13
- import type { ReloadFace } from "../../src/_platform/api/_lifecycle/steps/reload";
14
- import type { MarketEnv } from "../../src/_platform/api/market/api";
10
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
11
+ import type { FetchFace } from "../../src/app/api/_platform/_lifecycle/steps/download";
12
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
13
+ import type { ReloadFace } from "../../src/app/api/_platform/_lifecycle/steps/reload";
14
+ import type { MarketEnv } from "../../src/app/api/_platform/market/api";
15
15
  import { createHash } from "node:crypto";
16
16
  import { readFileSync } from "node:fs";
17
17
  import * as fsp from "node:fs/promises";
@@ -22,14 +22,14 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22
22
  import { parse } from "yaml";
23
23
  import { defaultTestKey, nodeVerifyEd25519, signedArchiveFor } from "../../../src/lifecycle/__tests__/helpers";
24
24
  import { signIndex } from "../../../src/publish";
25
- import { readLock, writeLock } from "../../src/_platform/api/_lifecycle/lock";
26
- import { compareVersions } from "../../src/_platform/api/_lifecycle/semver";
27
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
28
- import { getTask, writeTask } from "../../src/_platform/api/_lifecycle/tasks";
29
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
30
- import { invalidateAllUsers } from "../../src/_platform/api/_shared/service/infra/access";
31
- import { createMarketApi, marketEnv, parseVersionsJson, versionsJsonPathOf } from "../../src/_platform/api/market/api";
32
- import { localIndexSource } from "../../src/_platform/api/market/index-source";
25
+ import { readLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock";
26
+ import { compareVersions } from "../../src/app/api/_platform/_lifecycle/semver";
27
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
28
+ import { getTask, writeTask } from "../../src/app/api/_platform/_lifecycle/tasks";
29
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
30
+ import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
31
+ import { createMarketApi, marketEnv, parseVersionsJson, versionsJsonPathOf } from "../../src/app/api/_platform/market/api";
32
+ import { localIndexSource } from "../../src/app/api/_platform/market/index-source";
33
33
  import { installDb } from "../fixture";
34
34
 
35
35
  function hexToBytes(hex: string): Uint8Array {
@@ -305,7 +305,7 @@ describe("权限门(R9:市场编排 superadmin 独占,meta 登录即可)
305
305
  it("pair-pin:meta apiVersion === _platform/manifest.yaml version(手工镜像漂移防护,P2-9)", async () => {
306
306
  // API_VERSION(market/api.ts)与 _platform/manifest.yaml version 为手工同步的双真源;
307
307
  // 本测试经 meta 端点间接钉住 API_VERSION,漂移即红(peerPlatform 预检口径失真)。
308
- const manifestPath = join(dirname(fileURLToPath(import.meta.url)), "../../src/_platform/api/manifest.yaml");
308
+ const manifestPath = join(dirname(fileURLToPath(import.meta.url)), "../../src/app/api/_platform/manifest.yaml");
309
309
  const manifestVersion = (parse(readFileSync(manifestPath, "utf8")) as { version: string }).version;
310
310
  const api = createMarketApi(makeEnv());
311
311
  g.http = { user: { id: 1 } };
@@ -7,16 +7,16 @@
7
7
  // rollbackGuard 纯函数:目标产物 max seq < 账本 head = 降版部署,禁止(OJ-9/M003 同源红线);
8
8
  // seq 为数值非版本号,直接数值比较(semver.ts 不适用)。
9
9
  // exec/fs 面注入 stub(同 steps-fs 范式),不真跑 oj/sqlite。
10
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
11
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
12
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
13
- import type { MigrateContext } from "../../src/_platform/api/_lifecycle/steps/migrate";
10
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
11
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
12
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
13
+ import type { MigrateContext } from "../../src/app/api/_platform/_lifecycle/steps/migrate";
14
14
  import * as fsp from "node:fs/promises";
15
15
  import { tmpdir } from "node:os";
16
16
  import { join } from "node:path";
17
17
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
18
- import { parseLock } from "../../src/_platform/api/_lifecycle/lock";
19
- import { buildMigrateCmd, maxAppliedSeq, migrate, migrateSvcDirOf, parseMigrateOutput, rollbackGuard } from "../../src/_platform/api/_lifecycle/steps/migrate";
18
+ import { parseLock } from "../../src/app/api/_platform/_lifecycle/lock";
19
+ import { buildMigrateCmd, maxAppliedSeq, migrate, migrateSvcDirOf, parseMigrateOutput, rollbackGuard } from "../../src/app/api/_platform/_lifecycle/steps/migrate";
20
20
 
21
21
  const g = globalThis as any;
22
22
  const DB_BYTES = new TextEncoder().encode("sqlite-header-bytes\n");
@@ -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>;
@@ -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";