@oj-module/cli 0.2.1 → 0.2.5

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 (231) hide show
  1. package/devkit/README.md +14 -3
  2. package/devkit/SKILL.md +16 -4
  3. package/devkit/framework-dev.md +8 -6
  4. package/devkit/manual.md +243 -23
  5. package/package.json +2 -2
  6. package/shell-dist/assets/runtime.js +143 -8
  7. package/shell-dist/index.html +2 -2
  8. package/shell-dist/versions.json +3 -3
  9. package/src/args.ts +67 -0
  10. package/src/build.ts +11 -3
  11. package/src/bundle.ts +197 -0
  12. package/src/contract/__tests__/handler-scan.test.ts +82 -0
  13. package/src/contract/check.ts +2 -1
  14. package/src/contract/handler-scan.ts +32 -0
  15. package/src/deterministic-tar.ts +120 -0
  16. package/src/dev.ts +4 -2
  17. package/src/enable.ts +28 -0
  18. package/src/index.ts +63 -2
  19. package/src/info.ts +3 -2
  20. package/src/init.ts +147 -14
  21. package/src/install.ts +268 -0
  22. package/src/keygen.ts +88 -0
  23. package/src/layout.ts +31 -0
  24. package/src/lifecycle/__tests__/authoring.test.ts +109 -0
  25. package/src/lifecycle/__tests__/bundle.test.ts +150 -0
  26. package/src/lifecycle/__tests__/deterministic-tar.test.ts +145 -0
  27. package/src/lifecycle/__tests__/fixtures/README.md +59 -0
  28. package/src/lifecycle/__tests__/fixtures/fixture-invalid/EXPECTED-VIOLATIONS.md +27 -0
  29. package/src/lifecycle/__tests__/fixtures/fixture-invalid/api/src/Demo/manifest.yaml +6 -0
  30. package/src/lifecycle/__tests__/fixtures/fixture-invalid/manifest.yaml +40 -0
  31. package/src/lifecycle/__tests__/fixtures/fixture-invalid/web/src/Demo/entry.ts +14 -0
  32. package/src/lifecycle/__tests__/fixtures/fixture-invalid/web/src/Demo/locales/en-US.json +12 -0
  33. package/src/lifecycle/__tests__/fixtures/fixture-invalid/web/src/Demo/locales/zh-CN.json +12 -0
  34. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/manifest.yaml +9 -0
  35. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/migrations/0001__init.sql +6 -0
  36. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/ping/api.ts +9 -0
  37. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/schema.yaml +9 -0
  38. package/src/lifecycle/__tests__/fixtures/fixture-valid/manifest.yaml +96 -0
  39. package/src/lifecycle/__tests__/fixtures/fixture-valid/web/src/demo-solution/entry.ts +23 -0
  40. package/src/lifecycle/__tests__/fixtures/fixture-valid/web/src/demo-solution/locales/en-US.json +29 -0
  41. package/src/lifecycle/__tests__/fixtures/fixture-valid/web/src/demo-solution/locales/zh-CN.json +29 -0
  42. package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz +0 -0
  43. package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.bad.sig +7 -0
  44. package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.sig +7 -0
  45. package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.unknown.sig +7 -0
  46. package/src/lifecycle/__tests__/fixtures/sig/gen-signed-fixtures.d.mts +21 -0
  47. package/src/lifecycle/__tests__/fixtures/sig/gen-signed-fixtures.mjs +196 -0
  48. package/src/lifecycle/__tests__/helpers.ts +78 -0
  49. package/src/lifecycle/__tests__/install.test.ts +514 -0
  50. package/src/lifecycle/__tests__/keygen.test.ts +61 -0
  51. package/src/lifecycle/__tests__/manifest-contract.test.ts +228 -0
  52. package/src/lifecycle/__tests__/publish.test.ts +80 -0
  53. package/src/lifecycle/__tests__/sig-fixtures.test.ts +100 -0
  54. package/src/lifecycle/__tests__/sign.test.ts +131 -0
  55. package/src/lifecycle/__tests__/smoke.test.ts +61 -0
  56. package/src/lifecycle/__tests__/trust.test.ts +177 -0
  57. package/src/lifecycle/__tests__/uninstall.test.ts +87 -0
  58. package/src/lifecycle/keygen.ts +40 -0
  59. package/src/lifecycle/sign.ts +74 -0
  60. package/src/manifest-contract.ts +696 -0
  61. package/src/oj-cert.ts +19 -0
  62. package/src/oj-cmd.ts +3 -3
  63. package/src/preview.ts +2 -2
  64. package/src/publish.ts +150 -0
  65. package/src/sign.ts +128 -0
  66. package/src/site.ts +146 -0
  67. package/src/trust.ts +116 -0
  68. package/src/uninstall.ts +29 -0
  69. package/src/usage.ts +35 -1
  70. package/templates/README.md +46 -7
  71. package/templates/api/config.yaml +47 -0
  72. package/templates/api/seed.mysql.sql +1 -1
  73. package/templates/api/seed.pg.sql +1 -1
  74. package/templates/api/src/_platform/_lifecycle/atomic.ts +98 -0
  75. package/templates/api/src/_platform/_lifecycle/history.ts +31 -0
  76. package/templates/api/src/_platform/_lifecycle/installer.ts +1080 -0
  77. package/templates/api/src/_platform/_lifecycle/lock.ts +148 -0
  78. package/templates/api/src/_platform/_lifecycle/semver.ts +61 -0
  79. package/templates/api/src/_platform/_lifecycle/sha384.ts +187 -0
  80. package/templates/api/src/_platform/_lifecycle/state.ts +256 -0
  81. package/templates/api/src/_platform/_lifecycle/steps/deploy-web.ts +182 -0
  82. package/templates/api/src/_platform/_lifecycle/steps/download.ts +90 -0
  83. package/templates/api/src/_platform/_lifecycle/steps/extract.ts +239 -0
  84. package/templates/api/src/_platform/_lifecycle/steps/migrate.ts +363 -0
  85. package/templates/api/src/_platform/_lifecycle/steps/preflight.ts +590 -0
  86. package/templates/api/src/_platform/_lifecycle/steps/register.ts +211 -0
  87. package/templates/api/src/_platform/_lifecycle/steps/reload.ts +81 -0
  88. package/templates/api/src/_platform/_lifecycle/steps/smoke.ts +216 -0
  89. package/templates/api/src/_platform/_lifecycle/steps/verify.ts +207 -0
  90. package/templates/api/src/_platform/_lifecycle/tasks.ts +119 -0
  91. package/templates/api/src/_platform/_lifecycle/text-edit.ts +110 -0
  92. package/templates/api/src/_platform/_lifecycle/tgz-guard.ts +41 -0
  93. package/templates/api/src/_platform/_lifecycle/trust.ts +229 -0
  94. package/templates/api/src/_platform/_lifecycle/types.ts +94 -0
  95. package/templates/api/src/_platform/_lifecycle/uninstall.ts +283 -0
  96. package/templates/api/src/_platform/_lifecycle/verify-checks.ts +228 -0
  97. package/templates/api/src/_platform/_lifecycle/verify-report.ts +92 -0
  98. package/templates/api/src/_platform/_shared/service/access.ts +2 -2
  99. package/templates/api/src/_platform/_shared/service/settings.ts +10 -1
  100. package/templates/api/src/_platform/_shared/service/solution-perms.ts +14 -7
  101. package/templates/api/src/_platform/_shared/site-facade.ts +44 -0
  102. package/templates/api/src/_platform/manifest.yaml +1 -0
  103. package/templates/api/src/_platform/market/api.ts +883 -0
  104. package/templates/api/src/_platform/market/bootstrap.ts +39 -0
  105. package/templates/api/src/_platform/migrations/0010__sys_solution_route.mysql.sql +17 -0
  106. package/templates/api/src/_platform/migrations/0010__sys_solution_route.pg.sql +16 -0
  107. package/templates/api/src/_platform/migrations/0010__sys_solution_route.sql +18 -0
  108. package/templates/api/src/_platform/migrations/0011__sys_setting_group_label.mysql.sql +2 -0
  109. package/templates/api/src/_platform/migrations/0011__sys_setting_group_label.sql +4 -0
  110. package/templates/api/src/_platform/migrations/0012__market_history.mysql.sql +13 -0
  111. package/templates/api/src/_platform/migrations/0012__market_history.pg.sql +12 -0
  112. package/templates/api/src/_platform/migrations/0012__market_history.sql +14 -0
  113. package/templates/api/src/_platform/schema.yaml +21 -4
  114. package/templates/api/src/_platform/seed.sql +2 -2
  115. package/templates/api/src/_platform/solutions/api.ts +212 -0
  116. package/templates/api/src/demo/manifest.yaml +5 -0
  117. package/templates/api/src/demo/migrations/0001__create_todos.sql +9 -0
  118. package/templates/api/src/demo/schema.yaml +11 -0
  119. package/templates/api/src/demo/seed.sql +5 -0
  120. package/templates/api/src/demo/todos/api.ts +59 -0
  121. package/templates/api/src/home/line/api.ts +57 -0
  122. package/templates/api/src/home/manifest.yaml +6 -0
  123. package/templates/api/src/home/migrations/0001__create_home_tables.sql +13 -0
  124. package/templates/api/src/home/pie/api.ts +40 -0
  125. package/templates/api/src/home/schema.yaml +14 -0
  126. package/templates/api/src/home/seed.sql +70 -0
  127. package/templates/api/src/system/oplog/api.ts +44 -2
  128. package/templates/api/tests/fixture.ts +16 -0
  129. package/templates/api/tests/lifecycle/atomic-state.spec.ts +183 -0
  130. package/templates/api/tests/lifecycle/bootstrap.spec.ts +116 -0
  131. package/templates/api/tests/lifecycle/deploy-smoke.spec.ts +492 -0
  132. package/templates/api/tests/lifecycle/installer.spec.ts +1218 -0
  133. package/templates/api/tests/lifecycle/market-api.spec.ts +971 -0
  134. package/templates/api/tests/lifecycle/migrate.spec.ts +575 -0
  135. package/templates/api/tests/lifecycle/preflight.spec.ts +255 -0
  136. package/templates/api/tests/lifecycle/register.spec.ts +194 -0
  137. package/templates/api/tests/lifecycle/semver.spec.ts +43 -0
  138. package/templates/api/tests/lifecycle/sha384.spec.ts +48 -0
  139. package/templates/api/tests/lifecycle/sig-fixtures.spec.ts +65 -0
  140. package/templates/api/tests/lifecycle/solutions-api.spec.ts +98 -0
  141. package/templates/api/tests/lifecycle/steps-fs.spec.ts +623 -0
  142. package/templates/api/tests/lifecycle/text-edit.spec.ts +177 -0
  143. package/templates/api/tests/lifecycle/tgz-guard.spec.ts +103 -0
  144. package/templates/api/tests/lifecycle/trust.spec.ts +310 -0
  145. package/templates/api/tests/lifecycle/uninstall.spec.ts +386 -0
  146. package/templates/api/tests/lifecycle/verify-checks.spec.ts +385 -0
  147. package/templates/api/tests/lifecycle/verify-report.spec.ts +290 -0
  148. package/templates/api/tests/rbac-settings.spec.ts +8 -0
  149. package/templates/init.include.yaml +56 -0
  150. package/templates/openapi.json +527 -0
  151. package/templates/package.json +52 -0
  152. package/templates/web/src/_platform/__client/api.schemas.ts +8 -0
  153. package/templates/web/src/_platform/__client/api.ts +335 -0
  154. package/templates/web/src/about/entry.ts +42 -0
  155. package/templates/web/src/about/locales/en-US.json +16 -0
  156. package/templates/web/src/about/locales/zh-CN.json +16 -0
  157. package/templates/web/src/about/pages/constants.ts +20 -0
  158. package/templates/web/src/about/pages/index.tsx +135 -0
  159. package/templates/web/src/access/entry.ts +74 -0
  160. package/templates/web/src/access/locales/en-US.json +46 -0
  161. package/templates/web/src/access/locales/zh-CN.json +46 -0
  162. package/templates/web/src/access/pages/access-mode/index.tsx +16 -0
  163. package/templates/web/src/access/pages/admin-visible/index.tsx +16 -0
  164. package/templates/web/src/access/pages/button-control/index.tsx +181 -0
  165. package/templates/web/src/access/pages/common-visible/index.tsx +16 -0
  166. package/templates/web/src/access/pages/page-control/index.tsx +142 -0
  167. package/templates/web/src/demo/__client/api.schemas.ts +20 -0
  168. package/templates/web/src/demo/__client/api.ts +89 -0
  169. package/templates/web/src/exception/entry.ts +80 -0
  170. package/templates/web/src/exception/locales/en-US.json +13 -0
  171. package/templates/web/src/exception/locales/zh-CN.json +13 -0
  172. package/templates/web/src/exception/pages/403/index.tsx +30 -0
  173. package/templates/web/src/exception/pages/404/index.tsx +30 -0
  174. package/templates/web/src/exception/pages/500/index.tsx +30 -0
  175. package/templates/web/src/exception/pages/unknown-component/index.tsx +38 -0
  176. package/templates/web/src/home/__client/api.schemas.ts +22 -0
  177. package/templates/web/src/home/__client/api.ts +113 -0
  178. package/templates/web/src/login/locales/en-US.json +6 -0
  179. package/templates/web/src/login/locales/zh-CN.json +6 -0
  180. package/templates/web/src/login/pages/login.tsx +67 -18
  181. package/templates/web/src/oj-market/__tests__/CardGrid.test.tsx +93 -0
  182. package/templates/web/src/oj-market/__tests__/HistoryDrawer.test.tsx +28 -0
  183. package/templates/web/src/oj-market/__tests__/InstallDialog.test.tsx +69 -0
  184. package/templates/web/src/oj-market/__tests__/RollbackDialog.test.tsx +43 -0
  185. package/templates/web/src/oj-market/__tests__/UninstallConfirm.test.tsx +30 -0
  186. package/templates/web/src/oj-market/__tests__/marketClient.fake.ts +70 -0
  187. package/templates/web/src/oj-market/__tests__/poll.test.ts +24 -0
  188. package/templates/web/src/oj-market/__tests__/render.tsx +14 -0
  189. package/templates/web/src/oj-market/__tests__/runtime-stub.ts +14 -0
  190. package/templates/web/src/oj-market/__tests__/semver.test.ts +26 -0
  191. package/templates/web/src/oj-market/api/MarketClientProvider.tsx +12 -0
  192. package/templates/web/src/oj-market/api/client.ts +135 -0
  193. package/templates/web/src/oj-market/api/context.ts +11 -0
  194. package/templates/web/src/oj-market/api/defaultClient.ts +7 -0
  195. package/templates/web/src/oj-market/components/CardGrid.tsx +76 -0
  196. package/templates/web/src/oj-market/components/CardItem.tsx +102 -0
  197. package/templates/web/src/oj-market/components/DetailDrawer.tsx +53 -0
  198. package/templates/web/src/oj-market/components/HistoryDrawer.tsx +75 -0
  199. package/templates/web/src/oj-market/components/InstallDialog.tsx +112 -0
  200. package/templates/web/src/oj-market/components/RollbackDialog.tsx +79 -0
  201. package/templates/web/src/oj-market/components/UninstallConfirm.tsx +74 -0
  202. package/templates/web/src/oj-market/entry.ts +27 -0
  203. package/templates/web/src/oj-market/hooks/useMarket.ts +91 -0
  204. package/templates/web/src/oj-market/locales/en-US.json +100 -0
  205. package/templates/web/src/oj-market/locales/zh-CN.json +100 -0
  206. package/templates/web/src/oj-market/pages/MarketApp.tsx +136 -0
  207. package/templates/web/src/oj-market/types.ts +84 -0
  208. package/templates/web/src/oj-market/utils/poll.ts +36 -0
  209. package/templates/web/src/oj-market/utils/semver.ts +14 -0
  210. package/templates/web/src/outside/entry.ts +80 -0
  211. package/templates/web/src/outside/locales/en-US.json +10 -0
  212. package/templates/web/src/outside/locales/zh-CN.json +10 -0
  213. package/templates/web/src/personal-center/entry.ts +1 -10
  214. package/templates/web/src/personal-center/locales/en-US.json +1 -5
  215. package/templates/web/src/personal-center/locales/zh-CN.json +1 -5
  216. package/templates/web/src/route-nest/entry.ts +73 -0
  217. package/templates/web/src/route-nest/locales/en-US.json +9 -0
  218. package/templates/web/src/route-nest/locales/zh-CN.json +9 -0
  219. package/templates/web/src/route-nest/pages/menu1/menu1-1/index.tsx +5 -0
  220. package/templates/web/src/route-nest/pages/menu1/menu1-2/index.tsx +5 -0
  221. package/templates/web/src/route-nest/pages/menu2/index.tsx +5 -0
  222. package/templates/web/src/system/__client/api.schemas.ts +162 -0
  223. package/templates/web/src/system/__client/api.ts +382 -0
  224. package/templates/web/src/system/pages/oplog/index.tsx +1 -1
  225. package/templates/web/src/system/pages/roles/index.tsx +1 -0
  226. package/templates/web/src/system/pages/settings/index.tsx +1 -1
  227. package/templates/web/src/system/pages/users/index.tsx +1 -1
  228. package/templates/web.config.ts +32 -40
  229. package/vendor/host-versions.json +3 -3
  230. package/templates/pnpm-workspace.yaml +0 -14
  231. package/templates/web/src/personal-center/pages/settings/index.tsx +0 -5
@@ -0,0 +1,136 @@
1
+ import type { IndexRow, TaskRecord } from "../types";
2
+ import { Typography } from "antd";
3
+ import React, { useState } from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import { useMarketClient } from "../api/context";
6
+ import { CardGrid } from "../components/CardGrid";
7
+ import { DetailDrawer } from "../components/DetailDrawer";
8
+ import { HistoryDrawer } from "../components/HistoryDrawer";
9
+ import { InstallDialog } from "../components/InstallDialog";
10
+ import { RollbackDialog } from "../components/RollbackDialog";
11
+ import { UninstallConfirm } from "../components/UninstallConfirm";
12
+ import { useMarketIndex } from "../hooks/useMarket";
13
+
14
+ /** 市场应用主页面:组装卡片网格 + 详情/安装/卸载/回滚/历史交互面(单一容器,DRY 编排) */
15
+ export default function MarketApp() {
16
+ const { t } = useTranslation();
17
+ const client = useMarketClient();
18
+ const index = useMarketIndex();
19
+
20
+ const [detailRow, setDetailRow] = useState<IndexRow | null>(null);
21
+ const [installRow, setInstallRow] = useState<IndexRow | null>(null);
22
+ const [installVersion, setInstallVersion] = useState<string | null>(null);
23
+ const [installMode, setInstallMode] = useState<"install" | "upgrade" | "rollback">("install");
24
+ const [uninstallRow, setUninstallRow] = useState<IndexRow | null>(null);
25
+ const [rollbackRow, setRollbackRow] = useState<IndexRow | null>(null);
26
+ const [historyRow, setHistoryRow] = useState<IndexRow | null>(null);
27
+ const [busyName, setBusyName] = useState<string | null>(null);
28
+
29
+ const rows = index.status === "ok" ? index.data : [];
30
+
31
+ // 安装/升级:目标版本缺省(取 latest);已装视为升级
32
+ const handleInstall = (row: IndexRow) => {
33
+ setDetailRow(null);
34
+ setInstallRow(row);
35
+ setInstallVersion(null);
36
+ setInstallMode(row.installed != null ? "upgrade" : "install");
37
+ };
38
+
39
+ // 回滚:先开版本选择抽屉,选中旧版后转安装(降级安装,显式 version)
40
+ const handleRollback = (row: IndexRow) => {
41
+ setDetailRow(null);
42
+ setRollbackRow(row);
43
+ };
44
+
45
+ const handleRollbackChosen = (row: IndexRow, version: string) => {
46
+ setRollbackRow(null);
47
+ setInstallRow(row);
48
+ setInstallVersion(version);
49
+ setInstallMode("rollback");
50
+ };
51
+
52
+ const handleEnableToggle = async (row: IndexRow) => {
53
+ setBusyName(row.name);
54
+ try {
55
+ await client.enable({ name: row.name, enabled: row.enabled !== true });
56
+ index.retry();
57
+ }
58
+ finally {
59
+ setBusyName(null);
60
+ }
61
+ };
62
+
63
+ const handleUninstallConfirmed = async (row: IndexRow) => {
64
+ setBusyName(row.name);
65
+ try {
66
+ await client.uninstall(row.name);
67
+ setUninstallRow(null);
68
+ index.retry();
69
+ }
70
+ finally {
71
+ setBusyName(null);
72
+ }
73
+ };
74
+
75
+ const handleInstallDone = (_rec: TaskRecord) => {
76
+ index.retry();
77
+ };
78
+
79
+ return (
80
+ <div style={{ padding: 24 }} data-testid="market-app">
81
+ <Typography.Title level={4}>{t("oj-market:card:title")}</Typography.Title>
82
+ <Typography.Paragraph type="secondary">{t("oj-market:card:subtitle")}</Typography.Paragraph>
83
+
84
+ <CardGrid
85
+ loading={index.status === "loading"}
86
+ error={index.status === "error" ? index.error : null}
87
+ rows={rows}
88
+ busyName={busyName}
89
+ onInstall={handleInstall}
90
+ onEnableToggle={handleEnableToggle}
91
+ onDetails={r => setDetailRow(r)}
92
+ onHistory={r => setHistoryRow(r)}
93
+ onUninstall={r => setUninstallRow(r)}
94
+ onRollback={handleRollback}
95
+ onRetry={index.retry}
96
+ />
97
+
98
+ <DetailDrawer
99
+ row={detailRow}
100
+ open={detailRow != null}
101
+ onClose={() => setDetailRow(null)}
102
+ onInstall={handleInstall}
103
+ onHistory={(r) => {
104
+ setDetailRow(null);
105
+ setHistoryRow(r);
106
+ }}
107
+ onRollback={handleRollback}
108
+ />
109
+
110
+ <InstallDialog
111
+ row={installRow}
112
+ open={installRow != null}
113
+ onClose={() => setInstallRow(null)}
114
+ onDone={handleInstallDone}
115
+ version={installVersion}
116
+ mode={installMode}
117
+ />
118
+
119
+ <UninstallConfirm
120
+ row={uninstallRow}
121
+ open={uninstallRow != null}
122
+ onClose={() => setUninstallRow(null)}
123
+ onConfirmed={handleUninstallConfirmed}
124
+ />
125
+
126
+ <RollbackDialog
127
+ row={rollbackRow}
128
+ open={rollbackRow != null}
129
+ onClose={() => setRollbackRow(null)}
130
+ onRollback={handleRollbackChosen}
131
+ />
132
+
133
+ <HistoryDrawer row={historyRow} open={historyRow != null} onClose={() => setHistoryRow(null)} />
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1,84 @@
1
+ // 市场领域类型(形状对齐宿主 _platform 编排面真源 + 本仓 market_history 表)。
2
+ // 真源:templates/api/src/_platform/_lifecycle/types.ts:47 (TaskRecord) 与 market/api.ts:IndexRow。
3
+ // 任何形状漂移以 templates 源码为准——实现前已核对(2026-10-17)。
4
+
5
+ /** 安装阶段名(契约附录 A 十阶段逐字 + lock_commit/audit/done 终态) */
6
+ export type InstallStage = "preflight" | "downloading" | "verifying" | "extracting" | "migrating" | "lock_commit" | "registering" | "deploying_web" | "reloading" | "smoke" | "audit" | "done";
7
+
8
+ /** 任务状态(§10.2 三态 + partial:锁后失败部分生效) */
9
+ export type TaskStatus = "running" | "done" | "failed" | "partial";
10
+
11
+ /** 阶段明细日志(UI 详情日志区数据源) */
12
+ export interface TaskStageLog {
13
+ stage: InstallStage
14
+ ok: boolean
15
+ message: string
16
+ }
17
+
18
+ /** 失败/partial 终态:reason + retryable + 指引文案 key(契约附录 A) */
19
+ export interface TaskError {
20
+ reason: string
21
+ retryable: boolean
22
+ hintKey?: string
23
+ }
24
+
25
+ /** 安装任务记录(§10.2 + stages 明细日志;落 .install-tasks/<id>.json,800ms 轮询消费面) */
26
+ export interface TaskRecord {
27
+ id: string
28
+ name: string
29
+ version: string
30
+ status: TaskStatus
31
+ stage: InstallStage | null
32
+ /** 0-100 整体进度 */
33
+ progress: number
34
+ message: string
35
+ stages: TaskStageLog[]
36
+ error?: TaskError
37
+ startedAt: number
38
+ finishedAt?: number
39
+ appliedSeq?: number
40
+ }
41
+
42
+ /** 卡片数据源行(GET /_platform/market/index):CDN bundles.yaml ⊕ 本地 sidecar state */
43
+ export interface IndexRow {
44
+ name: string
45
+ latest: string | null
46
+ installed: string | null
47
+ enabled: boolean | null
48
+ upgradable: boolean
49
+ }
50
+
51
+ /** 安装输入(POST /_platform/market/install) */
52
+ export interface InstallInput {
53
+ name: string
54
+ version?: string | null
55
+ source?: "cdn" | "file"
56
+ }
57
+
58
+ /** 启用/停用输入(POST /_platform/market/enable) */
59
+ export interface EnableInput {
60
+ name: string
61
+ enabled: boolean
62
+ }
63
+
64
+ /** 市场操作历史动作 */
65
+ export type HistoryAction = "install" | "uninstall" | "enable" | "disable" | "upgrade" | "rollback";
66
+
67
+ /** 市场操作历史记录(本仓 market_history 表,只读消费) */
68
+ export interface MarketHistory {
69
+ id: number
70
+ solution: string
71
+ version: string
72
+ action: HistoryAction
73
+ status: string
74
+ operator: string | null
75
+ message: string | null
76
+ created_at: number
77
+ }
78
+
79
+ /** 卸载影响面预检(P3 U1 语义;服务端返回供前端二次确认) */
80
+ export interface UninstallImpact {
81
+ name: string
82
+ affectedRoles: number
83
+ affectedUsers: number
84
+ }
@@ -0,0 +1,36 @@
1
+ // 通用轮询工具(§10.2:市场任务 800ms 轮询、退避上限 5s、超时 30min 由调用方配置)。
2
+ // 纯函数、无副作用、可独立单测。
3
+
4
+ export interface PollOptions {
5
+ /** 轮询间隔(默认 800ms) */
6
+ intervalMs?: number
7
+ /** 整体超时(默认 30min) */
8
+ timeoutMs?: number
9
+ /** 中断信号 */
10
+ signal?: AbortSignal
11
+ }
12
+
13
+ /**
14
+ * 反复执行 fn,直到 isDone(value) 为真或超时。
15
+ * 超时返回最近一次取值(由调用方标记超时态),不抛错——避免轮询中断业务。
16
+ */
17
+ export async function poll<T>(
18
+ fn: () => Promise<T>,
19
+ isDone: (value: T) => boolean,
20
+ opts: PollOptions = {},
21
+ ): Promise<T> {
22
+ const interval = opts.intervalMs ?? 800;
23
+ const timeout = opts.timeoutMs ?? 30 * 60 * 1000;
24
+ const start = Date.now();
25
+ // 轮询循环:每轮先取最新值,终态即返回,超时则返回当前值
26
+ for (;;) {
27
+ if (opts.signal?.aborted)
28
+ throw new DOMException("aborted", "AbortError");
29
+ const value = await fn();
30
+ if (isDone(value))
31
+ return value;
32
+ if (Date.now() - start > timeout)
33
+ return value;
34
+ await new Promise<void>(resolve => setTimeout(resolve, interval));
35
+ }
36
+ }
@@ -0,0 +1,14 @@
1
+ // 版本比较(镜像 oj-module _lifecycle/semver 的最小口径,前端本地副本——宿主 semver 在
2
+ // 服务端隔离层,前端不可 import;点分/连字符预发布字段逐段数值比)。返回 <0 / 0 / >0。
3
+ // 用途:双版本卡片 upgradable 对比展示 + 回滚候选过滤(候选 = 版本 < 已装版本)。
4
+ export function compareVersions(a: string, b: string): number {
5
+ const pa = String(a).split(/[.\-]/).map(x => Number.parseInt(x, 10) || 0);
6
+ const pb = String(b).split(/[.\-]/).map(x => Number.parseInt(x, 10) || 0);
7
+ const n = Math.max(pa.length, pb.length);
8
+ for (let i = 0; i < n; i++) {
9
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
10
+ if (d !== 0)
11
+ return d < 0 ? -1 : 1;
12
+ }
13
+ return 0;
14
+ }
@@ -0,0 +1,80 @@
1
+ import type { AppRouteRecordRaw, ModuleDefinition } from "@oj-module/runtime";
2
+ import { AntDesignOutlined, ContainerOutlined } from "@ant-design/icons";
3
+
4
+ import { EmbeddedIcon, ExternalIcon, Iframe, OutsidePageIcon, RiReactjsLine } from "@oj-module/runtime";
5
+ import { createElement } from "react";
6
+ import { Outlet } from "react-router";
7
+
8
+ const routes: AppRouteRecordRaw[] = [
9
+ {
10
+ path: "/outside",
11
+ handle: {
12
+ layout: "container",
13
+ icon: createElement(OutsidePageIcon),
14
+ title: "outside:menu.outside",
15
+ order: 40,
16
+ },
17
+ children: [
18
+ {
19
+ path: "/outside/embedded",
20
+ Component: Outlet,
21
+ handle: {
22
+ icon: createElement(EmbeddedIcon),
23
+ title: "outside:menu.embedded",
24
+ },
25
+ children: [
26
+ {
27
+ path: "/outside/embedded/ant-design",
28
+ Component: Iframe,
29
+ handle: {
30
+ icon: createElement(AntDesignOutlined),
31
+ title: "outside:menu.antd",
32
+ iframeLink: "https://ant.design/",
33
+ },
34
+ },
35
+ {
36
+ path: "/outside/embedded/project-docs",
37
+ Component: Iframe,
38
+ handle: {
39
+ icon: createElement(ContainerOutlined),
40
+ title: "outside:menu.projectDocs",
41
+ iframeLink: "https://condorheroblog.github.io/react-antd-admin/docs/",
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ {
47
+ path: "/outside/external-link",
48
+ Component: Outlet,
49
+ handle: {
50
+ icon: createElement(ExternalIcon),
51
+ title: "outside:menu.externalLink",
52
+ },
53
+ children: [
54
+ {
55
+ path: "/outside/external-link/react-docs",
56
+ Component: Iframe,
57
+ handle: {
58
+ icon: createElement(RiReactjsLine),
59
+ title: "outside:menu.reactDocs",
60
+ externalLink: "https://react.dev/",
61
+ },
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ },
67
+ ];
68
+
69
+ const mod: ModuleDefinition = {
70
+ name: "outside",
71
+ description: "外部链接模块",
72
+ version: "1.0.0",
73
+ routes,
74
+ i18n: {
75
+ "zh-CN": () => import("./locales/zh-CN.json"),
76
+ "en-US": () => import("./locales/en-US.json"),
77
+ },
78
+ };
79
+
80
+ export default mod;
@@ -0,0 +1,10 @@
1
+ {
2
+ "menu": {
3
+ "outside": "External Page",
4
+ "embedded": "Embedded",
5
+ "externalLink": "External Link",
6
+ "antd": "Ant Design",
7
+ "projectDocs": "Project Docs",
8
+ "reactDocs": "React Docs"
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "menu": {
3
+ "outside": "外部页面",
4
+ "embedded": "内嵌",
5
+ "externalLink": "外链",
6
+ "antd": "Ant Design",
7
+ "projectDocs": "项目文档",
8
+ "reactDocs": "React 文档"
9
+ }
10
+ }
@@ -1,10 +1,9 @@
1
1
  import type { AppRouteRecordRaw, ModuleDefinition, UploadApiProvider } from "@oj-module/runtime";
2
- import { ProfileCardIcon, RiAccountCircleLine, RiUserSettingsLine, useAuthStore } from "@oj-module/runtime";
2
+ import { ProfileCardIcon, RiAccountCircleLine, useAuthStore } from "@oj-module/runtime";
3
3
 
4
4
  import { createElement, lazy } from "react";
5
5
 
6
6
  const MyProfile = lazy(() => import("./pages/my-profile"));
7
- const Settings = lazy(() => import("./pages/settings"));
8
7
 
9
8
  const routes: AppRouteRecordRaw[] = [
10
9
  {
@@ -24,14 +23,6 @@ const routes: AppRouteRecordRaw[] = [
24
23
  icon: createElement(ProfileCardIcon),
25
24
  },
26
25
  },
27
- {
28
- path: "/personal-center/settings",
29
- Component: Settings,
30
- handle: {
31
- title: "personal-center:menu.settings",
32
- icon: createElement(RiUserSettingsLine),
33
- },
34
- },
35
26
  ],
36
27
  },
37
28
  ];
@@ -1,10 +1,6 @@
1
1
  {
2
- "accountSecurity": "Account Security",
3
- "generalSettings": "General Settings",
4
- "notificationSettings": "Notification Settings",
5
2
  "menu": {
6
3
  "personalCenter": "Personal Center",
7
- "profile": "My Profile",
8
- "settings": "Settings"
4
+ "profile": "My Profile"
9
5
  }
10
6
  }
@@ -1,10 +1,6 @@
1
1
  {
2
- "accountSecurity": "账户安全",
3
- "generalSettings": "通用设置",
4
- "notificationSettings": "通知设置",
5
2
  "menu": {
6
3
  "personalCenter": "个人中心",
7
- "profile": "我的资料",
8
- "settings": "设置"
4
+ "profile": "我的资料"
9
5
  }
10
6
  }
@@ -0,0 +1,73 @@
1
+ import type { AppRouteRecordRaw, ModuleDefinition } from "@oj-module/runtime";
2
+
3
+ import {
4
+ NodeExpandOutlined,
5
+ SisternodeOutlined,
6
+ SubnodeOutlined,
7
+ } from "@ant-design/icons";
8
+ import { createElement, lazy } from "react";
9
+
10
+ const Menu1And1 = lazy(() => import("./pages/menu1/menu1-1"));
11
+ const Menu1And2 = lazy(() => import("./pages/menu1/menu1-2"));
12
+ const Menu2 = lazy(() => import("./pages/menu2"));
13
+
14
+ const routes: AppRouteRecordRaw[] = [
15
+ {
16
+ path: "/route-nest",
17
+ handle: {
18
+ order: 20,
19
+ title: "route-nest:menu.nestMenus",
20
+ icon: createElement(NodeExpandOutlined),
21
+ layout: "container",
22
+ },
23
+ children: [
24
+ {
25
+ path: "/route-nest/menu1",
26
+ handle: {
27
+ title: "route-nest:menu.menu1",
28
+ icon: createElement(SisternodeOutlined),
29
+ layout: "parent",
30
+ },
31
+ children: [
32
+ {
33
+ path: "/route-nest/menu1/menu1-1",
34
+ Component: Menu1And1,
35
+ handle: {
36
+ title: "route-nest:menu.menu1-1",
37
+ icon: createElement(SubnodeOutlined),
38
+ },
39
+ },
40
+ {
41
+ path: "/route-nest/menu1/menu1-2",
42
+ Component: Menu1And2,
43
+ handle: {
44
+ title: "route-nest:menu.menu1-2",
45
+ icon: createElement(SubnodeOutlined),
46
+ },
47
+ },
48
+ ],
49
+ },
50
+ {
51
+ path: "/route-nest/menu2",
52
+ Component: Menu2,
53
+ handle: {
54
+ title: "route-nest:menu.menu2",
55
+ icon: createElement(SubnodeOutlined),
56
+ },
57
+ },
58
+ ],
59
+ },
60
+ ];
61
+
62
+ const mod: ModuleDefinition = {
63
+ name: "route-nest",
64
+ description: "嵌套路由模块",
65
+ version: "1.0.0",
66
+ routes,
67
+ i18n: {
68
+ "zh-CN": () => import("./locales/zh-CN.json"),
69
+ "en-US": () => import("./locales/en-US.json"),
70
+ },
71
+ };
72
+
73
+ export default mod;
@@ -0,0 +1,9 @@
1
+ {
2
+ "menu": {
3
+ "nestMenus": "Nest Menus",
4
+ "menu1": "Menu1",
5
+ "menu1-1": "Menu1-1",
6
+ "menu1-2": "Menu1-2",
7
+ "menu2": "Menu2"
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "menu": {
3
+ "nestMenus": "嵌套菜单",
4
+ "menu1": "菜单 1",
5
+ "menu1-1": "菜单 1-1",
6
+ "menu1-2": "菜单 1-2",
7
+ "menu2": "菜单 2"
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ import { BasicContent } from "@oj-module/runtime";
2
+
3
+ export default function Menu1And1() {
4
+ return <BasicContent>menu1-1</BasicContent>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { BasicContent } from "@oj-module/runtime";
2
+
3
+ export default function Menu1And2() {
4
+ return <BasicContent>menu1-2</BasicContent>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { BasicContent } from "@oj-module/runtime";
2
+
3
+ export default function Menu2() {
4
+ return <BasicContent>menu2</BasicContent>;
5
+ }