dsh-request-doctor 0.13.4 → 0.14.1
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/client.js +252 -220
- package/docs/01-/350/275/257/344/273/266/351/234/200/346/261/202/350/247/204/346/240/274/350/257/264/346/230/216.md +1 -1
- package/docs/02-/350/275/257/344/273/266/350/256/276/350/256/241/350/257/264/346/230/216.md +1 -1
- package/docs/03-/350/275/257/344/273/266/346/265/213/350/257/225/346/212/245/345/221/212.md +5 -5
- package/docs/04-/345/256/211/350/243/205/351/203/250/347/275/262/350/257/264/346/230/216.md +1 -1
- package/docs/05-/347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
- package/docs/06-/350/275/257/344/273/266/350/264/250/351/207/217/345/272/246/351/207/217/346/212/245/345/221/212.md +1 -1
- package/docs/quality-report.json +10 -10
- package/index.js +478 -466
- package/lib/allow-route.js +39 -0
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -1,220 +1,252 @@
|
|
|
1
|
-
// 插件面板里的「不兼容安装」页。
|
|
2
|
-
//
|
|
3
|
-
// 管理器把版本检查放在安装流程里,对不上的包直接拒掉,然后原样回滚。它自己留了版本
|
|
4
|
-
// 豁免这个出口,但只有命令行或它自己的弹窗能走。这里把它做成一个页面:列出当前被拒的
|
|
5
|
-
// 插件,一键授权并安装,装完交给后台扫描去对齐声明、建兼容环境。
|
|
6
|
-
//
|
|
7
|
-
// 界面和 Host 之间走同源 HTTP,不碰 typert 的 remote 门面。动态加载的客户端模块拿不到
|
|
8
|
-
// 那个服务(它的上下文里没有),而 fetch 只要同源就行。Host 侧对应
|
|
9
|
-
// /doctor/incompatible 与 /doctor/install 两条路由。
|
|
10
|
-
window.__ModuleLoader__.load({
|
|
11
|
-
id: 'dsh-request-doctor',
|
|
12
|
-
factory: (require) => {
|
|
13
|
-
const React = require('react')
|
|
14
|
-
const h = React.createElement
|
|
15
|
-
const inject = ['slots']
|
|
16
|
-
|
|
17
|
-
// 界面文案。语言服务不再参与:动态模块拿不到它,标签还曾经因此显示成键名。
|
|
18
|
-
const TEXT = {
|
|
19
|
-
tabLabel: '不兼容安装',
|
|
20
|
-
intro: '下面是被当前 DSH 版本拒装的插件。装它们只需要一步:先按管理器的规则授权这个版本,再让它自己装。装完之后,声明对齐和兼容环境会自动补上。',
|
|
21
|
-
placeholder: '包名@版本,例如 dsh-ssh-ops@0.4.0',
|
|
22
|
-
install: '授权并安装',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
lines.push(h('div', { key:
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
})
|
|
1
|
+
// 插件面板里的「不兼容安装」页。
|
|
2
|
+
//
|
|
3
|
+
// 管理器把版本检查放在安装流程里,对不上的包直接拒掉,然后原样回滚。它自己留了版本
|
|
4
|
+
// 豁免这个出口,但只有命令行或它自己的弹窗能走。这里把它做成一个页面:列出当前被拒的
|
|
5
|
+
// 插件,一键授权并安装,装完交给后台扫描去对齐声明、建兼容环境。
|
|
6
|
+
//
|
|
7
|
+
// 界面和 Host 之间走同源 HTTP,不碰 typert 的 remote 门面。动态加载的客户端模块拿不到
|
|
8
|
+
// 那个服务(它的上下文里没有),而 fetch 只要同源就行。Host 侧对应
|
|
9
|
+
// /doctor/incompatible 与 /doctor/install 两条路由。
|
|
10
|
+
window.__ModuleLoader__.load({
|
|
11
|
+
id: 'dsh-request-doctor',
|
|
12
|
+
factory: (require) => {
|
|
13
|
+
const React = require('react')
|
|
14
|
+
const h = React.createElement
|
|
15
|
+
const inject = ['slots']
|
|
16
|
+
|
|
17
|
+
// 界面文案。语言服务不再参与:动态模块拿不到它,标签还曾经因此显示成键名。
|
|
18
|
+
const TEXT = {
|
|
19
|
+
tabLabel: '不兼容安装',
|
|
20
|
+
intro: '下面是被当前 DSH 版本拒装的插件。装它们只需要一步:先按管理器的规则授权这个版本,再让它自己装。装完之后,声明对齐和兼容环境会自动补上。',
|
|
21
|
+
placeholder: '包名@版本,例如 dsh-ssh-ops@0.4.0',
|
|
22
|
+
install: '授权并安装',
|
|
23
|
+
allow: '只授权',
|
|
24
|
+
requires: '要求运行在',
|
|
25
|
+
none: '当前没有被拒装的插件。可以在上面直接填包名安装,或点「重新扫描」重算一遍。',
|
|
26
|
+
working: '正在处理',
|
|
27
|
+
done: '已装好,兼容环境会在后台补上',
|
|
28
|
+
failed: '没能装上',
|
|
29
|
+
rescan: '重新扫描',
|
|
30
|
+
restartTitle: '需要重启后生效',
|
|
31
|
+
sourceManager: '来源:插件管理器拒装',
|
|
32
|
+
sourceAudit: '来源:本插件审计',
|
|
33
|
+
sourceUnknown: '来源:未知',
|
|
34
|
+
verdict: '判定:',
|
|
35
|
+
missing: '缺符号:',
|
|
36
|
+
environment: '环境:',
|
|
37
|
+
offline: '连不上 Host 侧的安装路由,可以改用命令:',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const style = {
|
|
41
|
+
column: { display: 'flex', flexDirection: 'column', gap: 14, width: '100%', minWidth: 0 },
|
|
42
|
+
title: { margin: 0, fontSize: '0.95em', fontWeight: 600 },
|
|
43
|
+
text: { margin: 0, fontSize: '0.9em', lineHeight: 1.6, opacity: 0.8 },
|
|
44
|
+
hush: { margin: 0, fontSize: '0.9em', opacity: 0.7 },
|
|
45
|
+
mono: { margin: 0, fontSize: '0.85em', opacity: 0.75, fontFamily: 'monospace' },
|
|
46
|
+
line: { display: 'flex', gap: 8, alignItems: 'center' },
|
|
47
|
+
field: {
|
|
48
|
+
flex: 1, padding: '6px 10px', borderRadius: 8, font: 'inherit',
|
|
49
|
+
border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4))',
|
|
50
|
+
background: 'var(--dsw-alias-bg-l1, transparent)', color: 'inherit',
|
|
51
|
+
},
|
|
52
|
+
press: {
|
|
53
|
+
padding: '6px 14px', borderRadius: 8, cursor: 'pointer', font: 'inherit',
|
|
54
|
+
border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.4))',
|
|
55
|
+
background: 'var(--dsw-alias-bg-l2, transparent)', color: 'inherit',
|
|
56
|
+
},
|
|
57
|
+
card: {
|
|
58
|
+
display: 'flex', flexDirection: 'column', gap: 8, padding: '10px 12px', borderRadius: 10,
|
|
59
|
+
border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.3))',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 统一走同源路由:取回 JSON,失败就把原因抛出去,由调用方决定怎么显示。
|
|
64
|
+
async function requestJson(path, init) {
|
|
65
|
+
const response = await fetch(path, init)
|
|
66
|
+
return response.json()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 这一行是不是「审计说兼容、管理器仍拒装」。是的话要给授权按钮。
|
|
70
|
+
function hasConflict(row, conflicts) {
|
|
71
|
+
return conflicts.indexOf(row.name) >= 0
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 一行一条审计结论:判定、理由、缺的符号、环境。
|
|
75
|
+
// 没有结论的(只有管理器拒装)就什么都不加,不占地方。
|
|
76
|
+
function briefLines(brief, style) {
|
|
77
|
+
if (brief === undefined) return []
|
|
78
|
+
const lines = [h('div', { key: `v-${brief.verdict}`, style: style.hush }, `${TEXT.verdict} ${brief.verdict}`)]
|
|
79
|
+
for (const reason of (brief.reasons || []).slice(-2)) {
|
|
80
|
+
lines.push(h('div', { key: `r-${reason}`, style: style.hush }, reason))
|
|
81
|
+
}
|
|
82
|
+
if ((brief.missing || []).length > 0) {
|
|
83
|
+
lines.push(h('div', { key: 'missing', style: style.hush }, `${TEXT.missing} ${brief.missing.join(', ')}`))
|
|
84
|
+
}
|
|
85
|
+
if (brief.environment !== undefined) {
|
|
86
|
+
lines.push(h('div', { key: 'env', style: style.hush }, `${TEXT.environment} ${brief.environment}`))
|
|
87
|
+
}
|
|
88
|
+
return lines
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 这条是谁给的:管理器拒装和本插件审计,含义完全不同。
|
|
92
|
+
function sourceLabel(source) {
|
|
93
|
+
if (source === 'manager') return TEXT.sourceManager
|
|
94
|
+
if (source === 'audit') return TEXT.sourceAudit
|
|
95
|
+
return TEXT.sourceUnknown
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 读一次被拒插件清单。Host 侧已经把不可用的行筛掉了,
|
|
99
|
+
// 顺带回一段「已经修好但需要重启」的提示。
|
|
100
|
+
async function loadPanel() {
|
|
101
|
+
const data = await requestJson('/doctor/incompatible', { headers: { accept: 'application/json' } })
|
|
102
|
+
if (data.ok !== true) throw new Error(data.reason || 'host declined')
|
|
103
|
+
return {
|
|
104
|
+
rows: data.rows || [],
|
|
105
|
+
restart: data.restart || [],
|
|
106
|
+
briefs: data.briefs || {},
|
|
107
|
+
conflicts: data.conflicts || [],
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 装一个包。授权、重试、声明对齐都在 Host 侧完成,这里只拿结果。
|
|
112
|
+
// 两条写路由形状相同,只在路径上不同。
|
|
113
|
+
async function postSpec(path, spec) {
|
|
114
|
+
const data = await requestJson(path, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'content-type': 'application/json' },
|
|
117
|
+
body: JSON.stringify({ spec }),
|
|
118
|
+
})
|
|
119
|
+
return { ok: data.ok === true, reason: data.text || '' }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 装一个包。授权、重试、声明对齐都在 Host 侧完成,这里只拿结果。
|
|
123
|
+
async function postInstall(spec) {
|
|
124
|
+
return postSpec('/doctor/install', spec)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 让 Host 放行一个版本。冲突行上的按钮走这里,规则与 /doctor allow 相同。
|
|
128
|
+
async function postAllow(spec) {
|
|
129
|
+
return postSpec('/doctor/allow', spec)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
// 让 Host 立刻重算一轮审计,不用等下一次定时扫描。
|
|
134
|
+
async function postRescan() {
|
|
135
|
+
const data = await requestJson('/doctor/rescan')
|
|
136
|
+
return { ok: data.ok === true, reason: data.reason || '' }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function IncompatiblePanel() {
|
|
140
|
+
const [rows, setRows] = React.useState([])
|
|
141
|
+
const [spec, setSpec] = React.useState('')
|
|
142
|
+
const [busy, setBusy] = React.useState(false)
|
|
143
|
+
const [note, setNote] = React.useState('')
|
|
144
|
+
const [restart, setRestart] = React.useState([])
|
|
145
|
+
const [briefs, setBriefs] = React.useState({})
|
|
146
|
+
const [conflicts, setConflicts] = React.useState([])
|
|
147
|
+
|
|
148
|
+
const refresh = React.useCallback(async () => {
|
|
149
|
+
try {
|
|
150
|
+
const panel = await loadPanel()
|
|
151
|
+
setRows(panel.rows)
|
|
152
|
+
setRestart(panel.restart)
|
|
153
|
+
setBriefs(panel.briefs || {})
|
|
154
|
+
setConflicts(panel.conflicts || [])
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// 路由不通就把可用的替代办法说出来,不留空白。
|
|
157
|
+
setNote(`${TEXT.offline} /doctor install <包名>(${String((error && error.message) || error)})`)
|
|
158
|
+
}
|
|
159
|
+
}, [])
|
|
160
|
+
|
|
161
|
+
React.useEffect(() => { refresh() }, [refresh])
|
|
162
|
+
|
|
163
|
+
// 两个入口共用同一段收尾:跑、报结果、刷新列表。
|
|
164
|
+
async function run(label, task) {
|
|
165
|
+
setBusy(true)
|
|
166
|
+
setNote(`${TEXT.working} ${label}`)
|
|
167
|
+
try {
|
|
168
|
+
const outcome = await task()
|
|
169
|
+
setNote(outcome.ok ? `${TEXT.done}:${label}` : `${TEXT.failed}:${outcome.reason}`)
|
|
170
|
+
await refresh()
|
|
171
|
+
} catch (error) {
|
|
172
|
+
setNote(`${TEXT.failed}:${String(error)}`)
|
|
173
|
+
} finally {
|
|
174
|
+
setBusy(false)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const submit = () => {
|
|
179
|
+
const trimmed = spec.trim()
|
|
180
|
+
if (trimmed === '') return
|
|
181
|
+
run(trimmed, () => postInstall(trimmed))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const children = []
|
|
185
|
+
children.push(h('p', { key: 'title', style: style.title }, TEXT.tabLabel))
|
|
186
|
+
children.push(h('p', { key: 'intro', style: style.text }, TEXT.intro))
|
|
187
|
+
children.push(h('div', { key: 'entry', style: style.line },
|
|
188
|
+
h('input', {
|
|
189
|
+
value: spec, style: style.field, placeholder: TEXT.placeholder,
|
|
190
|
+
onChange: (event) => setSpec(event.target.value),
|
|
191
|
+
}),
|
|
192
|
+
h('button', {
|
|
193
|
+
type: 'button', style: style.press, disabled: busy, onClick: submit,
|
|
194
|
+
}, TEXT.install),
|
|
195
|
+
h('button', {
|
|
196
|
+
type: 'button', style: style.press, disabled: busy,
|
|
197
|
+
onClick: () => run(TEXT.rescan, postRescan),
|
|
198
|
+
style: { ...style.press, opacity: 0.8 },
|
|
199
|
+
}, TEXT.rescan)))
|
|
200
|
+
|
|
201
|
+
if (rows.length === 0) {
|
|
202
|
+
children.push(h('p', { key: 'none', style: style.hush }, TEXT.none))
|
|
203
|
+
} else {
|
|
204
|
+
children.push(h('div', { key: 'rows', style: style.column }, rows.map((row) => h('div', {
|
|
205
|
+
key: `${row.name}@${row.version}`, style: style.card,
|
|
206
|
+
},
|
|
207
|
+
h('div', { style: style.title }, `${row.name}@${row.version}`),
|
|
208
|
+
h('div', { style: style.hush }, `${TEXT.requires} ${row.runtimeVersion || '?'}`),
|
|
209
|
+
h('div', { style: style.hush }, sourceLabel(row.source)),
|
|
210
|
+
...briefLines(briefs[row.name], style),
|
|
211
|
+
|
|
212
|
+
hasConflict(row, conflicts)
|
|
213
|
+
? h('button', {
|
|
214
|
+
type: 'button', style: style.press, disabled: busy,
|
|
215
|
+
onClick: () => run(`${row.name}@${row.version}`, () => postAllow(`${row.name}@${row.version}`)),
|
|
216
|
+
}, TEXT.allow)
|
|
217
|
+
: h('span'),
|
|
218
|
+
h('button', {
|
|
219
|
+
type: 'button', style: style.press, disabled: busy,
|
|
220
|
+
onClick: () => run(`${row.name}@${row.version}`, () => postInstall(`${row.name}@${row.version}`)),
|
|
221
|
+
}, TEXT.install)))))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if (restart.length > 0) {
|
|
226
|
+
children.push(h('p', { key: 'restart-title', style: style.title }, TEXT.restartTitle))
|
|
227
|
+
for (const item of restart) {
|
|
228
|
+
children.push(h('p', { key: `restart-${item.name}`, style: style.hush }, item.hint))
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Host 的返回是多行文本(安装结果、位置、兼容环境),按原样保留换行。
|
|
232
|
+
if (note !== '') children.push(h('p', { key: 'note', style: { ...style.hush, whiteSpace: 'pre-wrap' } }, note))
|
|
233
|
+
return h('div', { style: style.column }, ...children)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function apply(ctx) {
|
|
237
|
+
if (typeof ctx.slots?.inject !== 'function') return
|
|
238
|
+
const cells = [
|
|
239
|
+
['plugins.tab', 'requestDoctorIncompatible', 5, TEXT.tabLabel, IncompatiblePanel],
|
|
240
|
+
['settings.plugins.tab', 'requestDoctorSettingsTab', 20, TEXT.tabLabel, IncompatiblePanel],
|
|
241
|
+
]
|
|
242
|
+
for (const [slot, id, order, label, Component] of cells) {
|
|
243
|
+
ctx.slots.inject(slot, () => ctx.slots.register({ name: slot, id, order, label }, Component))
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const module = { exports: {} }
|
|
248
|
+
module.exports.apply = apply
|
|
249
|
+
module.exports.inject = inject
|
|
250
|
+
return module.exports
|
|
251
|
+
},
|
|
252
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 软件测试报告
|
|
2
2
|
|
|
3
3
|
**产品名称**:request-doctor(请求急救员)
|
|
4
|
-
**版本**:0.
|
|
4
|
+
**版本**:0.14.1
|
|
5
5
|
**编制依据**:GB/T 8567-2006《计算机软件文档编制规范》6.6;GB/T 8566-2007 验证与确认过程
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -59,14 +59,14 @@
|
|
|
59
59
|
| `test/pinned-peers.test.mjs` | 1 | peer 钉死成上一补丁版本:六条全对齐、只给导入的建垫片 |
|
|
60
60
|
| `test/install-rescue.test.mjs` | 4 | 从失败里取出包名与版本,供授权使用 |
|
|
61
61
|
| `test/install-in-place.test.mjs` | 2 | 已装着的包就地修复,不再重复安装 |
|
|
62
|
-
| `test/client-half.test.mjs` |
|
|
62
|
+
| `test/client-half.test.mjs` | 11 | 客户端半:模块身份、注入声明、apply 注册、组件能渲染 |
|
|
63
63
|
| `test/diagnostics.test.mjs` | 3 | 快照可选写入:不配置不写盘、突发合并、坏路径不致命 |
|
|
64
64
|
| `test/restart-notice.test.mjs` | 3 |
|
|
65
|
-
| `test/allow-version.test.mjs` |
|
|
65
|
+
| `test/allow-version.test.mjs` | 6 | 放行:版本必填、缺管理器拒绝、成功只调一次、失败如实报、请求体解析 |
|
|
66
66
|
| `test/panel-blocked.test.mjs` | 7 | 冲突判定:审计兼容却被拒装、清单形状解析、给 allow 动作 |
|
|
67
67
|
| `test/panel-briefs.test.mjs` | 6 | 界面摘要:按名索引、只送可见行、缺数据不抛、不污染诊断 |
|
|
68
68
|
| `test/verdict-report.test.mjs` | 29 | why 与 list:判定、理由、环境摘要、自动扫描、分组、缺口、按形态给建议 | 缺导出判为需要重启;其它失败不误判;多次累积不覆盖 |
|
|
69
|
-
| **合计** | **
|
|
69
|
+
| **合计** | **214** | **全部通过** |
|
|
70
70
|
|
|
71
71
|
执行结果:
|
|
72
72
|
|
|
@@ -219,4 +219,4 @@
|
|
|
219
219
|
|
|
220
220
|
### 7.2 分文件执行结果
|
|
221
221
|
|
|
222
|
-
34 个测试文件逐个单独执行,每个都有用例且全部通过,不存在因导入失败而静默跳过的文件。用例数分布:compat.test.mjs 17、plugin.test.mjs 15、compat-env.test.mjs 14、classify.test.mjs 13,其余按职能分散,合计
|
|
222
|
+
34 个测试文件逐个单独执行,每个都有用例且全部通过,不存在因导入失败而静默跳过的文件。用例数分布:compat.test.mjs 17、plugin.test.mjs 15、compat-env.test.mjs 14、classify.test.mjs 13,其余按职能分散,合计 214。
|
package/docs/quality-report.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"standard": "GB/T 45717-2025 信息技术 软件测量 软件质量测量 自动化的源代码质量测度",
|
|
3
|
-
"generatedAt": "2026-10-29T16:
|
|
3
|
+
"generatedAt": "2026-10-29T16:30:49.675Z",
|
|
4
4
|
"scope": {
|
|
5
|
-
"files":
|
|
6
|
-
"totalLines":
|
|
7
|
-
"sourceLines":
|
|
8
|
-
"commentLines":
|
|
5
|
+
"files": 36,
|
|
6
|
+
"totalLines": 5398,
|
|
7
|
+
"sourceLines": 3897,
|
|
8
|
+
"commentLines": 997
|
|
9
9
|
},
|
|
10
10
|
"measures": {
|
|
11
|
-
"commentDensity": 25.
|
|
12
|
-
"duplicationPercent": 4.
|
|
13
|
-
"functionCount":
|
|
11
|
+
"commentDensity": 25.58,
|
|
12
|
+
"duplicationPercent": 4.91,
|
|
13
|
+
"functionCount": 413,
|
|
14
14
|
"maxComplexity": 10,
|
|
15
|
-
"maxFunctionLines":
|
|
15
|
+
"maxFunctionLines": 57,
|
|
16
16
|
"maxFileLines": 500,
|
|
17
|
-
"averageComplexity": 3.
|
|
17
|
+
"averageComplexity": 3.84
|
|
18
18
|
},
|
|
19
19
|
"limits": {
|
|
20
20
|
"maxComplexity": 10,
|