@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
@@ -372,4 +372,22 @@ describe("p0 §3.2 质辩1 / E7·E8 匿名保留段与残留检测(写锁前
372
372
  const r = await preflight(ctx({ manifest: m, existingAnonymousPaths: ["/_platform/health", "/auth/login"] }));
373
373
  expect(r.ok).toBe(true);
374
374
  });
375
+
376
+ it("e8 第一方编排段 oj-market:config 出厂匿名条目(catalog/upload/yank)属脚手架内建编排面,非方案残留 → 放行", async () => {
377
+ // Phase B 演练实锤(2026-11-07):市场编排面随脚手架内置(config 出厂即匿名
378
+ // /oj-market/catalog|cdn/upload|yank),第一方纯 web 包(无 api 段、无 anonymous
379
+ // 声明)收敛/首装会整段撞 anonymous_residual——编排段条目与 /auth/* 同为宿主内建。
380
+ const m = JSON.parse(JSON.stringify(VALID).replaceAll("demo-solution", "oj-market")) as typeof VALID;
381
+ delete m.api;
382
+ const r = await preflight(ctx({ manifest: m, existingAnonymousPaths: ["/oj-market/catalog/**", "/oj-market/cdn/upload", "/oj-market/yank"] }));
383
+ expect(r.ok, r.message).toBe(true);
384
+ });
385
+
386
+ it("e8 非编排段的同 prefix 残留仍拒(豁免不扩散)", async () => {
387
+ const m = JSON.parse(JSON.stringify(VALID).replaceAll("demo-solution", "other-solution")) as typeof VALID;
388
+ delete m.api;
389
+ const r = await preflight(ctx({ manifest: m, existingAnonymousPaths: ["/other-solution/legacy"] }));
390
+ expect(r.ok).toBe(false);
391
+ expect(r.code).toBe("anonymous_residual");
392
+ });
375
393
  });
@@ -3,7 +3,7 @@
3
3
  // 端点层:GET /market/trust、POST /market/trust/key、DELETE /market/trust/key?key_id=(del 动词 + query,system/roles 同款先例)。
4
4
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(market-api.spec 同约定) */
5
5
  import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
6
- import type { MarketEnv } from "../../src/app/api/_platform/market/api";
6
+ import type { MarketEnv } from "../../src/oj-market/api/api";
7
7
  import { Buffer } from "node:buffer";
8
8
  import { createHash } from "node:crypto";
9
9
  import * as fsp from "node:fs/promises";
@@ -12,7 +12,7 @@ import { join } from "node:path";
12
12
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
13
13
  import { createTrustStore, isValidKeyId } from "../../src/app/api/_platform/_lifecycle/trust";
14
14
  import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
15
- import { createMarketApi, marketEnv } from "../../src/app/api/_platform/market/api";
15
+ import { createMarketApi, marketEnv } from "../../src/oj-market/api/api";
16
16
  import { installDb } from "../fixture";
17
17
 
18
18
  const g = globalThis as any;
@@ -6,7 +6,7 @@
6
6
  // 0012(IF NOT EXISTS)随 installDb 重放建立,守卫语义(表不存在静默跳过)单列一例。
7
7
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(market-api.spec 同约定) */
8
8
  import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
9
- import type { MarketEnv } from "../../src/app/api/_platform/market/api";
9
+ import type { MarketEnv } from "../../src/oj-market/api/api";
10
10
  import * as fsp from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import { dirname, join } from "node:path";
@@ -15,7 +15,7 @@ import { readLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock
15
15
  import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
16
16
  import { collectRetainedTables, moduleTablePrefix } from "../../src/app/api/_platform/_lifecycle/uninstall";
17
17
  import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
18
- import { createMarketApi } from "../../src/app/api/_platform/market/api";
18
+ import { createMarketApi } from "../../src/oj-market/api/api";
19
19
  import { installDb } from "../fixture";
20
20
 
21
21
  const g = globalThis as any;
@@ -165,6 +165,40 @@ describe("POST uninstall 卸载编排(superadmin,R9)", () => {
165
165
  expect(String(hist[0].operator)).toBe("admin");
166
166
  });
167
167
 
168
+ it("双轨同名(web-only 方案 vs 同名内建模块锁条目):版本以 sidecar 为准——只删方案版本目录,内建锁条目与目录保留", async () => {
169
+ // 2026-11-07 市场线演练实锤:版本解析「锁优先」会把内建 oj-market 编排模块
170
+ // (锁 oj-market: 0.1.0,平台 api 轨)当成方案版本删——内建目录被移除、编排端点全灭。
171
+ // web-only 方案的真相在 sidecar(lock_commit 零锁变更),锁是 api 轨真源与方案版本可能同名不同轨。
172
+ await writeLock(join(sharedRoot, "manifests.yaml"), { "oj-market": "0.1.0", "other": "1.0.0" });
173
+ const st0 = await createStateStore(statePath);
174
+ await st0.write({ solutions: { "oj-market": { version: "0.2.0", enabled: true, installedAt: 1 } } });
175
+ await fsp.mkdir(join(sharedRoot, "oj-market-0.1.0"), { recursive: true }); // 内建编排模块目录(不可删)
176
+ await fsp.mkdir(join(sharedRoot, "oj-market-0.2.0"), { recursive: true }); // 方案版本目录(该删)
177
+ await fsp.writeFile(join(sharedRoot, "oj-market-0.2.0", "manifest.yaml"), "name: oj-market\nversion: 0.2.0\n");
178
+ await fsp.writeFile(modulesJsonPath, JSON.stringify([{ name: "oj-market", enabled: true }, { name: "other", enabled: true }]));
179
+ dbo.prepare("INSERT INTO sys_solution_route (path, module, category) VALUES (?, ?, ?)").run("/oj-market", "oj-market", "main");
180
+ const api = createMarketApi(makeEnv({
181
+ apiPrefix: "/api/oj-market",
182
+ // 双轨同名:内建锁条目保留 → oj 装配面无锁 diff,reload 报 unchanged 属预期
183
+ // (方案摘除发生在 modules.json/sidecar/route 面;断言不得按「卸载空转」拒)
184
+ reload: { reloadConfig: async () => ({ generation: 2, unchanged: true, applied: [], restart_pending: [], kept_listener: [] }) },
185
+ }));
186
+ g.http = { user: { id: 1 }, body: { name: "oj-market" } };
187
+ await api.uninstall.post();
188
+ expect(last?.status).toBe(200);
189
+ expect(last?.data).toMatchObject({ ok: true });
190
+ // 内建锁条目保留(api 轨不被方案卸载波及),other 亦保留
191
+ expect(await readLock(join(sharedRoot, "manifests.yaml"))).toEqual({ "oj-market": "0.1.0", "other": "1.0.0" });
192
+ // 方案版本目录删除;内建目录完好
193
+ await expect(fsp.readdir(join(sharedRoot, "oj-market-0.2.0"))).rejects.toBeTruthy();
194
+ await fsp.access(join(sharedRoot, "oj-market-0.1.0"));
195
+ // sidecar 删条目 + modules.json 删方案条目(other 保留)
196
+ expect((await createStateStore(statePath)).read().solutions["oj-market"]).toBeUndefined();
197
+ const list = JSON.parse(await fsp.readFile(modulesJsonPath, "utf8")) as Array<{ name: string }>;
198
+ expect(list.find(e => e.name === "oj-market")).toBeUndefined();
199
+ expect(list.find(e => e.name === "other")).toBeDefined();
200
+ });
201
+
168
202
  it("未安装方案 → 404 资源缺失语义(P3-11:非 200 {ok:false}),人话在 message", async () => {
169
203
  const api = createMarketApi(makeEnv());
170
204
  g.http = { user: { id: 1 }, body: { name: "nope" } };
@@ -19,8 +19,8 @@ import {
19
19
 
20
20
  verifyRouteReachability,
21
21
  } from "../../src/app/api/_platform/_lifecycle/verify-checks";
22
- import { siteOrigin } from "../../src/app/api/_platform/market/api";
23
22
  import { createSolutionsApi, solutionsEnv } from "../../src/app/api/_platform/solutions/api";
23
+ import { siteOrigin } from "../../src/oj-market/api/api";
24
24
  import { installDb } from "../fixture";
25
25
 
26
26
  const g = globalThis as any;
@@ -213,4 +213,12 @@ describe("B7:列表(用户管理页数据源)", () => {
213
213
  expect(u2?.roles).toEqual(["editor"]);
214
214
  expect(JSON.stringify(users)).not.toContain("password_hash");
215
215
  });
216
+ it("U3:listUsers 返回 employee_no / phone_number(users 表既有列;未设置即 null)", async () => {
217
+ const uid = mkUser("u_emp");
218
+ dbo.prepare("UPDATE users SET employee_no = ?, phone_number = ? WHERE id = ?").run("E001", "13800000000", uid);
219
+ mkUser("u_emp2");
220
+ const users = await listUsers();
221
+ expect(users.find(u => u.username === "u_emp")).toMatchObject({ employee_no: "E001", phone_number: "13800000000" });
222
+ expect(users.find(u => u.username === "u_emp2")).toMatchObject({ employee_no: null, phone_number: null });
223
+ });
216
224
  });
@@ -0,0 +1,55 @@
1
+ // BDD:system 域「方案安装」入口(Phase B T3)——市场 UI 出厂未装,superadmin 经
2
+ // /system/solutions 首装。页面本身只做 query + 渲染,真正会回归的是状态推导:
3
+ // 目录行有无 / 装没装 / 任务终态映射,故这里测纯函数(roles-page.spec 同范式)。
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ import { marketEntryOf, taskOutcomeOf } from "../src/app/web/system/pages/solutions/market-entry";
7
+
8
+ const ROW = { name: "oj-market", latest: "0.2.0", installed: null as string | null, upgradable: false };
9
+
10
+ describe("方案安装入口:市场包状态推导", () => {
11
+ it("目录无 oj-market 行 → absent(出厂未装且目录未回填)", () => {
12
+ expect(marketEntryOf([])).toEqual({ kind: "absent" });
13
+ expect(marketEntryOf([{ ...ROW, name: "wiki" }])).toEqual({ kind: "absent" });
14
+ });
15
+
16
+ it("目录有行未装 → installable(携带目录最新版)", () => {
17
+ expect(marketEntryOf([ROW])).toEqual({ kind: "installable", latest: "0.2.0" });
18
+ });
19
+
20
+ it("已装 → installed(携带已装版/目录最新版/可升级位)", () => {
21
+ expect(marketEntryOf([{ ...ROW, installed: "0.2.0" }])).toEqual({ kind: "installed", installed: "0.2.0", latest: "0.2.0", upgradable: false });
22
+ });
23
+
24
+ it("已装且目录有新版 → upgradable=true(市场内升级提示)", () => {
25
+ expect(marketEntryOf([{ ...ROW, latest: "0.3.0", installed: "0.2.0", upgradable: true }])).toEqual({ kind: "installed", installed: "0.2.0", latest: "0.3.0", upgradable: true });
26
+ });
27
+
28
+ it("不改入参(useQuery 缓存对象不能被就地改写)", () => {
29
+ const list = [ROW, { ...ROW, name: "wiki" }];
30
+ const before = list.map(r => r.name);
31
+ marketEntryOf(list);
32
+ expect(list.map(r => r.name)).toEqual(before);
33
+ });
34
+ });
35
+
36
+ describe("方案安装入口:任务终态映射(needs_trust 是非成功态)", () => {
37
+ const REC = { status: "running", progress: 40, message: "deploying" };
38
+
39
+ it("running → running(携带进度与阶段消息)", () => {
40
+ expect(taskOutcomeOf(REC)).toEqual({ kind: "running", progress: 40, message: "deploying" });
41
+ });
42
+
43
+ it("done → done", () => {
44
+ expect(taskOutcomeOf({ ...REC, status: "done" })).toEqual({ kind: "done" });
45
+ });
46
+
47
+ it("needs_trust → needsTrust(发布方 key 待 TOFU 确认——不得按成功渲染)", () => {
48
+ expect(taskOutcomeOf({ ...REC, status: "needs_trust" })).toEqual({ kind: "needsTrust" });
49
+ });
50
+
51
+ it("failed/partial → failed(人话取任务 message)", () => {
52
+ expect(taskOutcomeOf({ ...REC, status: "failed", message: "验签失败" })).toEqual({ kind: "failed", message: "验签失败" });
53
+ expect(taskOutcomeOf({ ...REC, status: "partial", message: "锁后失败" })).toEqual({ kind: "failed", message: "锁后失败" });
54
+ });
55
+ });
@@ -10,6 +10,7 @@
10
10
  "#/auth/*": ["./src/app/api/auth/*"],
11
11
  "#/home/*": ["./src/app/api/home/*"],
12
12
  "#/notification/*": ["./src/app/api/notification/*"],
13
+ "#/oj-market/*": ["./src/oj-market/api/*"],
13
14
  "#/personal-center/*": ["./src/app/api/personal-center/*"],
14
15
  "#/publish/*": ["./src/app/api/publish/*"],
15
16
  "#/system/*": ["./src/app/api/system/*"],
@@ -0,0 +1,34 @@
1
+ // U2 类型回归探针:钉住 global.d.ts 的流式响应类型与 oj 运行时/devkit 对齐。
2
+ //
3
+ // 本文件**不是路由**(oj 只认 `api.ts`/`ws.ts` 为端点),也不是 src/ 下的模块
4
+ // (ojm api --check 要求 src/<dir> 至少一侧是模块,不能放 src 内);放在 types/
5
+ // 是因为 api 侧 tsconfig(src/tsconfig.json)已 include "../types" 而 web 侧没有——
6
+ // 恰好只被 typecheck:api 覆盖(见根 tests/cli/typecheck-coverage.test.ts 的并/交不变量)。
7
+ // global.d.ts 的
8
+ // stream/sse opts 或 StreamWriter.write 一旦被改回旧签名(缺 fileName/disposition、
9
+ // write 不收 Uint8Array),本文件即产生 TS 错误。tests/templates/ 下的
10
+ // stream-types 用例负责在 CI 里真正跑这次 tsc。
11
+ // 探针刻意同时覆盖:文本流(string 通道)与二进制流(Uint8Array 字节通道)。
12
+
13
+ // 文本流:CSV 导出——status/contentType/fileName/disposition 全量使用
14
+ export function probeTextStream(): void {
15
+ const writer = json.stream({ status: 200, contentType: "text/csv", fileName: "report.csv", disposition: "attachment" });
16
+ writer.write("a,b\n");
17
+ writer.write("1,2\n");
18
+ writer.end();
19
+ }
20
+
21
+ // 二进制流:附件下载——非 ASCII 文件名 + 字节通道
22
+ export function probeBinaryStream(bytes: Uint8Array): void {
23
+ const writer = json.stream({ contentType: "application/octet-stream", fileName: "导出.zip" });
24
+ writer.write(bytes);
25
+ writer.end();
26
+ }
27
+
28
+ // 内联展示 + SSE 的 fileName/disposition 透传(oj devkit 同签名)
29
+ export function probeInlineAndSse(): void {
30
+ json.stream({ contentType: "image/png", fileName: "图.png", disposition: "inline" }).end();
31
+ const sse = json.sse({ heartbeatSecs: 5, fileName: "events.txt", disposition: "attachment" });
32
+ sse.write("tick");
33
+ sse.end();
34
+ }
@@ -18,12 +18,12 @@ const allModules = [
18
18
  // 各根按 handle.order 排序(/home 1、/personal-center 110、/system -100 底部),
19
19
  // 与拆分时的菜单逐项一致;/login、/change-password、/notification-center 走 hideInMenu。
20
20
  { name: "app", entry: "src/app/web/entry.ts" },
21
- // 解决方案市场:第一方能力(消费 _platform 编排面 /api/_platform/market/*,
22
- // R29:第三方方案不得仿照此调用面),默认随脚手架下发。保持独立模块——
23
- // 可选能力,不想要就改 `enabled: false`(不要删目录:src/app、src/oj-market
24
- // 受完整性门禁保护,删改会让 ojm build 失败,见 src/README.md);
25
- // 发布管理页(/publish)也在这个模块里。
26
- { name: "oj-market", entry: "src/oj-market/web/entry.ts" },
21
+ // 解决方案市场(Phase B T2 起):UI 为纯 web 方案包(src/oj-market/solution.yaml,
22
+ // 出厂未装),superadmin 经 system 域首装后由安装器供给——故本清单不再登记
23
+ // oj-market。模板工程里它仍被 src/ 扫描捡进构建(打包源),用户工程则没有
24
+ // web/entry.ts(白名单只下发 __client 生成物),登记了就是死条目。api 编排面
25
+ // (/api/oj-market/*,R29 第三方不得仿照)随脚手架内置不受影响;发布管理页
26
+ // (/publish)随方案包走,clientOwners 里 publish 归属 oj-market 不变。
27
27
  // #region demo-only
28
28
  // 以下为模板内置的演示模块(about/access/exception/outside/route-nest),
29
29
  // 用于把 templates 当可运行 demo 直接跑;其文件不在 `ojm init` 的
@@ -8,9 +8,9 @@
8
8
  "react-router": "7.18.2",
9
9
  "react-router/dom": "7.18.2",
10
10
  "@tanstack/react-query": "5.102.8",
11
- "@oj-module/runtime": "0.2.68",
12
- "@oj-module/runtime/contract": "0.2.68",
13
- "@oj-module/runtime/contract/errors": "0.2.68",
11
+ "@oj-module/runtime": "0.2.71",
12
+ "@oj-module/runtime/contract": "0.2.71",
13
+ "@oj-module/runtime/contract/errors": "0.2.71",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",
package/bin/oj DELETED
@@ -1,4 +0,0 @@
1
- [diffend] Oversized file quarantined before diffing.
2
- name: package/bin/oj
3
- size: 126034368 bytes
4
- sha256: 6774d629abecdd96a84bed71ed220ce85ae46bbbe9a818819eca87a5b5d50be6