@oj-module/cli 0.2.68 → 0.2.69

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/cli",
3
3
  "type": "module",
4
- "version": "0.2.68",
4
+ "version": "0.2.69",
5
5
  "description": "React Antd 模块工程命令行工具(含预构建宿主 shell-dist)",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@faker-js/faker": "^10.4.0",
52
- "@oj-module/runtime": "0.2.68",
52
+ "@oj-module/runtime": "0.2.69",
53
53
  "@redocly/cli": "^2.51.0",
54
54
  "@tailwindcss/vite": "^4.3.0",
55
55
  "cac": "^6.7.14",
@@ -200,10 +200,10 @@ function hr() {
200
200
  return {
201
201
  pkg: {
202
202
  name: "@oj-module/runtime",
203
- version: "0.2.68",
203
+ version: "0.2.69",
204
204
  license: "MIT"
205
205
  },
206
- lastBuildTime: "2026-11-06 11:25:48"
206
+ lastBuildTime: "2026-11-06 13:26:56"
207
207
  };
208
208
  }
209
209
  var gr = w((() => {}));
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" href="/favicon.ico" />
7
7
  <title>React Antd Admin · Shell</title>
8
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-36CAd5UpXCtQqxASbhUFfw=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
- <script type="importmap" nonce="36CAd5UpXCtQqxASbhUFfw==">{
8
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-tifpCLPY1fTUzXJs6I+vIg=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
+ <script type="importmap" nonce="tifpCLPY1fTUzXJs6I+vIg==">{
10
10
  "imports": {
11
11
  "react": "/assets/react.1ew63mwts0.js",
12
12
  "react/jsx-runtime": "/assets/jsx-runtime.h40xu4o5b0.js",
@@ -16,7 +16,7 @@
16
16
  "react-router": "/assets/react-router.2h9rebcv50.js",
17
17
  "react-router/dom": "/assets/react-router-dom.3c6zld8h40.js",
18
18
  "@tanstack/react-query": "/assets/react-query.3rln45ow00.js",
19
- "@oj-module/runtime": "/assets/runtime.1xzahckt10.js",
19
+ "@oj-module/runtime": "/assets/runtime.zrv2zhj9j0.js",
20
20
  "@oj-module/runtime/contract": "/assets/contract.3h327o3mj0.js",
21
21
  "@oj-module/runtime/contract/errors": "/assets/contract-errors.2o17vz9u50.js",
22
22
  "@plane/kit": "/assets/kit.37kr8ui390.js",
@@ -79,7 +79,7 @@
79
79
  .boot-skeleton:after{content:"";width:18px;height:18px;margin-left:8px;border:2px solid #d9d9d9;border-top-color:#1677ff;border-radius:50%;animation:bs-spin .8s linear infinite}
80
80
  @keyframes bs-spin{to{transform:rotate(360deg)}}
81
81
  </style>
82
- <script nonce="36CAd5UpXCtQqxASbhUFfw==" data-ojm-preload="/assets/cssinjs.atgmhan7m0.js,/assets/pro-components.2kyot1vcl0.js,/assets/runtime.1xzahckt10.js,/assets/react-query.3rln45ow00.js,/assets/antd.uw1coq15l0.js,/assets/react.1ew63mwts0.js,/assets/react-dom-client.ick0v05ob0.js,/assets/react-i18next.auhvjhqv00.js,/assets/react-router.2h9rebcv50.js,/assets/jsx-runtime.h40xu4o5b0.js,/assets/react-dom.2so9uxg3f0.js,/assets/clsx.jotilrt2e0.js,/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js,/assets/dayjs.pa60l6wjo0.js,/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js,/assets/use-sync-external-store-shim.1nz1pi9bb0.js,/assets/icons.159wjwals0.js,/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js,/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js,/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js,/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js,/assets/dayjs-plugin-localeData.2rwbjp9nk0.js,/assets/dayjs-plugin-weekday.2d56a4tml0.js,/assets/dayjs-plugin-weekOfYear.gvn9iph080.js,/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js,/assets/dnd-kit-core.1xyreig6r0.js,/assets/dnd-kit-utilities.1umw4xphb0.js,/assets/dnd-kit-sortable.s94qciuft0.js,/assets/ky.1cgch7gu60.js,/assets/zustand.3gjgzlnqo0.js,/assets/zustand-middleware.1xaxjueme0.js,/assets/react-error-boundary.2dncdfj3b0.js,/assets/antd-locale-en_US.561tozx860.js,/assets/antd-locale-zh_CN.1cmpmgzh70.js,/assets/i18next.33zqofapj0.js,/assets/tailwind-merge.125ehiqjw0.js,/assets/nprogress.2nz76n9ny0.js,/assets/nprogress-css.1cq2pzkre0.js,/assets/react-jss.1ei6dr9e00.js,/assets/ahooks.2l850ymmh0.js,/assets/spin-delay.yjeyj6iwp0.js,/assets/simplebar-react.2g739uvai0.js,/assets/keepalive-for-react.xenu5vam70.js,/assets/zustand-shallow.6cz0yyv9x0.js,/assets/contract-errors.2o17vz9u50.js,/assets/antd-img-crop.196rkty480.js,/assets/icons-es-components-Context.365qmflwg0.js,/assets/dayjs-plugin-weekYear.2u78l3ex10.js,/assets/zustand-vanilla.1mo3ka3g30.js,/assets/zustand-react.9fsl9brxy0.js,/assets/zustand-vanilla-shallow.3424monv50.js,/assets/zustand-react-shallow.pybayv1hx0.js,/assets/ojm-antd-es-modal.bf3tgdh8v0.js,/assets/ojm-antd-es-upload.13gx45e0g0.js,/assets/ojm-antd-es-button.2uriphio50.js,/assets/ojm-antd-es-slider.xyvptuhid0.js" data-ojm-entry="/assets/index.6alqyx6ae0.js">(function(){var cs=document.currentScript;try{var ua=(navigator.userAgent||"").toLowerCase(),p=location.pathname;while(p.length>1&&p.charAt(p.length-1)==="/")p=p.slice(0,-1);if(ua.indexOf("wxwork")>=0&&p.slice(-6)==="/login"&&!new URLSearchParams(location.search).get("wecom_ticket")){var a=(typeof window.__OJ_API_BASE__==="string"&&window.__OJ_API_BASE__.trim())||"/api";while(a.length>1&&a.charAt(a.length-1)==="/")a=a.slice(0,-1);var q=new URLSearchParams(location.search),w=new URLSearchParams(),k=["corp","agent"];for(var i=0;i<k.length;i++){var v=(q.get(k[i])||"").trim();if(v)w.set(k[i],v)}var qs=w.toString();location.replace(a+"/auth/wecom/login"+(qs?"?"+qs:""));return}}catch(e){}var pl=cs.getAttribute("data-ojm-preload");if(pl){var ps=pl.split(",");for(var i=0;i<ps.length;i++){var l=document.createElement("link");l.rel="modulepreload";l.href=ps[i];document.head.appendChild(l);}}var s=document.createElement("script");s.type="module";s.src=cs.getAttribute("data-ojm-entry");document.head.appendChild(s);})();</script>
82
+ <script nonce="tifpCLPY1fTUzXJs6I+vIg==" data-ojm-preload="/assets/cssinjs.atgmhan7m0.js,/assets/pro-components.2kyot1vcl0.js,/assets/runtime.zrv2zhj9j0.js,/assets/react-query.3rln45ow00.js,/assets/antd.uw1coq15l0.js,/assets/react.1ew63mwts0.js,/assets/react-dom-client.ick0v05ob0.js,/assets/react-i18next.auhvjhqv00.js,/assets/react-router.2h9rebcv50.js,/assets/jsx-runtime.h40xu4o5b0.js,/assets/react-dom.2so9uxg3f0.js,/assets/clsx.jotilrt2e0.js,/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js,/assets/dayjs.pa60l6wjo0.js,/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js,/assets/use-sync-external-store-shim.1nz1pi9bb0.js,/assets/icons.159wjwals0.js,/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js,/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js,/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js,/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js,/assets/dayjs-plugin-localeData.2rwbjp9nk0.js,/assets/dayjs-plugin-weekday.2d56a4tml0.js,/assets/dayjs-plugin-weekOfYear.gvn9iph080.js,/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js,/assets/dnd-kit-core.1xyreig6r0.js,/assets/dnd-kit-utilities.1umw4xphb0.js,/assets/dnd-kit-sortable.s94qciuft0.js,/assets/ky.1cgch7gu60.js,/assets/zustand.3gjgzlnqo0.js,/assets/zustand-middleware.1xaxjueme0.js,/assets/react-error-boundary.2dncdfj3b0.js,/assets/antd-locale-en_US.561tozx860.js,/assets/antd-locale-zh_CN.1cmpmgzh70.js,/assets/i18next.33zqofapj0.js,/assets/tailwind-merge.125ehiqjw0.js,/assets/nprogress.2nz76n9ny0.js,/assets/nprogress-css.1cq2pzkre0.js,/assets/react-jss.1ei6dr9e00.js,/assets/ahooks.2l850ymmh0.js,/assets/spin-delay.yjeyj6iwp0.js,/assets/simplebar-react.2g739uvai0.js,/assets/keepalive-for-react.xenu5vam70.js,/assets/zustand-shallow.6cz0yyv9x0.js,/assets/contract-errors.2o17vz9u50.js,/assets/antd-img-crop.196rkty480.js,/assets/icons-es-components-Context.365qmflwg0.js,/assets/dayjs-plugin-weekYear.2u78l3ex10.js,/assets/zustand-vanilla.1mo3ka3g30.js,/assets/zustand-react.9fsl9brxy0.js,/assets/zustand-vanilla-shallow.3424monv50.js,/assets/zustand-react-shallow.pybayv1hx0.js,/assets/ojm-antd-es-modal.bf3tgdh8v0.js,/assets/ojm-antd-es-upload.13gx45e0g0.js,/assets/ojm-antd-es-button.2uriphio50.js,/assets/ojm-antd-es-slider.xyvptuhid0.js" data-ojm-entry="/assets/index.6alqyx6ae0.js">(function(){var cs=document.currentScript;try{var ua=(navigator.userAgent||"").toLowerCase(),p=location.pathname;while(p.length>1&&p.charAt(p.length-1)==="/")p=p.slice(0,-1);if(ua.indexOf("wxwork")>=0&&p.slice(-6)==="/login"&&!new URLSearchParams(location.search).get("wecom_ticket")){var a=(typeof window.__OJ_API_BASE__==="string"&&window.__OJ_API_BASE__.trim())||"/api";while(a.length>1&&a.charAt(a.length-1)==="/")a=a.slice(0,-1);var q=new URLSearchParams(location.search),w=new URLSearchParams(),k=["corp","agent"];for(var i=0;i<k.length;i++){var v=(q.get(k[i])||"").trim();if(v)w.set(k[i],v)}var qs=w.toString();location.replace(a+"/auth/wecom/login"+(qs?"?"+qs:""));return}}catch(e){}var pl=cs.getAttribute("data-ojm-preload");if(pl){var ps=pl.split(",");for(var i=0;i<ps.length;i++){var l=document.createElement("link");l.rel="modulepreload";l.href=ps[i];document.head.appendChild(l);}}var s=document.createElement("script");s.type="module";s.src=cs.getAttribute("data-ojm-entry");document.head.appendChild(s);})();</script>
83
83
  </head>
84
84
  <body>
85
85
  <div id="root"><div class="boot-skeleton">加载中…</div></div>
@@ -7,9 +7,9 @@
7
7
  "react-router": "7.18.2",
8
8
  "react-router/dom": "7.18.2",
9
9
  "@tanstack/react-query": "5.102.8",
10
- "@oj-module/runtime": "0.2.68",
11
- "@oj-module/runtime/contract": "0.2.68",
12
- "@oj-module/runtime/contract/errors": "0.2.68",
10
+ "@oj-module/runtime": "0.2.69",
11
+ "@oj-module/runtime/contract": "0.2.69",
12
+ "@oj-module/runtime/contract/errors": "0.2.69",
13
13
  "@plane/kit": "0.1.20",
14
14
  "@plane/editor": "0.1.20",
15
15
  "antd": "6.6.1",
@@ -121,16 +121,18 @@ interface JsonApi {
121
121
  permanentRedirect: (url: string) => void // 308 永久 + 方法保持
122
122
  }
123
123
  // 流式响应(v0.1.35):开流后逐块 write,绕过 {code,msg,data} 信封。
124
- // opts:{ status?, contentType? };返回 writer 必须显式 end()(或 handler 返回即自动关流)。
125
- stream: (opts?: { status?: number, contentType?: string }) => StreamWriter
124
+ // opts:{ status?, contentType?, fileName?, disposition? }——fileName + disposition:
125
+ // "attachment"(缺省)按原名附件下载、"inline" 内联;返回 writer 必须显式 end()(或 handler 返回即自动关流)。
126
+ // write 按入参类型分发:Uint8Array/ArrayBuffer/TypedArray/DataView 走字节通道(二进制无损),string 走 UTF-8 文本通道。
127
+ stream: (opts?: { status?: number, contentType?: string, fileName?: string, disposition?: "attachment" | "inline" }) => StreamWriter
126
128
  // SSE 便捷封装:自动 text/event-stream + 数据帧 `data: <c>\n\n`;
127
129
  // opts:{ status?, heartbeatSecs? }(心跳默认 15s,保活穿透代理)。
128
- sse: (opts?: { status?: number, heartbeatSecs?: number }) => StreamWriter
130
+ sse: (opts?: { status?: number, heartbeatSecs?: number, fileName?: string, disposition?: "attachment" | "inline" }) => StreamWriter
129
131
  }
130
132
 
131
133
  // 流式写入器(json.stream/json.sse 返回)。
132
134
  interface StreamWriter {
133
- write: (chunk: string) => void
135
+ write: (chunk: string | Uint8Array) => void
134
136
  end: () => void
135
137
  }
136
138
 
@@ -201,6 +201,9 @@
201
201
  "string",
202
202
  "null"
203
203
  ]
204
+ },
205
+ "force": {
206
+ "type": "boolean"
204
207
  }
205
208
  }
206
209
  }
@@ -6632,6 +6635,18 @@
6632
6635
  "null"
6633
6636
  ]
6634
6637
  },
6638
+ "employee_no": {
6639
+ "type": [
6640
+ "string",
6641
+ "null"
6642
+ ]
6643
+ },
6644
+ "phone_number": {
6645
+ "type": [
6646
+ "string",
6647
+ "null"
6648
+ ]
6649
+ },
6635
6650
  "departments": {
6636
6651
  "type": "array",
6637
6652
  "items": {
@@ -25,6 +25,10 @@ export interface UserRow {
25
25
  display_name: string | null
26
26
  email: string | null
27
27
  position: string | null
28
+ /** 工号(tenant 内唯一,未设置即 null)——U3:列表接口透出 users 表既有列 */
29
+ employee_no: string | null
30
+ /** 手机号(未设置即 null)——U3:列表接口透出 users 表既有列 */
31
+ phone_number: string | null
28
32
  /** 所属部门(主部门在前) */
29
33
  departments: UserDeptRef[]
30
34
  is_forbidden: boolean
@@ -108,7 +112,7 @@ async function assertMembershipAllowed(actorId: number, nextRoles: string[]): Pr
108
112
  }
109
113
 
110
114
  export async function listUsers(): Promise<UserRow[]> {
111
- const rows = await db.table("users").select(["id", "username", "display_name", "email", "position", "is_forbidden", "must_change"]).orderBy([{ field: "username" }]).all() as Array<Record<string, unknown>>;
115
+ const rows = await db.table("users").select(["id", "username", "display_name", "email", "position", "employee_no", "phone_number", "is_forbidden", "must_change"]).orderBy([{ field: "username" }]).all() as Array<Record<string, unknown>>;
112
116
  const roleRows = await db.table("sys_user_role").select(["user_id", "role_name"]).all() as Array<{ user_id: number, role_name: string }>;
113
117
  const roleMap = new Map<number, string[]>();
114
118
  for (const r of roleRows)
@@ -120,6 +124,8 @@ export async function listUsers(): Promise<UserRow[]> {
120
124
  display_name: r.display_name == null ? null : String(r.display_name),
121
125
  email: r.email == null ? null : String(r.email),
122
126
  position: r.position == null ? null : String(r.position),
127
+ employee_no: r.employee_no == null ? null : String(r.employee_no),
128
+ phone_number: r.phone_number == null ? null : String(r.phone_number),
123
129
  departments: depts.get(Number(r.id)) ?? [],
124
130
  is_forbidden: Number(r.is_forbidden) === 1,
125
131
  must_change: Number(r.must_change) === 1,
@@ -25,6 +25,9 @@ get.response = {
25
25
  display_name: { type: ["string", "null"] },
26
26
  email: { type: ["string", "null"] },
27
27
  position: { type: ["string", "null"] },
28
+ // U3:users 表既有列透出(工号 tenant 内唯一);未设置为 null,故为可选字段
29
+ employee_no: { type: ["string", "null"] },
30
+ phone_number: { type: ["string", "null"] },
28
31
  // 所属部门(主部门在前);未加入任何部门即空数组。带 id 供前端按部门筛选
29
32
  departments: {
30
33
  type: "array",
@@ -31,7 +31,8 @@ export const schemas = {
31
31
  body: z.object({
32
32
  "source": z.enum(["cdn", "file"]),
33
33
  "name": z.string(),
34
- "version": z.string().nullable().optional()
34
+ "version": z.string().nullable().optional(),
35
+ "force": z.boolean().optional()
35
36
  }),
36
37
  data: z.object({
37
38
  "task_id": z.string().optional()
@@ -242,6 +242,8 @@ export const schemas = {
242
242
  "display_name": z.string().nullable(),
243
243
  "email": z.string().nullable(),
244
244
  "position": z.string().nullable(),
245
+ "employee_no": z.string().nullable().optional(),
246
+ "phone_number": z.string().nullable().optional(),
245
247
  "departments": z.array(z.object({
246
248
  "id": z.number(),
247
249
  "name": z.string().nullable(),
@@ -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,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
+ }
@@ -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.69",
12
+ "@oj-module/runtime/contract": "0.2.69",
13
+ "@oj-module/runtime/contract/errors": "0.2.69",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",