@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 +2 -2
- package/shell-dist/assets/{runtime.1xzahckt10.js → runtime.zrv2zhj9j0.js} +2 -2
- package/shell-dist/index.html +4 -4
- package/shell-dist/versions.json +3 -3
- package/templates/global.d.ts +6 -4
- package/templates/openapi.json +15 -0
- package/templates/src/app/api/_platform/_shared/service/domain/users.ts +7 -1
- package/templates/src/app/api/system/users/api.ts +3 -0
- package/templates/src/app/web/_platform/__client/api.schemas.ts +2 -1
- package/templates/src/app/web/system/__client/api.schemas.ts +2 -0
- package/templates/tests/rbac-users.spec.ts +8 -0
- package/templates/types/stream-probes/stream-handlers.ts +34 -0
- package/vendor/host-versions.json +3 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oj-module/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
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.
|
|
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.
|
|
203
|
+
version: "0.2.69",
|
|
204
204
|
license: "MIT"
|
|
205
205
|
},
|
|
206
|
-
lastBuildTime: "2026-11-06
|
|
206
|
+
lastBuildTime: "2026-11-06 13:26:56"
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
209
|
var gr = w((() => {}));
|
package/shell-dist/index.html
CHANGED
|
@@ -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-
|
|
9
|
-
<script type="importmap" nonce="
|
|
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.
|
|
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="
|
|
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>
|
package/shell-dist/versions.json
CHANGED
|
@@ -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.
|
|
11
|
-
"@oj-module/runtime/contract": "0.2.
|
|
12
|
-
"@oj-module/runtime/contract/errors": "0.2.
|
|
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",
|
package/templates/global.d.ts
CHANGED
|
@@ -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? }
|
|
125
|
-
|
|
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
|
|
package/templates/openapi.json
CHANGED
|
@@ -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.
|
|
12
|
-
"@oj-module/runtime/contract": "0.2.
|
|
13
|
-
"@oj-module/runtime/contract/errors": "0.2.
|
|
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",
|