@oj-module/cli 0.2.68 → 0.2.71

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 (153) hide show
  1. package/devkit/README.md +10 -8
  2. package/devkit/framework-dev.md +1 -1
  3. package/devkit/manual.md +45 -19
  4. package/devkit/module-dev-and-cdn-playground.md +20 -14
  5. package/devkit/publish-walkthrough.md +132 -43
  6. package/devkit/wecom-customer-reauth-guide.md +110 -0
  7. package/package.json +2 -2
  8. package/shell-dist/assets/{runtime.1xzahckt10.js → runtime.7x99hmm020.js} +2 -2
  9. package/shell-dist/bundles/oj-market-0.2.0.tgz +0 -0
  10. package/shell-dist/bundles/oj-market-0.2.0.tgz.sig +8 -0
  11. package/shell-dist/index.html +4 -4
  12. package/shell-dist/versions.json +3 -3
  13. package/src/__tests__/list.test.ts +7 -7
  14. package/src/__tests__/oj-health-host.test.ts +19 -0
  15. package/src/__tests__/site-tenant-header.test.ts +2 -2
  16. package/src/__tests__/templates-builtin-list.test.ts +47 -0
  17. package/src/__tests__/templates-schema-columns.test.ts +11 -2
  18. package/src/__tests__/templates-tenant-scope.test.ts +2 -3
  19. package/src/__tests__/users-roles-display.test.ts +84 -0
  20. package/src/bundle.ts +1 -1
  21. package/src/contract/__tests__/market-client.integration.test.ts +23 -22
  22. package/src/enable.ts +2 -2
  23. package/src/init.ts +45 -0
  24. package/src/install.ts +5 -5
  25. package/src/lifecycle/__tests__/install.test.ts +29 -29
  26. package/src/lifecycle/__tests__/market-bundle.test.ts +87 -0
  27. package/src/lifecycle/__tests__/publish.test.ts +8 -10
  28. package/src/lifecycle/__tests__/trust.test.ts +14 -14
  29. package/src/lifecycle/__tests__/uninstall.test.ts +4 -4
  30. package/src/list.ts +3 -3
  31. package/src/market/__tests__/bootstrap.test.ts +24 -7
  32. package/src/market/__tests__/failure-playbook.test.ts +0 -1
  33. package/src/market/__tests__/remote-push.test.ts +228 -0
  34. package/src/market/__tests__/verify.test.ts +45 -207
  35. package/src/market/bootstrap.ts +12 -7
  36. package/src/market/command.ts +33 -35
  37. package/src/market/db-seed.ts +129 -0
  38. package/src/market/failure-playbook.ts +3 -9
  39. package/src/market/fixture.ts +2 -6
  40. package/src/market/publish.ts +145 -89
  41. package/src/market/remote-push.ts +236 -0
  42. package/src/market/trial.ts +2 -2
  43. package/src/market/verify.ts +17 -270
  44. package/src/market/walkthrough.ts +38 -63
  45. package/src/oj.ts +13 -2
  46. package/src/preview.ts +5 -3
  47. package/src/publish.ts +3 -2
  48. package/src/scaffold-guard.ts +4 -0
  49. package/src/site.ts +1 -1
  50. package/src/trust.ts +8 -8
  51. package/src/uninstall.ts +2 -2
  52. package/src/usage.ts +23 -21
  53. package/templates/config.yaml +22 -3
  54. package/templates/global.d.ts +6 -4
  55. package/templates/init.include.yaml +16 -11
  56. package/templates/openapi.json +3971 -3663
  57. package/templates/src/.ojm-builtin.json +13 -0
  58. package/templates/src/README.md +5 -2
  59. package/templates/src/app/api/_platform/README.md +3 -2
  60. package/templates/src/app/api/_platform/_lifecycle/atomic.ts +7 -2
  61. package/templates/src/app/api/_platform/_lifecycle/bundle-converge.ts +158 -0
  62. package/templates/src/app/api/_platform/_lifecycle/history.ts +3 -2
  63. package/templates/src/app/api/_platform/_lifecycle/installer.ts +25 -5
  64. package/templates/src/app/api/_platform/_lifecycle/local-archive.ts +61 -0
  65. package/templates/src/app/api/_platform/_lifecycle/manifest-of-upload.ts +62 -0
  66. package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +12 -2
  67. package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +4 -1
  68. package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +8 -2
  69. package/templates/src/app/api/_platform/_lifecycle/types.ts +1 -1
  70. package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +14 -5
  71. package/templates/src/app/api/_platform/_shared/service/domain/users.ts +7 -1
  72. package/templates/src/app/api/_platform/_shared/site-facade.ts +31 -0
  73. package/templates/src/app/api/_platform/manifest.yaml +1 -2
  74. package/templates/src/app/api/_platform/migrations/0003__market_catalog.mysql.sql +6 -0
  75. package/templates/src/app/api/_platform/migrations/0003__market_catalog.pg.sql +6 -0
  76. package/templates/src/app/api/_platform/migrations/0003__market_catalog.sql +18 -0
  77. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.mysql.sql +6 -0
  78. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.pg.sql +6 -0
  79. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.sql +6 -0
  80. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.mysql.sql +8 -0
  81. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.pg.sql +8 -0
  82. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.sql +8 -0
  83. package/templates/src/app/api/_platform/schema.yaml +8 -11
  84. package/templates/src/app/api/_platform/seed.mysql.sql +1 -1
  85. package/templates/src/app/api/_platform/seed.pg.sql +1 -1
  86. package/templates/src/app/api/_platform/seed.sql +1 -1
  87. package/templates/src/app/api/auth/_shared/wecom/sync.ts +6 -0
  88. package/templates/src/app/api/system/users/api.ts +3 -0
  89. package/templates/src/app/web/_platform/__client/api.schemas.ts +0 -113
  90. package/templates/src/app/web/_platform/__client/index.ts +1 -333
  91. package/templates/src/app/web/system/__client/api.schemas.ts +2 -0
  92. package/templates/src/app/web/system/locales/en-US.json +15 -1
  93. package/templates/src/app/web/system/locales/zh-CN.json +15 -1
  94. package/templates/src/app/web/system/pages/departments/sync-bar.tsx +9 -3
  95. package/templates/src/app/web/system/pages/roles/index.tsx +4 -8
  96. package/templates/src/app/web/system/pages/roles/role-label.ts +26 -0
  97. package/templates/src/app/web/system/pages/solutions/index.tsx +110 -0
  98. package/templates/src/app/web/system/pages/solutions/market-entry.ts +46 -0
  99. package/templates/src/app/web/system/pages/users/index.tsx +12 -2
  100. package/templates/src/app/web/system/pages/users/match-keyword.ts +19 -0
  101. package/templates/src/app/web/system/pages/users/roles.tsx +43 -14
  102. package/templates/src/app/web/system/routes.ts +18 -1
  103. package/templates/src/{app/api/_platform/market → oj-market/api}/api.ts +475 -592
  104. package/templates/src/oj-market/api/backfill.ts +221 -0
  105. package/templates/src/{app/api/_platform/market → oj-market/api}/bootstrap.ts +2 -2
  106. package/templates/src/oj-market/api/cdn/api.ts +312 -0
  107. package/templates/src/oj-market/api/cdn/shared.ts +104 -0
  108. package/templates/src/oj-market/api/cdn/signed-auth.ts +75 -0
  109. package/templates/src/oj-market/api/manifest.yaml +8 -0
  110. package/templates/src/oj-market/solution.yaml +21 -0
  111. package/templates/src/oj-market/web/__client/api.schemas.ts +167 -0
  112. package/templates/src/oj-market/web/__client/index.ts +509 -0
  113. package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +6 -6
  114. package/templates/src/oj-market/web/api/client.ts +6 -6
  115. package/templates/src/oj-market/web/api/defaultClient.ts +31 -25
  116. package/templates/src/oj-market/web/entry.ts +5 -3
  117. package/templates/src/oj-market/web/hooks/useMarket.ts +6 -6
  118. package/templates/src/oj-market/web/pages/MarketApp.tsx +20 -3
  119. package/templates/src/oj-market/web/publish/locales/en-US.json +1 -1
  120. package/templates/src/oj-market/web/publish/locales/zh-CN.json +1 -1
  121. package/templates/src/oj-market/web/publish/pages/history.tsx +2 -11
  122. package/templates/src/oj-market/web/routes.ts +12 -0
  123. package/templates/src/oj-market/web/types.ts +14 -8
  124. package/templates/tests/lifecycle/anonymous-wiring.spec.ts +65 -0
  125. package/templates/tests/lifecycle/backfill.spec.ts +392 -0
  126. package/templates/tests/lifecycle/bootstrap.spec.ts +1 -1
  127. package/templates/tests/lifecycle/bundle-converge.spec.ts +264 -0
  128. package/templates/tests/lifecycle/cdn-upload.spec.ts +661 -0
  129. package/templates/tests/lifecycle/deploy-smoke.spec.ts +25 -0
  130. package/templates/tests/lifecycle/exec-grant.spec.ts +38 -0
  131. package/templates/tests/lifecycle/installer.spec.ts +36 -0
  132. package/templates/tests/lifecycle/market-api.spec.ts +448 -803
  133. package/templates/tests/lifecycle/preflight.spec.ts +18 -0
  134. package/templates/tests/lifecycle/trust.spec.ts +2 -2
  135. package/templates/tests/lifecycle/uninstall.spec.ts +36 -2
  136. package/templates/tests/lifecycle/verify-checks.spec.ts +1 -1
  137. package/templates/tests/rbac-users.spec.ts +8 -0
  138. package/templates/tests/solutions-entry.spec.ts +55 -0
  139. package/templates/tsconfig.paths.json +1 -0
  140. package/templates/types/stream-probes/stream-handlers.ts +34 -0
  141. package/templates/web.config.ts +6 -6
  142. package/vendor/host-versions.json +3 -3
  143. package/bin/oj +0 -4
  144. package/src/market/__tests__/build-index.test.ts +0 -641
  145. package/src/market/__tests__/sharding.test.ts +0 -90
  146. package/src/market/__tests__/yank-ledger.test.ts +0 -60
  147. package/src/market/build-index.ts +0 -526
  148. package/src/market/index-builder.ts +0 -149
  149. package/src/market/sharding.ts +0 -94
  150. package/src/market/yank-ledger.ts +0 -91
  151. package/templates/src/app/api/_platform/_lifecycle/source-seq.ts +0 -88
  152. package/templates/src/app/api/_platform/market/index-source.ts +0 -112
  153. package/templates/tests/lifecycle/source-seq.spec.ts +0 -55
@@ -0,0 +1,509 @@
1
+ /* eslint-disable */
2
+ // 生成物:ojm api 从契约生成,勿手改(改动请改契约文件后重跑 ojm api)
3
+ import { ContractApiError } from "@oj-module/runtime/contract/errors";
4
+ import type { ScopedRequestLike } from "@oj-module/runtime/contract/errors";
5
+ import type { ModuleContext } from "@oj-module/runtime";
6
+ import type { z } from "@oj-module/runtime";
7
+ import type { schemas } from "./api.schemas";
8
+
9
+ /** oj 信封(AC-D16):code=0 成功;非 0 时 HTTP status=code,由 toApiError 归一为 ContractApiError */
10
+ interface OjEnvelope<T> { code: number, msg?: string, data?: T }
11
+
12
+ let req: ScopedRequestLike | undefined;
13
+
14
+ /** 线上 API 前缀(ojm api 从契约抽取,唯一真源,勿手改):请求基址 + 前缀登记用 */
15
+ export const API_PREFIX = "/oj-market";
16
+
17
+ /** 认领身份前缀(契约 apiPrefix,字面等于模块目录名):跨模块认领护栏对比基准 */
18
+ export const MODULE_PREFIX = "/oj-market";
19
+
20
+ /**
21
+ * 模块 entry 的 onInit 里一行创建(构造即 install):完成 apiPrefix 登记 +
22
+ * scoped request 绑定,返回绑好前缀的端点函数集合。一模块一 client;
23
+ * 重复构造幂等安全(前缀重复登记同值、request 重绑同值)。
24
+ */
25
+ export function createOjMarketClient(ctx: ModuleContext) {
26
+ // 跨模块认领护栏(DEV):工厂知道自己该属于谁(MODULE_PREFIX = 契约 apiPrefix),
27
+ // ctx 又带 module.name——名实不符说明别的模块在替本模块创建 client,会共享 request
28
+ // 单槽、卸载互相耦合;跨模块需求应由本模块创建后经 provider 暴露。
29
+ // 注意对比基准是 MODULE_PREFIX(目录名)而非 API_PREFIX——D-M8 映射时线上前缀
30
+ // 属于别的命名空间,认领身份仍按目录名。
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 暴露。`);
33
+ }
34
+ ctx.register.apiPrefix(API_PREFIX);
35
+ bindRequest(ctx.utils.request);
36
+ return { ojMarketInstallPost, ojMarketInstallCancelPost, ojMarketTaskGet, ojMarketEnablePost, ojMarketMetaGet, ojMarketBackfillPost, ojMarketTrustGet, ojMarketTrustPost, ojMarketTrustKeyPost, ojMarketTrustKeyDelete, ojMarketIndexGet, ojMarketCatalogGet, ojMarketCatalogDetailGet, ojMarketYankPost, ojMarketUninstallImpactGet, ojMarketUninstallPost, ojMarketHistoryGet, ojMarketReleasesGet, ojMarketCdnUploadPost };
37
+ }
38
+
39
+ /**
40
+ * @deprecated 请改用 createOjMarketClient(ctx)——工厂构造即完成
41
+ * 前缀登记 + request 绑定,不会出现「登记了前缀没绑 request」的半接线状态。
42
+ */
43
+ export function bindRequest(r: ScopedRequestLike): void {
44
+ req = r;
45
+ }
46
+
47
+ function ensureReq(): ScopedRequestLike {
48
+ if (!req)
49
+ throw new ContractApiError(-1, "[ojm-api] 请求未绑定——请在模块 entry.ts 的 onInit 里调用 createOjMarketClient(ctx)(或 bindRequest(ctx.utils.request))。");
50
+ return req;
51
+ }
52
+
53
+ /** ky HTTPError → ContractApiError(错误体为信封时取 code/msg);契约违例与原错误原样透传 */
54
+ async function toApiError(e: unknown): Promise<unknown> {
55
+ if (e instanceof ContractApiError)
56
+ return e;
57
+ // ky 2.x 构造 HTTPError 时已用 getReader() 消费掉原 response 体(res.clone().json() 按规范抛 TypeError,
58
+ // 体方法一律失效),解析好的错误体在公开字段 error.data——故读 error.data,勿改回 clone().json()。
59
+ const err = e as { data?: { code?: unknown, msg?: string }, response?: Response } | null;
60
+ const res = err?.response;
61
+ const d = err?.data;
62
+ if (res instanceof Response && d && typeof d.code === "number")
63
+ return new ContractApiError(d.code, d.msg ?? res.statusText);
64
+ return e;
65
+ }
66
+
67
+ export type OjMarketInstallPostBody = z.input<(typeof schemas)["ojMarketInstallPost"]["body"]>;
68
+ export type OjMarketInstallPostData = z.infer<(typeof schemas)["ojMarketInstallPost"]["data"]>;
69
+
70
+ export async function ojMarketInstallPost(body: OjMarketInstallPostBody): Promise<OjMarketInstallPostData> {
71
+ const client = ensureReq();
72
+ try {
73
+ const env = await client.post(`oj-market/install`, { json: body }).json<OjEnvelope<OjMarketInstallPostData>>();
74
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
75
+ if (typeof env.code === "number" && env.code !== 0)
76
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
77
+ const data = env.data as OjMarketInstallPostData;
78
+ if (import.meta.env.DEV) {
79
+ const { schemas } = await import("./api.schemas");
80
+ const r = schemas["ojMarketInstallPost"].data.safeParse(data);
81
+ if (!r.success)
82
+ throw new ContractApiError(-1, `[契约违例] ojMarketInstallPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
83
+ }
84
+ return data;
85
+ }
86
+ catch (e) {
87
+ throw await toApiError(e);
88
+ }
89
+ }
90
+
91
+ export type OjMarketInstallCancelPostBody = z.input<(typeof schemas)["ojMarketInstallCancelPost"]["body"]>;
92
+ export type OjMarketInstallCancelPostData = z.infer<(typeof schemas)["ojMarketInstallCancelPost"]["data"]>;
93
+
94
+ export async function ojMarketInstallCancelPost(body: OjMarketInstallCancelPostBody): Promise<OjMarketInstallCancelPostData> {
95
+ const client = ensureReq();
96
+ try {
97
+ const env = await client.post(`oj-market/install/cancel`, { json: body }).json<OjEnvelope<OjMarketInstallCancelPostData>>();
98
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
99
+ if (typeof env.code === "number" && env.code !== 0)
100
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
101
+ const data = env.data as OjMarketInstallCancelPostData;
102
+ if (import.meta.env.DEV) {
103
+ const { schemas } = await import("./api.schemas");
104
+ const r = schemas["ojMarketInstallCancelPost"].data.safeParse(data);
105
+ if (!r.success)
106
+ throw new ContractApiError(-1, `[契约违例] ojMarketInstallCancelPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
107
+ }
108
+ return data;
109
+ }
110
+ catch (e) {
111
+ throw await toApiError(e);
112
+ }
113
+ }
114
+
115
+ export type OjMarketTaskGetQuery = z.input<(typeof schemas)["ojMarketTaskGet"]["query"]>;
116
+ export type OjMarketTaskGetData = z.infer<(typeof schemas)["ojMarketTaskGet"]["data"]>;
117
+
118
+ export async function ojMarketTaskGet(query: OjMarketTaskGetQuery): Promise<OjMarketTaskGetData> {
119
+ const client = ensureReq();
120
+ try {
121
+ const env = await client.get(`oj-market/task`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketTaskGetData>>();
122
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
123
+ if (typeof env.code === "number" && env.code !== 0)
124
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
125
+ const data = env.data as OjMarketTaskGetData;
126
+ if (import.meta.env.DEV) {
127
+ const { schemas } = await import("./api.schemas");
128
+ const r = schemas["ojMarketTaskGet"].data.safeParse(data);
129
+ if (!r.success)
130
+ throw new ContractApiError(-1, `[契约违例] ojMarketTaskGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
131
+ }
132
+ return data;
133
+ }
134
+ catch (e) {
135
+ throw await toApiError(e);
136
+ }
137
+ }
138
+
139
+ export type OjMarketEnablePostBody = z.input<(typeof schemas)["ojMarketEnablePost"]["body"]>;
140
+ export type OjMarketEnablePostData = z.infer<(typeof schemas)["ojMarketEnablePost"]["data"]>;
141
+
142
+ export async function ojMarketEnablePost(body: OjMarketEnablePostBody): Promise<OjMarketEnablePostData> {
143
+ const client = ensureReq();
144
+ try {
145
+ const env = await client.post(`oj-market/enable`, { json: body }).json<OjEnvelope<OjMarketEnablePostData>>();
146
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
147
+ if (typeof env.code === "number" && env.code !== 0)
148
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
149
+ const data = env.data as OjMarketEnablePostData;
150
+ if (import.meta.env.DEV) {
151
+ const { schemas } = await import("./api.schemas");
152
+ const r = schemas["ojMarketEnablePost"].data.safeParse(data);
153
+ if (!r.success)
154
+ throw new ContractApiError(-1, `[契约违例] ojMarketEnablePost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
155
+ }
156
+ return data;
157
+ }
158
+ catch (e) {
159
+ throw await toApiError(e);
160
+ }
161
+ }
162
+
163
+ export type OjMarketMetaGetData = z.infer<(typeof schemas)["ojMarketMetaGet"]["data"]>;
164
+
165
+ export async function ojMarketMetaGet(): Promise<OjMarketMetaGetData> {
166
+ const client = ensureReq();
167
+ try {
168
+ const env = await client.get(`oj-market/meta`).json<OjEnvelope<OjMarketMetaGetData>>();
169
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
170
+ if (typeof env.code === "number" && env.code !== 0)
171
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
172
+ const data = env.data as OjMarketMetaGetData;
173
+ if (import.meta.env.DEV) {
174
+ const { schemas } = await import("./api.schemas");
175
+ const r = schemas["ojMarketMetaGet"].data.safeParse(data);
176
+ if (!r.success)
177
+ throw new ContractApiError(-1, `[契约违例] ojMarketMetaGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
178
+ }
179
+ return data;
180
+ }
181
+ catch (e) {
182
+ throw await toApiError(e);
183
+ }
184
+ }
185
+
186
+ export async function ojMarketBackfillPost(): Promise<unknown> {
187
+ const client = ensureReq();
188
+ try {
189
+ const env = await client.post(`oj-market/backfill`).json<OjEnvelope<unknown>>();
190
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
191
+ if (typeof env.code === "number" && env.code !== 0)
192
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
193
+ const data = env.data as unknown;
194
+ return data;
195
+ }
196
+ catch (e) {
197
+ throw await toApiError(e);
198
+ }
199
+ }
200
+
201
+ export type OjMarketTrustGetData = z.infer<(typeof schemas)["ojMarketTrustGet"]["data"]>;
202
+
203
+ export async function ojMarketTrustGet(): Promise<OjMarketTrustGetData> {
204
+ const client = ensureReq();
205
+ try {
206
+ const env = await client.get(`oj-market/trust`).json<OjEnvelope<OjMarketTrustGetData>>();
207
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
208
+ if (typeof env.code === "number" && env.code !== 0)
209
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
210
+ const data = env.data as OjMarketTrustGetData;
211
+ if (import.meta.env.DEV) {
212
+ const { schemas } = await import("./api.schemas");
213
+ const r = schemas["ojMarketTrustGet"].data.safeParse(data);
214
+ if (!r.success)
215
+ throw new ContractApiError(-1, `[契约违例] ojMarketTrustGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
216
+ }
217
+ return data;
218
+ }
219
+ catch (e) {
220
+ throw await toApiError(e);
221
+ }
222
+ }
223
+
224
+ export type OjMarketTrustPostBody = z.input<(typeof schemas)["ojMarketTrustPost"]["body"]>;
225
+ export type OjMarketTrustPostData = z.infer<(typeof schemas)["ojMarketTrustPost"]["data"]>;
226
+
227
+ export async function ojMarketTrustPost(body: OjMarketTrustPostBody): Promise<OjMarketTrustPostData> {
228
+ const client = ensureReq();
229
+ try {
230
+ const env = await client.post(`oj-market/trust`, { json: body }).json<OjEnvelope<OjMarketTrustPostData>>();
231
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
232
+ if (typeof env.code === "number" && env.code !== 0)
233
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
234
+ const data = env.data as OjMarketTrustPostData;
235
+ if (import.meta.env.DEV) {
236
+ const { schemas } = await import("./api.schemas");
237
+ const r = schemas["ojMarketTrustPost"].data.safeParse(data);
238
+ if (!r.success)
239
+ throw new ContractApiError(-1, `[契约违例] ojMarketTrustPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
240
+ }
241
+ return data;
242
+ }
243
+ catch (e) {
244
+ throw await toApiError(e);
245
+ }
246
+ }
247
+
248
+ export type OjMarketTrustKeyPostBody = z.input<(typeof schemas)["ojMarketTrustKeyPost"]["body"]>;
249
+ export type OjMarketTrustKeyPostData = z.infer<(typeof schemas)["ojMarketTrustKeyPost"]["data"]>;
250
+
251
+ export async function ojMarketTrustKeyPost(body: OjMarketTrustKeyPostBody): Promise<OjMarketTrustKeyPostData> {
252
+ const client = ensureReq();
253
+ try {
254
+ const env = await client.post(`oj-market/trust/key`, { json: body }).json<OjEnvelope<OjMarketTrustKeyPostData>>();
255
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
256
+ if (typeof env.code === "number" && env.code !== 0)
257
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
258
+ const data = env.data as OjMarketTrustKeyPostData;
259
+ if (import.meta.env.DEV) {
260
+ const { schemas } = await import("./api.schemas");
261
+ const r = schemas["ojMarketTrustKeyPost"].data.safeParse(data);
262
+ if (!r.success)
263
+ throw new ContractApiError(-1, `[契约违例] ojMarketTrustKeyPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
264
+ }
265
+ return data;
266
+ }
267
+ catch (e) {
268
+ throw await toApiError(e);
269
+ }
270
+ }
271
+
272
+ export type OjMarketTrustKeyDeleteQuery = z.input<(typeof schemas)["ojMarketTrustKeyDelete"]["query"]>;
273
+ export type OjMarketTrustKeyDeleteData = z.infer<(typeof schemas)["ojMarketTrustKeyDelete"]["data"]>;
274
+
275
+ export async function ojMarketTrustKeyDelete(query: OjMarketTrustKeyDeleteQuery): Promise<OjMarketTrustKeyDeleteData> {
276
+ const client = ensureReq();
277
+ try {
278
+ const env = await client.delete(`oj-market/trust/key`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketTrustKeyDeleteData>>();
279
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
280
+ if (typeof env.code === "number" && env.code !== 0)
281
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
282
+ const data = env.data as OjMarketTrustKeyDeleteData;
283
+ if (import.meta.env.DEV) {
284
+ const { schemas } = await import("./api.schemas");
285
+ const r = schemas["ojMarketTrustKeyDelete"].data.safeParse(data);
286
+ if (!r.success)
287
+ throw new ContractApiError(-1, `[契约违例] ojMarketTrustKeyDelete 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
288
+ }
289
+ return data;
290
+ }
291
+ catch (e) {
292
+ throw await toApiError(e);
293
+ }
294
+ }
295
+
296
+ export type OjMarketIndexGetQuery = z.input<(typeof schemas)["ojMarketIndexGet"]["query"]>;
297
+ export type OjMarketIndexGetData = z.infer<(typeof schemas)["ojMarketIndexGet"]["data"]>;
298
+
299
+ export async function ojMarketIndexGet(query: OjMarketIndexGetQuery): Promise<OjMarketIndexGetData> {
300
+ const client = ensureReq();
301
+ try {
302
+ const env = await client.get(`oj-market/index`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketIndexGetData>>();
303
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
304
+ if (typeof env.code === "number" && env.code !== 0)
305
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
306
+ const data = env.data as OjMarketIndexGetData;
307
+ if (import.meta.env.DEV) {
308
+ const { schemas } = await import("./api.schemas");
309
+ const r = schemas["ojMarketIndexGet"].data.safeParse(data);
310
+ if (!r.success)
311
+ throw new ContractApiError(-1, `[契约违例] ojMarketIndexGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
312
+ }
313
+ return data;
314
+ }
315
+ catch (e) {
316
+ throw await toApiError(e);
317
+ }
318
+ }
319
+
320
+ export type OjMarketCatalogGetQuery = z.input<(typeof schemas)["ojMarketCatalogGet"]["query"]>;
321
+ export type OjMarketCatalogGetData = z.infer<(typeof schemas)["ojMarketCatalogGet"]["data"]>;
322
+
323
+ export async function ojMarketCatalogGet(query: OjMarketCatalogGetQuery): Promise<OjMarketCatalogGetData> {
324
+ const client = ensureReq();
325
+ try {
326
+ const env = await client.get(`oj-market/catalog`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketCatalogGetData>>();
327
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
328
+ if (typeof env.code === "number" && env.code !== 0)
329
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
330
+ const data = env.data as OjMarketCatalogGetData;
331
+ if (import.meta.env.DEV) {
332
+ const { schemas } = await import("./api.schemas");
333
+ const r = schemas["ojMarketCatalogGet"].data.safeParse(data);
334
+ if (!r.success)
335
+ throw new ContractApiError(-1, `[契约违例] ojMarketCatalogGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
336
+ }
337
+ return data;
338
+ }
339
+ catch (e) {
340
+ throw await toApiError(e);
341
+ }
342
+ }
343
+
344
+ export type OjMarketCatalogDetailGetQuery = z.input<(typeof schemas)["ojMarketCatalogDetailGet"]["query"]>;
345
+ export type OjMarketCatalogDetailGetData = z.infer<(typeof schemas)["ojMarketCatalogDetailGet"]["data"]>;
346
+
347
+ export async function ojMarketCatalogDetailGet(query: OjMarketCatalogDetailGetQuery): Promise<OjMarketCatalogDetailGetData> {
348
+ const client = ensureReq();
349
+ try {
350
+ const env = await client.get(`oj-market/catalog/detail`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketCatalogDetailGetData>>();
351
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
352
+ if (typeof env.code === "number" && env.code !== 0)
353
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
354
+ const data = env.data as OjMarketCatalogDetailGetData;
355
+ if (import.meta.env.DEV) {
356
+ const { schemas } = await import("./api.schemas");
357
+ const r = schemas["ojMarketCatalogDetailGet"].data.safeParse(data);
358
+ if (!r.success)
359
+ throw new ContractApiError(-1, `[契约违例] ojMarketCatalogDetailGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
360
+ }
361
+ return data;
362
+ }
363
+ catch (e) {
364
+ throw await toApiError(e);
365
+ }
366
+ }
367
+
368
+ export type OjMarketYankPostBody = z.input<(typeof schemas)["ojMarketYankPost"]["body"]>;
369
+ export type OjMarketYankPostData = z.infer<(typeof schemas)["ojMarketYankPost"]["data"]>;
370
+
371
+ export async function ojMarketYankPost(body: OjMarketYankPostBody): Promise<OjMarketYankPostData> {
372
+ const client = ensureReq();
373
+ try {
374
+ const env = await client.post(`oj-market/yank`, { json: body }).json<OjEnvelope<OjMarketYankPostData>>();
375
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
376
+ if (typeof env.code === "number" && env.code !== 0)
377
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
378
+ const data = env.data as OjMarketYankPostData;
379
+ if (import.meta.env.DEV) {
380
+ const { schemas } = await import("./api.schemas");
381
+ const r = schemas["ojMarketYankPost"].data.safeParse(data);
382
+ if (!r.success)
383
+ throw new ContractApiError(-1, `[契约违例] ojMarketYankPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
384
+ }
385
+ return data;
386
+ }
387
+ catch (e) {
388
+ throw await toApiError(e);
389
+ }
390
+ }
391
+
392
+ export type OjMarketUninstallImpactGetQuery = z.input<(typeof schemas)["ojMarketUninstallImpactGet"]["query"]>;
393
+ export type OjMarketUninstallImpactGetData = z.infer<(typeof schemas)["ojMarketUninstallImpactGet"]["data"]>;
394
+
395
+ export async function ojMarketUninstallImpactGet(query: OjMarketUninstallImpactGetQuery): Promise<OjMarketUninstallImpactGetData> {
396
+ const client = ensureReq();
397
+ try {
398
+ const env = await client.get(`oj-market/uninstall-impact`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketUninstallImpactGetData>>();
399
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
400
+ if (typeof env.code === "number" && env.code !== 0)
401
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
402
+ const data = env.data as OjMarketUninstallImpactGetData;
403
+ if (import.meta.env.DEV) {
404
+ const { schemas } = await import("./api.schemas");
405
+ const r = schemas["ojMarketUninstallImpactGet"].data.safeParse(data);
406
+ if (!r.success)
407
+ throw new ContractApiError(-1, `[契约违例] ojMarketUninstallImpactGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
408
+ }
409
+ return data;
410
+ }
411
+ catch (e) {
412
+ throw await toApiError(e);
413
+ }
414
+ }
415
+
416
+ export type OjMarketUninstallPostBody = z.input<(typeof schemas)["ojMarketUninstallPost"]["body"]>;
417
+ export type OjMarketUninstallPostData = z.infer<(typeof schemas)["ojMarketUninstallPost"]["data"]>;
418
+
419
+ export async function ojMarketUninstallPost(body: OjMarketUninstallPostBody): Promise<OjMarketUninstallPostData> {
420
+ const client = ensureReq();
421
+ try {
422
+ const env = await client.post(`oj-market/uninstall`, { json: body }).json<OjEnvelope<OjMarketUninstallPostData>>();
423
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
424
+ if (typeof env.code === "number" && env.code !== 0)
425
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
426
+ const data = env.data as OjMarketUninstallPostData;
427
+ if (import.meta.env.DEV) {
428
+ const { schemas } = await import("./api.schemas");
429
+ const r = schemas["ojMarketUninstallPost"].data.safeParse(data);
430
+ if (!r.success)
431
+ throw new ContractApiError(-1, `[契约违例] ojMarketUninstallPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
432
+ }
433
+ return data;
434
+ }
435
+ catch (e) {
436
+ throw await toApiError(e);
437
+ }
438
+ }
439
+
440
+ export type OjMarketHistoryGetData = z.infer<(typeof schemas)["ojMarketHistoryGet"]["data"]>;
441
+
442
+ export async function ojMarketHistoryGet(): Promise<OjMarketHistoryGetData> {
443
+ const client = ensureReq();
444
+ try {
445
+ const env = await client.get(`oj-market/history`).json<OjEnvelope<OjMarketHistoryGetData>>();
446
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
447
+ if (typeof env.code === "number" && env.code !== 0)
448
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
449
+ const data = env.data as OjMarketHistoryGetData;
450
+ if (import.meta.env.DEV) {
451
+ const { schemas } = await import("./api.schemas");
452
+ const r = schemas["ojMarketHistoryGet"].data.safeParse(data);
453
+ if (!r.success)
454
+ throw new ContractApiError(-1, `[契约违例] ojMarketHistoryGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
455
+ }
456
+ return data;
457
+ }
458
+ catch (e) {
459
+ throw await toApiError(e);
460
+ }
461
+ }
462
+
463
+ export type OjMarketReleasesGetQuery = z.input<(typeof schemas)["ojMarketReleasesGet"]["query"]>;
464
+ export type OjMarketReleasesGetData = z.infer<(typeof schemas)["ojMarketReleasesGet"]["data"]>;
465
+
466
+ export async function ojMarketReleasesGet(query: OjMarketReleasesGetQuery): Promise<OjMarketReleasesGetData> {
467
+ const client = ensureReq();
468
+ try {
469
+ const env = await client.get(`oj-market/releases`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketReleasesGetData>>();
470
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
471
+ if (typeof env.code === "number" && env.code !== 0)
472
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
473
+ const data = env.data as OjMarketReleasesGetData;
474
+ if (import.meta.env.DEV) {
475
+ const { schemas } = await import("./api.schemas");
476
+ const r = schemas["ojMarketReleasesGet"].data.safeParse(data);
477
+ if (!r.success)
478
+ throw new ContractApiError(-1, `[契约违例] ojMarketReleasesGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
479
+ }
480
+ return data;
481
+ }
482
+ catch (e) {
483
+ throw await toApiError(e);
484
+ }
485
+ }
486
+
487
+ export type OjMarketCdnUploadPostQuery = z.input<(typeof schemas)["ojMarketCdnUploadPost"]["query"]>;
488
+ export type OjMarketCdnUploadPostData = z.infer<(typeof schemas)["ojMarketCdnUploadPost"]["data"]>;
489
+
490
+ export async function ojMarketCdnUploadPost(query: OjMarketCdnUploadPostQuery): Promise<OjMarketCdnUploadPostData> {
491
+ const client = ensureReq();
492
+ try {
493
+ const env = await client.post(`oj-market/cdn/upload`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<OjMarketCdnUploadPostData>>();
494
+ // 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
495
+ if (typeof env.code === "number" && env.code !== 0)
496
+ throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
497
+ const data = env.data as OjMarketCdnUploadPostData;
498
+ if (import.meta.env.DEV) {
499
+ const { schemas } = await import("./api.schemas");
500
+ const r = schemas["ojMarketCdnUploadPost"].data.safeParse(data);
501
+ if (!r.success)
502
+ throw new ContractApiError(-1, `[契约违例] ojMarketCdnUploadPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
503
+ }
504
+ return data;
505
+ }
506
+ catch (e) {
507
+ throw await toApiError(e);
508
+ }
509
+ }
@@ -1,9 +1,9 @@
1
1
  import type { MarketClient } from "../api/client";
2
2
  // 内存实现 MarketClient(BDD 测试注入用,无需真实后端)。
3
- import type { EnableInput, IndexRow, InstallInput, MarketHistory, ReleasePage, TaskRecord, UninstallImpact } from "../types";
3
+ import type { EnableInput, IndexPage, InstallInput, MarketHistory, ReleasePage, TaskRecord, UninstallImpact } from "../types";
4
4
 
5
5
  export interface FakeState {
6
- indexRows?: IndexRow[]
6
+ indexPage?: IndexPage
7
7
  indexError?: Error
8
8
  taskRecord?: TaskRecord
9
9
  historyRows?: MarketHistory[]
@@ -24,11 +24,11 @@ export class InMemoryMarketClient implements MarketClient {
24
24
  return { apiVersion: "0.1.0" };
25
25
  }
26
26
 
27
- async index() {
28
- this.calls.push("index");
27
+ async index(input: { page?: number, size?: number } = {}) {
28
+ this.calls.push(`index:${input.page ?? 1}:${input.size ?? 20}`);
29
29
  if (this.state.indexError)
30
30
  throw this.state.indexError;
31
- return this.state.indexRows ?? [];
31
+ return this.state.indexPage ?? { list: [], total: 0, page: 1, size: 20 };
32
32
  }
33
33
 
34
34
  async install(input: InstallInput) {
@@ -68,7 +68,7 @@ export class InMemoryMarketClient implements MarketClient {
68
68
 
69
69
  async releases(input: { page?: number, limit?: number } = {}) {
70
70
  this.calls.push(`releases:${input.page ?? 1}:${input.limit ?? 20}`);
71
- return this.state.releasePage ?? { list: [], total: 0, page: 1, size: 20, syncError: null };
71
+ return this.state.releasePage ?? { list: [], total: 0, page: 1, size: 20 };
72
72
  }
73
73
 
74
74
  async uninstallImpact(name: string) {
@@ -1,13 +1,13 @@
1
1
  // 市场 API 客户端契约(DIP seams):组件/hooks 依赖此接口,而非具体 http 实现;
2
2
  // 测试可注入内存 fake(见 __tests__/marketClient.fake.ts)。
3
- // 第一方专用调用面(R29):仅市场应用消费 /api/_platform/market/*,第三方方案不得仿照。
3
+ // 第一方专用调用面(R29):仅市场应用消费 /api/oj-market/*,第三方方案不得仿照。
4
4
  //
5
5
  // 实现见同目录 defaultClient.ts——统一委托给 ojm 从契约生成的 __client,消除手写
6
6
  // URL/信封解包与契约漂移(DEV 下生成 client 还会对响应做 zod 校验)。
7
7
 
8
8
  import type {
9
9
  EnableInput,
10
- IndexRow,
10
+ IndexPage,
11
11
  InstallInput,
12
12
  MarketHistory,
13
13
  ReleasePage,
@@ -19,8 +19,8 @@ import type {
19
19
  export interface MarketClient {
20
20
  /** 登录即可:{ apiVersion }(peerPlatform 预检口径) */
21
21
  meta: () => Promise<{ apiVersion: string }>
22
- /** 卡片数据源(superadmin) */
23
- index: () => Promise<IndexRow[]>
22
+ /** 卡片数据源(superadmin;v4 §四 DB 目录分页,行字段不变) */
23
+ index: (input?: { page?: number, size?: number }) => Promise<IndexPage>
24
24
  /** 触发安装,返回轮询用 task_id */
25
25
  install: (input: InstallInput) => Promise<{ task_id: string }>
26
26
  /** 任务轮询消费面(query 形态 ?id=) */
@@ -32,9 +32,9 @@ export interface MarketClient {
32
32
  trust: (taskId: string) => Promise<{ result: string, key_id: string, fingerprint: string }>
33
33
  /** 生命周期开关(同步 setEnabled,无任务模型;{ok,message} 200 渲染 ok 位) */
34
34
  enable: (input: EnableInput) => Promise<{ ok: boolean, message: string }>
35
- /** 市场操作历史(_platform 只读端点 /_platform/market/history) */
35
+ /** 市场操作历史(_platform 只读端点 /oj-market/history) */
36
36
  history: () => Promise<MarketHistory[]>
37
- /** 发布历史(登录即可读;服务端每次读先与签名索引对账) */
37
+ /** 发布历史(登录即可读;v4 §四 直读 market_release 上传行,零索引依赖) */
38
38
  releases: (input?: { page?: number, limit?: number }) => Promise<ReleasePage>
39
39
  /** 卸载影响面预检(P3 U1;服务端返回供二次确认,P1-15a 补齐后接通) */
40
40
  uninstallImpact: (name: string) => Promise<UninstallImpact>