@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
@@ -418,6 +418,31 @@ describe("smoke 探活(D14/Q-A:health 不强制)", () => {
418
418
  expect(r.message).toContain("探活");
419
419
  expect(r.message).toContain("404");
420
420
  });
421
+
422
+ it("纯 web 方案(无 api 段,routes 均为页面路由):探活跳过、零探针请求(2026-11-07 实锤)", async () => {
423
+ // oj-market 0.2.0 重装现场:manifest 无 api 段、routes=[/oj-market](页面路由),
424
+ // 旧逻辑把页面路由塞进 api 前缀探 /api/oj-market → 404 → smoke 失败 + 全量补偿回滚
425
+ // (含 modules.json)→ 重装死循环、市场菜单永久消失。纯 web 包无 api 面可探 = 无事可验视为过。
426
+ await seedDeployed();
427
+ const urls: string[] = [];
428
+ const r = await smoke(smCtx({
429
+ manifest: solManifest({
430
+ api: undefined,
431
+ routes: [{ path: "/oj-market", category: "main", order: 200, title_key: "oj-market:menu.main" }],
432
+ permissions: undefined,
433
+ roles: undefined,
434
+ settings: undefined,
435
+ }),
436
+ query: async () => ({ module: "demo-solution", routes: ["/oj-market"], settings: [], roles: [], permissions: [] }),
437
+ fetch: async (url) => {
438
+ urls.push(url);
439
+ return { ok: false, status: 404 };
440
+ },
441
+ }));
442
+ expect(urls).toEqual([]);
443
+ expect(r.ok).toBe(true);
444
+ expect(r.message).toContain("纯 web");
445
+ });
421
446
  });
422
447
 
423
448
  describe("smoke integrity(R10:部署落位后重算 sha384 vs modules.json)", () => {
@@ -0,0 +1,38 @@
1
+ // exec 授权守卫(2026-11-07 市场线演练实锤):
2
+ // oj 的 exec 按「请求模块上下文」逐模块授权(oj-bin bridge/exec.rs per_module)——
3
+ // 安装器在端点请求内同步跑(解压 tar / oj migrate 都要 exec),所以编排面落在
4
+ // 哪个 api 模块,哪个模块就必须持有与 _platform 完全一致的 allow 清单。
5
+ // Phase B 把市场编排面放在 src/oj-market/api(模块上下文 = oj-market),
6
+ // 而 config 只授权了 _platform → 真机上首装/收敛全部 NotCapable
7
+ // 「module 'oj-market' is not authorized for exec」(单测全注入 exec,绕过了授权层,
8
+ // 演练才暴露)。本守卫钉住:两模块授权同集合同命令、逐字一致。
9
+
10
+ import { readFileSync } from "node:fs";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { describe, expect, it } from "vitest";
14
+ import { parse } from "yaml";
15
+
16
+ const configPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "../../config.yaml");
17
+
18
+ interface ExecGrant { allow?: string[] }
19
+
20
+ function execGrants(): Record<string, ExecGrant> {
21
+ const doc = parse(readFileSync(configPath, "utf8")) as { platform?: { exec?: Record<string, ExecGrant> } };
22
+ return doc.platform?.exec ?? {};
23
+ }
24
+
25
+ describe("platform.exec 逐模块授权", () => {
26
+ it("编排模块 oj-market 与 _platform 同集合同命令(逐字一致)", () => {
27
+ const grants = execGrants();
28
+ expect(Object.keys(grants).sort()).toEqual(["_platform", "oj-market"]);
29
+ expect(grants["oj-market"]?.allow).toEqual(grants._platform?.allow);
30
+ expect(grants._platform?.allow?.length).toBeGreaterThan(0);
31
+ });
32
+
33
+ it("编排模块 oj-market 具 platform 特权(安装器 reloadConfig/信任库写面;迁移自 _platform 编排端点)", () => {
34
+ const doc = parse(readFileSync(configPath, "utf8")) as { platform?: { modules?: string[] } };
35
+ expect(doc.platform?.modules).toContain("_platform");
36
+ expect(doc.platform?.modules).toContain("oj-market");
37
+ });
38
+ });
@@ -259,6 +259,42 @@ describe("runInstall 全绿路径(步骤 stub,register/smoke/audit 走真实
259
259
  expect(await getTask(taskDir, "nope")).toBeNull();
260
260
  });
261
261
 
262
+ it("web-only 方案包(无 api 段,extracting C2-31 放行面):lock_commit 整体归位版本目录后,deploying_web 改从版本目录取 web 树 → done(2026-11-07 市场线演练实锤:staging 被 rename 后部署段读空)", async () => {
263
+ const m = manifestFixture("0.2.0");
264
+ delete m.api;
265
+ m.web = { entry: "modules/demo-solution/0.2.0/entry.js" };
266
+ m.archive = { file: "demo-solution-0.2.0.tgz", sha256: SHA256, size: ARCHIVE.byteLength, signature: signedArchiveFor(ARCHIVE, "demo-solution", "0.2.0", { keyPair: TEST_KEY }).sig };
267
+ // exec stub:web-only 产物树(单根 + 无 api/ 目录——lock_commit 回退「整体 rename」形态,
268
+ // 版本目录 = 包内容;web 树随之离开 staging)
269
+ const exec: ExecFace = {
270
+ run: async (argv) => {
271
+ if (argv[0] === "tar" && argv[1] === "-tvzf")
272
+ return { code: 0, stdout: "demo-solution-0.2.0/manifest.yaml\ndemo-solution-0.2.0/web/modules/demo-solution/0.2.0/entry.js\n", stderr: "" };
273
+ if (argv[0] === "tar" && argv[1] === "-xzf") {
274
+ const dest = argv[4]!;
275
+ const dir = join(dest, "demo-solution-0.2.0", "web", "modules", "demo-solution", "0.2.0");
276
+ await fsp.mkdir(dir, { recursive: true });
277
+ await fsp.writeFile(join(dir, "entry.js"), ENTRY);
278
+ await fsp.writeFile(join(dest, "demo-solution-0.2.0", "manifest.yaml"), new TextEncoder().encode(JSON.stringify(m)));
279
+ return { code: 0, stdout: "", stderr: "" };
280
+ }
281
+ return { code: 0, stdout: "", stderr: "" };
282
+ },
283
+ };
284
+ const t = await runInstall(makeReq({ manifest: m, version: "0.2.0", exec }));
285
+ expect(t.status).toBe("done");
286
+ expect(t.stages.find(s => s.stage === "deploying_web")?.ok).toBe(true);
287
+ // web 树落站点产物根 + 清单 upsert(integrity 为包内容重算值,R10)
288
+ await fsp.access(join(siteModulesDir, "demo-solution", "0.2.0", "entry.js"));
289
+ const list = JSON.parse(await fsp.readFile(modulesJsonPath, "utf8")) as any[];
290
+ expect(list[0]).toMatchObject({ name: "demo-solution", entry: "modules/demo-solution/0.2.0/entry.js", integrity: `sha384-${sha384b64(ENTRY)}` });
291
+ // web-only 包 api 锁零变更:锁是 oj 装配真源(原生七键清单),写锁会让 oj 按 C2 清单装配
292
+ // (reload 400)并遮蔽同名内建模块(2026-11-07 演练实锤:双轨同名 oj-market 锁遮蔽编排面)。
293
+ // 版本目录仍归位(回滚素材/审计形态),生效面 = modules.json upsert + 注册 + sidecar。
294
+ expect(parseLock(await fsp.readFile(lockPath, "utf8").catch(() => ""))).toEqual({});
295
+ await fsp.access(join(sharedRoot, "demo-solution-0.2.0", "manifest.yaml"));
296
+ });
297
+
262
298
  it("preview 形态:派生 .ojm-preview.yaml 存在时两份 config 一起接线(否则 reload 恒 unchanged)", async () => {
263
299
  // ojm preview 派生出来的那份才是服务端在读的 config(spa 挂载 + platform.root 改写)
264
300
  const derived = join(root, ".ojm-preview.yaml");