dsh-plugin-manager-companion 0.1.0
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/LICENSE +21 -0
- package/README.en.md +144 -0
- package/README.md +142 -0
- package/cordis.patch.yml +9 -0
- package/dist/about.d.ts +77 -0
- package/dist/about.js +179 -0
- package/dist/cli.d.ts +226 -0
- package/dist/cli.js +856 -0
- package/dist/client/AboutPage.d.ts +75 -0
- package/dist/client/ConsolePage.d.ts +79 -0
- package/dist/client/KindsPage.d.ts +21 -0
- package/dist/client/MarketplacePage.d.ts +36 -0
- package/dist/client/OfficialSlots.d.ts +35 -0
- package/dist/client/UpgradeRow.d.ts +108 -0
- package/dist/client/index.d.ts +26 -0
- package/dist/client/locales.d.ts +475 -0
- package/dist/client/pmSelect.d.ts +38 -0
- package/dist/client/shared.d.ts +928 -0
- package/dist/client/upgradeView.d.ts +278 -0
- package/dist/client/wire.d.ts +401 -0
- package/dist/client.js +9194 -0
- package/dist/diagnostics.d.ts +332 -0
- package/dist/diagnostics.js +2631 -0
- package/dist/envManager.d.ts +1047 -0
- package/dist/envManager.js +3214 -0
- package/dist/fix.d.ts +60 -0
- package/dist/fix.js +168 -0
- package/dist/guard.d.ts +133 -0
- package/dist/guard.js +232 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +1150 -0
- package/dist/installSession.d.ts +111 -0
- package/dist/installSession.js +150 -0
- package/dist/kinds.d.ts +464 -0
- package/dist/kinds.js +1029 -0
- package/dist/marketView.d.ts +261 -0
- package/dist/marketView.js +406 -0
- package/dist/marketplace.d.ts +248 -0
- package/dist/marketplace.js +500 -0
- package/dist/match.d.ts +67 -0
- package/dist/match.js +203 -0
- package/dist/net.d.ts +108 -0
- package/dist/net.js +163 -0
- package/dist/official.d.ts +145 -0
- package/dist/official.js +205 -0
- package/dist/paths.d.ts +108 -0
- package/dist/paths.js +236 -0
- package/dist/presets.d.ts +299 -0
- package/dist/presets.js +578 -0
- package/dist/qualityGate.d.ts +66 -0
- package/dist/qualityGate.js +247 -0
- package/dist/rank.d.ts +88 -0
- package/dist/rank.js +164 -0
- package/dist/registry.d.ts +295 -0
- package/dist/registry.js +686 -0
- package/dist/rest.d.ts +122 -0
- package/dist/rest.js +219 -0
- package/dist/scan.d.ts +134 -0
- package/dist/scan.js +396 -0
- package/dist/settings.d.ts +447 -0
- package/dist/settings.js +263 -0
- package/dist/tags.d.ts +119 -0
- package/dist/tags.js +166 -0
- package/dist/tools.d.ts +131 -0
- package/dist/tools.js +377 -0
- package/dist/types.d.ts +651 -0
- package/dist/types.js +13 -0
- package/dist/upgrade.d.ts +428 -0
- package/dist/upgrade.js +1100 -0
- package/dist/upgradeView.d.ts +313 -0
- package/dist/upgradeView.js +273 -0
- package/docs/images/readme/01-console-health.png +0 -0
- package/docs/images/readme/02-console-envs.png +0 -0
- package/docs/images/readme/03-marketplace.png +0 -0
- package/docs/images/readme/04-official-plugin-page.png +0 -0
- package/package.json +104 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upgradeView.ts — 升级的**纯决策**:四态怎么显示、注册面收哪些包、结果怎么读。
|
|
3
|
+
*
|
|
4
|
+
* 归属:A 类·重写(新模块,无旧实现可参考:旧仓库的"升级"只是一句提示文案)。
|
|
5
|
+
* 旧实现参考:无(旧 dsh-web-plugin-manager 没有升级功能;其市场页只有"可更新 x.y.z"徽标)。
|
|
6
|
+
* 官方复用:无。本模块**不认识 React、不认识 ctx、不联网、不读盘**——它是可独立测试的
|
|
7
|
+
* 决策层(与 marketView.ts / tags.ts 同一方法论:把判定抽出来,组件只负责画)。
|
|
8
|
+
* 前提检查:四态(update-available / up-to-date / unknown / not-upgradable)由 host 的
|
|
9
|
+
* upgradeCheck op 直接给出(docs/REST-CONTRACT.md「UpgradeCheckResult.units[].state 四态」),
|
|
10
|
+
* 界面**直接渲染,不自己推断**。本模块只回答三个界面问题:
|
|
11
|
+
* ① 这一行该不该画、画什么;② 官方插件页的 keyed 注册面该注册哪些包名;③ 一次升级结果
|
|
12
|
+
* 该被读成成功/失败/回滚/没验证。
|
|
13
|
+
*
|
|
14
|
+
* 为什么必须有这一层(而不是把判断写进组件):
|
|
15
|
+
* · "靠缺席传达状态"是本仓库反复踩过的坑(DESIGN §5.5 / §12.3.3)。四态里 up-to-date
|
|
16
|
+
* 与 unknown 都不画升级入口,但它们的**事实完全不同**:前者是"查过,没有更新",
|
|
17
|
+
* 后者是"查不到"。把两者的可见性判定收进一个纯函数,测试才能逐个钉住
|
|
18
|
+
* "unknown 绝不显示成已是最新"。
|
|
19
|
+
* · 注册对账(§5.5:不留孤儿 key)需要一个可断言的集合运算,而不是散在 apply 里的副作用。
|
|
20
|
+
*/
|
|
21
|
+
import type { UpgradeState } from '../types.ts';
|
|
22
|
+
/**
|
|
23
|
+
* 本模块读的单元形状(host 的 UpgradeUnitReport 是它的超集,wire 的 UpgradeUnitView 也是)。
|
|
24
|
+
*
|
|
25
|
+
* 刻意用结构类型而不是 import 契约类型:判定只看这几个字段,
|
|
26
|
+
* 于是同一份函数既能吃 host 的原始报告(host 侧测试)也能吃归一后的视图(客户端)。
|
|
27
|
+
*/
|
|
28
|
+
export interface UpgradeUnitLike {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly state: string;
|
|
31
|
+
readonly currentVersion: string | null;
|
|
32
|
+
readonly targetVersion: string | null;
|
|
33
|
+
readonly targetTag: string | null;
|
|
34
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
35
|
+
readonly source?: string;
|
|
36
|
+
readonly at?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 升级入口的显示形态。
|
|
40
|
+
*
|
|
41
|
+
* 刻意做成**四态一一对应**的枚举(而不是布尔的 canUpgrade):布尔会把
|
|
42
|
+
* up-to-date 与 unknown 压成同一个 false,而那正是要防的"靠缺席传达"。
|
|
43
|
+
*/
|
|
44
|
+
export type UpgradeRowKind =
|
|
45
|
+
/** 有版本事实且比当前新 → 画升级入口(tags 非空时列出让用户挑)。 */
|
|
46
|
+
'upgrade'
|
|
47
|
+
/** 有版本事实、没有更新的 → 插件页**不画**("已是最新"的口径在市场页/关于页)。 */
|
|
48
|
+
| 'hidden'
|
|
49
|
+
/** 查不到版本事实 → **必须画**「查不到:<原因>」+ 重试。 */
|
|
50
|
+
| 'unknown'
|
|
51
|
+
/** 结构上就升不了(安装方提供的层)→ 画说明 + 命令,**不给按钮**。 */
|
|
52
|
+
| 'command';
|
|
53
|
+
/** 状态值 → 形态(逐态显式表:新增状态时编译期就会在这里暴露,不会静默落进 default)。 */
|
|
54
|
+
export declare const ROW_KIND_OF_STATE: Readonly<Record<UpgradeState, UpgradeRowKind>>;
|
|
55
|
+
/** 四态的稳定顺序(用于断言与文档对照,不用于渲染顺序)。 */
|
|
56
|
+
export declare const ROW_KIND_ORDER: readonly UpgradeRowKind[];
|
|
57
|
+
/**
|
|
58
|
+
* 一个单元在界面上的形态。
|
|
59
|
+
*
|
|
60
|
+
* @param unit - host 给出的单元报告。
|
|
61
|
+
* @returns 形态;状态值未知时按 unknown 处理(读不懂的状态不能染成"已是最新",
|
|
62
|
+
* 也不能给出一个可能无效的升级按钮)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function rowKindOf(unit: {
|
|
65
|
+
readonly state: string;
|
|
66
|
+
}): UpgradeRowKind;
|
|
67
|
+
/**
|
|
68
|
+
* 这一行是否要出现在界面上。
|
|
69
|
+
*
|
|
70
|
+
* 判据直接来自 DESIGN §5.5 的表格:只有 up-to-date 缺席,其余三态各自有形态。
|
|
71
|
+
* 这是本模块**唯一**的可见性出口——组件不许再写一份。
|
|
72
|
+
*
|
|
73
|
+
* @param unit - host 给出的单元报告。
|
|
74
|
+
* @returns 要画时 true。
|
|
75
|
+
*/
|
|
76
|
+
export declare function rowVisible(unit: {
|
|
77
|
+
readonly state: string;
|
|
78
|
+
}): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* 把一次检查结果折成"包名 → 单元"的查找表。
|
|
81
|
+
*
|
|
82
|
+
* 同名条目后到者不覆盖先到者:host 的 unitFacts 按名字去重过,出现重复说明契约被破坏,
|
|
83
|
+
* 此时保留第一条(并让界面的计数与列表一致)比"悄悄用最后一条"更可解释。
|
|
84
|
+
*
|
|
85
|
+
* @param units - 检查结果的单元列表。
|
|
86
|
+
* @returns 查找表。
|
|
87
|
+
*/
|
|
88
|
+
export declare function unitIndex<T extends {
|
|
89
|
+
readonly name: string;
|
|
90
|
+
}>(units: readonly T[]): ReadonlyMap<string, T>;
|
|
91
|
+
/**
|
|
92
|
+
* 注册面的**目标集合**:该为哪些包名持有 plugins.bundle.config 的 key。
|
|
93
|
+
*
|
|
94
|
+
* 判据 = **已装** ∧ (还没查过 ∨ 这一态要显示)。两种情况都在集合里:
|
|
95
|
+
*
|
|
96
|
+
* ① **还没查过**(units 为 undefined,或这一轮检查里没有这个包)→ 注册。
|
|
97
|
+
* 这是必须的,而且有两个理由:
|
|
98
|
+
* · 诚实:此时界面画的是"尚未检查 + 检查按钮",而不是一片安静——
|
|
99
|
+
* "查不到"与"没查"都必须说出来(§12.3.3:不许用缺席表达状态);
|
|
100
|
+
* · 因果:检查正是由**行自己挂载时的 effect** 触发的(§5.5 的"进入即查")。
|
|
101
|
+
* 若先要求"查过才注册",就永远没有东西去触发那次检查(先有鸡还是先有蛋)。
|
|
102
|
+
* ② **查过且要显示**(update-available / unknown / not-upgradable)→ 注册;
|
|
103
|
+
* **查过且 up-to-date** → 不注册,于是那个 key 的 disposer 被释放,
|
|
104
|
+
* 官方 config-ledger 重算,那一节**当场消失**(§5.5 的回收纪律)。
|
|
105
|
+
*
|
|
106
|
+
* 与 {@link rowVisible} 分开的理由:它们回答不同的问题——"这一行要不要画"(渲染期)与
|
|
107
|
+
* "这个 key 要不要注册"(装配期)。合并会让"不画"变成"不注册",而 keyed slot 的 key
|
|
108
|
+
* 一旦撤掉那一节会**当场消失**(§5.5 要的正是这个),所以两者必须能被分别断言。
|
|
109
|
+
*
|
|
110
|
+
* @param installed - 当前已装的包名(官方台账)。
|
|
111
|
+
* @param units - 最新一次检查的单元列表;还没检查时为 undefined。
|
|
112
|
+
* @returns 要注册的包名(去重、稳定顺序:按名字排序)。
|
|
113
|
+
*/
|
|
114
|
+
export declare function registeredNames(installed: Iterable<string>, units: readonly {
|
|
115
|
+
readonly name: string;
|
|
116
|
+
readonly state: string;
|
|
117
|
+
}[] | undefined): string[];
|
|
118
|
+
/**
|
|
119
|
+
* 默认选中的 tag。
|
|
120
|
+
*
|
|
121
|
+
* 口径与 host 的 pickTarget 一致(同线最新优先),但**不重算**:host 已经在
|
|
122
|
+
* unit.targetTag 里给了答案,这里只是"把那个 tag 在列表里找出来"。找不到时退到
|
|
123
|
+
* preferred 标记,再退到第一个——绝不返回一个不在列表里的 tag。
|
|
124
|
+
*
|
|
125
|
+
* @param unit - 单元报告。
|
|
126
|
+
* @returns 选中的 tag 名;没有可选 tag 时 undefined(此时不给"挑版本")。
|
|
127
|
+
*/
|
|
128
|
+
export declare function defaultTag(unit: {
|
|
129
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
130
|
+
readonly targetTag: string | null;
|
|
131
|
+
}): string | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* 本模块只读 dist-tag 的这四个字段(host 的 UpgradeTag 是它的超集)。
|
|
134
|
+
*
|
|
135
|
+
* line 是宽松的 string:host 新增一档版本线时,界面要能如实显示,而不是被一个窄联合
|
|
136
|
+
* 挡在编译期之外。判定只对 'other-line' 取真(其余一律按"同线/未知"处理,宁可少提醒一次
|
|
137
|
+
* 也不谎报"会切到另一条线")。
|
|
138
|
+
*/
|
|
139
|
+
export interface UpgradeTagLike {
|
|
140
|
+
readonly tag: string;
|
|
141
|
+
readonly version: string;
|
|
142
|
+
readonly line: string;
|
|
143
|
+
readonly preferred: boolean;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 用户挑的 tag 对应的版本。
|
|
147
|
+
*
|
|
148
|
+
* @param unit - 单元报告。
|
|
149
|
+
* @param tag - 选中的 tag 名;undefined 时用 {@link defaultTag}。
|
|
150
|
+
* @returns 版本;没有可选 tag 时退回 host 给的 targetVersion。
|
|
151
|
+
*/
|
|
152
|
+
export declare function versionForTag(unit: {
|
|
153
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
154
|
+
readonly targetTag: string | null;
|
|
155
|
+
readonly targetVersion: string | null;
|
|
156
|
+
}, tag?: string): string | null;
|
|
157
|
+
/**
|
|
158
|
+
* 所选版本是不是**切到另一条线**。
|
|
159
|
+
*
|
|
160
|
+
* 判据用 host 给的 UpgradeTag.line,不在这里重算版本线(那是 host 的 versionLine)。
|
|
161
|
+
*
|
|
162
|
+
* @param unit - 单元报告。
|
|
163
|
+
* @param tag - 选中的 tag 名。
|
|
164
|
+
* @returns 会切到另一条线时 true。
|
|
165
|
+
*/
|
|
166
|
+
export declare function changesLine(unit: {
|
|
167
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
168
|
+
readonly targetTag: string | null;
|
|
169
|
+
}, tag?: string): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* 所选版本与当前版本是否相同(相同时不给可点按钮,DESIGN §5.5)。
|
|
172
|
+
*
|
|
173
|
+
* @param unit - 单元报告。
|
|
174
|
+
* @param tag - 选中的 tag 名。
|
|
175
|
+
* @returns 相同时 true。
|
|
176
|
+
*/
|
|
177
|
+
export declare function sameAsCurrent(unit: {
|
|
178
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
179
|
+
readonly targetTag: string | null;
|
|
180
|
+
readonly currentVersion: string | null;
|
|
181
|
+
readonly targetVersion: string | null;
|
|
182
|
+
}, tag?: string): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* 「当前 x → y | 升级」里的版本对,缺失时给 null 让界面显示"未知"而不是编一个。
|
|
185
|
+
*
|
|
186
|
+
* @param unit - 单元报告。
|
|
187
|
+
* @param tag - 选中的 tag 名。
|
|
188
|
+
* @returns 当前版本与目标版本。
|
|
189
|
+
*/
|
|
190
|
+
export declare function versionPair(unit: {
|
|
191
|
+
readonly currentVersion: string | null;
|
|
192
|
+
readonly tags: readonly UpgradeTagLike[] | null;
|
|
193
|
+
readonly targetTag: string | null;
|
|
194
|
+
readonly targetVersion: string | null;
|
|
195
|
+
}, tag?: string): {
|
|
196
|
+
readonly from: string | null;
|
|
197
|
+
readonly to: string | null;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* 「来源与时间」这一行要显示什么。
|
|
201
|
+
*
|
|
202
|
+
* 契约要求界面**必须**把事实来源与时间标出来(否则"最新"这个断言没有依据)。
|
|
203
|
+
* 来源未知时返回 undefined(不编一个来源出来)。
|
|
204
|
+
*
|
|
205
|
+
* @param unit - 单元报告。
|
|
206
|
+
* @returns 来源标识与时间;来源未知时 undefined。
|
|
207
|
+
*/
|
|
208
|
+
export declare function sourceFacts(unit: {
|
|
209
|
+
readonly source?: string;
|
|
210
|
+
readonly at?: string;
|
|
211
|
+
}): {
|
|
212
|
+
readonly source: 'market-index' | 'registry';
|
|
213
|
+
readonly at: string | undefined;
|
|
214
|
+
} | undefined;
|
|
215
|
+
/**
|
|
216
|
+
* 升级结果的**诚实分类**。
|
|
217
|
+
*
|
|
218
|
+
* 这是本模块最要紧的一条:失败态绝不能被渲染成完成(task-14/18/85 那一类问题的第四次机会)。
|
|
219
|
+
* 分类刻意按"用户该知道什么"切,而不是按 host 的错误码切:
|
|
220
|
+
* · done:真升级成功;
|
|
221
|
+
* · rolled-back:试装没通过 → **没有在真实环境执行升级**(host 返回 canary-not-passed);
|
|
222
|
+
* · failed:官方通道失败或盘上核对没到位;
|
|
223
|
+
* · unverified:金丝雀**没跑**就升级了 —— "没验证"既不是通过也不是失败,
|
|
224
|
+
* 它必须能被看出来(否则用户以为验证过了)。
|
|
225
|
+
*/
|
|
226
|
+
export type UpgradeOutcomeKind = 'done' | 'rolled-back' | 'failed' | 'unverified';
|
|
227
|
+
/**
|
|
228
|
+
* 读一次升级结果。
|
|
229
|
+
*
|
|
230
|
+
* @param result - host 的 upgrade op 结果(已归一)。
|
|
231
|
+
* @returns 分类。
|
|
232
|
+
*/
|
|
233
|
+
export declare function upgradeOutcome(result: {
|
|
234
|
+
readonly ok: boolean;
|
|
235
|
+
readonly code?: string;
|
|
236
|
+
readonly canary?: {
|
|
237
|
+
readonly ran: boolean;
|
|
238
|
+
readonly conclusion?: string;
|
|
239
|
+
};
|
|
240
|
+
}): UpgradeOutcomeKind;
|
|
241
|
+
/**
|
|
242
|
+
* 金丝雀的读法:**"没验证"与"验证失败"必须分开**。
|
|
243
|
+
*
|
|
244
|
+
* @param canary - 升级结果里的金丝雀报告;没有时为 undefined。
|
|
245
|
+
* @returns 四态;没有金丝雀字段时 absent(宿主没给,界面据此不宣称任何结论)。
|
|
246
|
+
*/
|
|
247
|
+
export declare function canaryVerdict(canary: {
|
|
248
|
+
readonly ran: boolean;
|
|
249
|
+
readonly conclusion?: string;
|
|
250
|
+
} | undefined): 'passed' | 'failed' | 'not-run' | 'absent';
|
|
251
|
+
/**
|
|
252
|
+
* 一次升级/回滚结果里"盘上事实"是否真的对上了。
|
|
253
|
+
*
|
|
254
|
+
* 回滚用的是 host 的 clean(它按盘上事实核对过);升级用的是 ok。
|
|
255
|
+
* 两者都不是"我们没看到报错"——**没看到不等于核对过**。
|
|
256
|
+
*
|
|
257
|
+
* @param result - 升级或回滚结果。
|
|
258
|
+
* @returns 核对通过时 true;载荷缺字段时 false(读不出来不算核对过)。
|
|
259
|
+
*/
|
|
260
|
+
export declare function diskVerified(result: {
|
|
261
|
+
readonly ok: boolean;
|
|
262
|
+
readonly clean?: boolean;
|
|
263
|
+
}): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* 市场卡片上要不要画「升级到 x.y.z」。
|
|
266
|
+
*
|
|
267
|
+
* 零新机制:用既有的 updateAvailable 判据(marketView.ts)与 latestVersion。
|
|
268
|
+
* 未安装、无更新、或版本读不到时都不出现。
|
|
269
|
+
*
|
|
270
|
+
* @param item - 市场条目(只读它关心的字段)。
|
|
271
|
+
* @param updateAvailable - marketView 的判据(注入以便复用同一份实现,不抄第二份)。
|
|
272
|
+
* @returns 目标版本;不画时 undefined。
|
|
273
|
+
*/
|
|
274
|
+
export declare function marketUpgradeTarget<T extends {
|
|
275
|
+
readonly installed?: boolean;
|
|
276
|
+
readonly installedVersion?: string;
|
|
277
|
+
readonly latestVersion?: string;
|
|
278
|
+
}>(item: T, updateAvailable: (item: T) => boolean): string | undefined;
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wire.ts — 把外来的 JSON(自有 REST / 官方 settings)归一成组件可以安全读取的对象。
|
|
3
|
+
*
|
|
4
|
+
* 归属:A 类·重写(旧仓库客户端对线缆数据一律 `as` 断言后直接渲染;本模块是新契约的
|
|
5
|
+
* 失败模式驱动的,没有可参考的旧实现)。
|
|
6
|
+
* 旧实现参考:dsh-web-plugin-manager/src/client/*(旧实现同样信任 host 返回的形状;
|
|
7
|
+
* 未复制代码——差异是刻意的:旧实现把"数据一定齐全"当作前提,而实测证明它不成立)。
|
|
8
|
+
* 官方复用:无。官方 primitives 只渲染、不校验;官方 settings 的 SettingsScopeController
|
|
9
|
+
* 自带 schema 校验(packages/client/ui-settings/src/client/settings-scope.ts:202),
|
|
10
|
+
* 但那只覆盖**官方 settings 通道**,本插件的自有 REST 通道没有这层保护。
|
|
11
|
+
* 前提检查:"外部数据一定符合 src/types.ts 声明的形状"这个前提**已被实测证伪**:
|
|
12
|
+
* host 的 JobRegistry.start() 返回裸 job id 字符串(docs/REST-CONTRACT.md 写的是
|
|
13
|
+
* `{ jobId }`),客户端把字符串当成报告,HealthPanel 在 `report.counts[layer]` 上抛
|
|
14
|
+
* TypeError,官方 SlotErrorBoundary 渲染一个空 div —— 用户看到的就是整页空白。
|
|
15
|
+
*
|
|
16
|
+
* 纪律(本模块存在的全部理由):
|
|
17
|
+
* 1. 归一**只发生在数据入口**(shared.ts 的控制器),组件里不再散落 `?? []`;
|
|
18
|
+
* 这样"能通过类型检查的字段"与"运行期真的存在的字段"重新合一。
|
|
19
|
+
* 2. 归一**不编造事实**:缺字段补的是"空/未知"([] / 0 / undefined),不是看似合理的值;
|
|
20
|
+
* 整份载荷不可用(不是对象、缺关键标识)时返回 undefined,由控制器如实报"失败"。
|
|
21
|
+
* 3. 归一**不吞发现**:诊断报告里的每条 issue 都保留(枚举值未知时保留原值,
|
|
22
|
+
* 由组件回退成原始 id 显示),不因为一个字段不认识就丢掉这条发现。
|
|
23
|
+
*
|
|
24
|
+
* 类型纪律:`src/types.ts` 是 wire 契约的唯一权威;本模块只做"运行期兑现契约",
|
|
25
|
+
* 不新增字段、不改语义。归一后的对象带完整类型,组件按类型读即可。
|
|
26
|
+
*/
|
|
27
|
+
import type { DiagnosticLayer, DiagnosticReport, EnvironmentBackup, EnvironmentBackupDiff, EnvironmentInfo, EnvironmentResult, GatedInstallResult, KindListResult, MarketplaceResult } from '../types.ts';
|
|
28
|
+
import type { OfficialCapabilities } from '../official.ts';
|
|
29
|
+
import type { CompanionConfig, TrialConfig } from '../settings.ts';
|
|
30
|
+
/**
|
|
31
|
+
* 归一官方能力探针。
|
|
32
|
+
*
|
|
33
|
+
* 界面只读 `missing`(哪里能力缺失要如实告诉用户)。探针读不到时返回 undefined,
|
|
34
|
+
* 调用方保持"未探到",绝不伪造"能力齐全"。
|
|
35
|
+
*
|
|
36
|
+
* @param raw - capabilities op 的原始值。
|
|
37
|
+
* @returns 探针结果;不可用时 undefined。
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeCapabilities(raw: unknown): OfficialCapabilities | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* 一条可能读不到的事实(与 host 的 `AboutFact<T>` 同构)。
|
|
42
|
+
*
|
|
43
|
+
* 客户端**不做**二次归一(例如把 unknown 折成空串):那会抹掉"读不到"与"读到了空值"的区别,
|
|
44
|
+
* 而界面正是靠这个区别决定显示"未知"还是显示值(§12.3.3)。
|
|
45
|
+
*/
|
|
46
|
+
export type AboutFactView<T> = {
|
|
47
|
+
readonly value: T;
|
|
48
|
+
readonly source: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly unknown: string;
|
|
51
|
+
};
|
|
52
|
+
/** 「关于」页的全部事实(客户端视图)。 */
|
|
53
|
+
export interface AboutFactsView {
|
|
54
|
+
readonly runtime: {
|
|
55
|
+
readonly version: AboutFactView<string>;
|
|
56
|
+
readonly installAnchor: AboutFactView<string>;
|
|
57
|
+
};
|
|
58
|
+
readonly process: {
|
|
59
|
+
readonly node: AboutFactView<string>;
|
|
60
|
+
readonly platform: AboutFactView<string>;
|
|
61
|
+
readonly arch: AboutFactView<string>;
|
|
62
|
+
};
|
|
63
|
+
readonly companion: {
|
|
64
|
+
readonly version: AboutFactView<string>;
|
|
65
|
+
};
|
|
66
|
+
readonly profile: {
|
|
67
|
+
readonly name: AboutFactView<string>;
|
|
68
|
+
readonly dir: AboutFactView<string>;
|
|
69
|
+
};
|
|
70
|
+
readonly files: {
|
|
71
|
+
readonly settingsPath: AboutFactView<string>;
|
|
72
|
+
readonly registryCachePath: AboutFactView<string>;
|
|
73
|
+
readonly registryCacheAgeMs: AboutFactView<number>;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 归一 `about` op 的结果。
|
|
78
|
+
*
|
|
79
|
+
* @param raw - op 的原始值。
|
|
80
|
+
* @returns 视图;载荷整体不可用时 undefined(界面显示"读不到"并给重试,而不是画一张空表)。
|
|
81
|
+
*/
|
|
82
|
+
export declare function normalizeAbout(raw: unknown): AboutFactsView | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* 归一一次诊断的报告。
|
|
85
|
+
*
|
|
86
|
+
* 各层计数由**归一后的发现**重新统计(而不是信线缆上的 counts):界面上的"各层计数"
|
|
87
|
+
* 必须与用户能数出来的列表一致,两者矛盾时以列表为准。传入的 `layers` 决定总览网格
|
|
88
|
+
* 里哪些层有确定的 0 值(缺层不能渲染成 undefined)。
|
|
89
|
+
*
|
|
90
|
+
* @param raw - diagnose job 的结果。
|
|
91
|
+
* @param layers - 已知的层(本插件来自 shared.ts 的 LAYER_ORDER)。
|
|
92
|
+
* @returns 报告;整份载荷不可用时 undefined(调用方如实报失败,不渲染半个报告)。
|
|
93
|
+
*/
|
|
94
|
+
export declare function normalizeReport(raw: unknown, layers: readonly DiagnosticLayer[]): DiagnosticReport | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* 归一环境列表。
|
|
97
|
+
*
|
|
98
|
+
* 环境名是这张表的唯一标识(也是后续所有跨环境操作的参数),缺名字的项无法渲染也
|
|
99
|
+
* 无法操作,直接丢弃。
|
|
100
|
+
*
|
|
101
|
+
* @param raw - listEnvironments op 的结果。
|
|
102
|
+
* @returns 环境列表;载荷不可用(不是数组)时 undefined。
|
|
103
|
+
*/
|
|
104
|
+
export declare function normalizeEnvironments(raw: unknown): EnvironmentInfo[] | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* 归一一次变更类操作的结果。
|
|
107
|
+
*
|
|
108
|
+
* `output` 是给用户看的原始诊断,缺失时不能把 undefined 塞进 Toast(那会渲染成空提示)。
|
|
109
|
+
*
|
|
110
|
+
* @param raw - 操作结果。
|
|
111
|
+
* @returns 结果;不是对象时按失败处理(ok=false),绝不当作成功。
|
|
112
|
+
*/
|
|
113
|
+
export declare function normalizeEnvironmentResult(raw: unknown): EnvironmentResult;
|
|
114
|
+
/**
|
|
115
|
+
* 归一一份环境备份(导出结果与用户导入的文件走同一个入口)。
|
|
116
|
+
*
|
|
117
|
+
* 归一失败意味着"这不是本插件的备份":`backupRestore` 会据此改写目标环境的 manifest,
|
|
118
|
+
* 宁可拒绝也不能猜。
|
|
119
|
+
*
|
|
120
|
+
* @param raw - 备份文档。
|
|
121
|
+
* @returns 备份;形状不符时 undefined。
|
|
122
|
+
*/
|
|
123
|
+
export declare function normalizeBackup(raw: unknown): EnvironmentBackup | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* 归一备份差异。
|
|
126
|
+
*
|
|
127
|
+
* 五个分类都会渲染,缺一个就是渲染期崩溃——所以每一类都归一成数组。
|
|
128
|
+
*
|
|
129
|
+
* @param raw - backupDiff op 的结果。
|
|
130
|
+
* @returns 差异;载荷不可用时 undefined。
|
|
131
|
+
*/
|
|
132
|
+
export declare function normalizeBackupDiff(raw: unknown): EnvironmentBackupDiff | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* 归一市场查询结果。
|
|
135
|
+
*
|
|
136
|
+
* @param raw - marketplace op 的结果。
|
|
137
|
+
* @returns 结果;载荷不可用时 undefined。
|
|
138
|
+
*/
|
|
139
|
+
export declare function normalizeMarketplace(raw: unknown): MarketplaceResult | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* 归一受质量门保护的安装结果。
|
|
142
|
+
*
|
|
143
|
+
* @param raw - install op 的结果。
|
|
144
|
+
* @returns 结果;载荷不可用时按失败处理。
|
|
145
|
+
*/
|
|
146
|
+
export declare function normalizeGatedInstall(raw: unknown): GatedInstallView;
|
|
147
|
+
/**
|
|
148
|
+
* 一次安装里的试装结论(客户端视图)。
|
|
149
|
+
*
|
|
150
|
+
* 枚举值(conclusion / policy / depth / baseline / candidate)**原样保留字符串**,
|
|
151
|
+
* 不在这里映射成已知集合:宿主新增一档结论时,界面要能如实显示原始值,而不是把它吞掉
|
|
152
|
+
* 或猜成最接近的一档(本模块纪律 3:归一不吞发现)。文案映射在组件层,未知值回退显示原值。
|
|
153
|
+
*/
|
|
154
|
+
export interface TrialOutcomeView {
|
|
155
|
+
readonly conclusion: string;
|
|
156
|
+
readonly policy: string;
|
|
157
|
+
readonly policyNote: string;
|
|
158
|
+
readonly output: string;
|
|
159
|
+
readonly elapsedMs: number;
|
|
160
|
+
readonly escalated: boolean;
|
|
161
|
+
readonly depth?: string;
|
|
162
|
+
readonly escalationReason?: string;
|
|
163
|
+
readonly baseline?: string;
|
|
164
|
+
readonly candidate?: string;
|
|
165
|
+
}
|
|
166
|
+
/** 受质量门保护的安装结果的客户端视图:`trial` 换成上面那个视图。 */
|
|
167
|
+
export type GatedInstallView = Omit<GatedInstallResult, 'trial'> & {
|
|
168
|
+
readonly trial?: TrialOutcomeView;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* 试装开启前必须让用户看到的事实。
|
|
172
|
+
*
|
|
173
|
+
* 数字与口径**必须一起**给出:只有数字没有 measurement 时返回 undefined(宁可不显示,
|
|
174
|
+
* 也不给一个没有口径的数字——那正是"把不知道说成知道")。
|
|
175
|
+
*/
|
|
176
|
+
export interface TrialDisclosureView {
|
|
177
|
+
readonly executesCandidateCode: boolean;
|
|
178
|
+
readonly peakMemoryMiB: number;
|
|
179
|
+
readonly measurement: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 归一试装告知事实(来自 capabilities op 的 `trialDisclosure`)。
|
|
183
|
+
*
|
|
184
|
+
* @param raw - `capabilities.trialDisclosure`。
|
|
185
|
+
* @returns 告知事实;数字或口径缺失/不可读时 undefined(界面显示"未知",不硬编码)。
|
|
186
|
+
*/
|
|
187
|
+
export declare function normalizeTrialDisclosure(raw: unknown): TrialDisclosureView | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* 一个测试环境(`<真实环境名>-dpmc`)的只读事实。
|
|
190
|
+
*
|
|
191
|
+
* 布尔事实用 `undefined` 表示"宿主没给",而不是 `false`:`false` 是一个结论
|
|
192
|
+
* ("不在运行"、"归属环境没了"),不能拿它顶替"读不到"(DESIGN §12.3.3)。
|
|
193
|
+
*/
|
|
194
|
+
export interface TrialEnvironmentView {
|
|
195
|
+
readonly name: string;
|
|
196
|
+
readonly owner: string;
|
|
197
|
+
readonly ownerExists: boolean | undefined;
|
|
198
|
+
readonly dir: string;
|
|
199
|
+
readonly running: boolean | undefined;
|
|
200
|
+
readonly modifiedAt: string;
|
|
201
|
+
readonly ageDays: number | undefined;
|
|
202
|
+
readonly bytes: number | null;
|
|
203
|
+
readonly bytesReason?: string;
|
|
204
|
+
readonly files: number;
|
|
205
|
+
readonly sharedFiles: number;
|
|
206
|
+
readonly snapshotMatchesOwner: boolean | null;
|
|
207
|
+
}
|
|
208
|
+
/** 试装环境查询结果的客户端视图(`trialEnvironments` op)。 */
|
|
209
|
+
export interface TrialEnvironmentsView {
|
|
210
|
+
readonly environments: readonly TrialEnvironmentView[];
|
|
211
|
+
readonly factsReadable: boolean | undefined;
|
|
212
|
+
readonly factsReason?: string;
|
|
213
|
+
readonly totals: {
|
|
214
|
+
readonly count: number;
|
|
215
|
+
readonly running: number | undefined;
|
|
216
|
+
readonly bytes: number;
|
|
217
|
+
readonly unknownBytes: number;
|
|
218
|
+
};
|
|
219
|
+
readonly retention: {
|
|
220
|
+
readonly days: number;
|
|
221
|
+
readonly autoCleanup: boolean;
|
|
222
|
+
readonly maxKept: number;
|
|
223
|
+
} | undefined;
|
|
224
|
+
readonly plan: {
|
|
225
|
+
readonly remove: readonly {
|
|
226
|
+
readonly name: string;
|
|
227
|
+
readonly reason: string;
|
|
228
|
+
}[];
|
|
229
|
+
readonly keep: readonly {
|
|
230
|
+
readonly name: string;
|
|
231
|
+
readonly reason: string;
|
|
232
|
+
}[];
|
|
233
|
+
} | undefined;
|
|
234
|
+
readonly overCap: boolean | undefined;
|
|
235
|
+
readonly notes: readonly string[];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 归一试装环境查询结果。
|
|
239
|
+
*
|
|
240
|
+
* @param raw - `trialEnvironments` op 的结果。
|
|
241
|
+
* @returns 视图;载荷不是对象时 undefined(控制器如实报"失败",不渲染空列表当"没有测试环境")。
|
|
242
|
+
*/
|
|
243
|
+
export declare function normalizeTrialEnvironments(raw: unknown): TrialEnvironmentsView | undefined;
|
|
244
|
+
/** 一次清理的结果(`trialCleanup` op):环境操作结果 + 实际删掉的名字。 */
|
|
245
|
+
export interface TrialCleanupView extends EnvironmentResult {
|
|
246
|
+
readonly removed: readonly string[];
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 归一一次试装环境清理的结果。
|
|
250
|
+
*
|
|
251
|
+
* @param raw - `trialCleanup` op 的结果。
|
|
252
|
+
* @returns 结果;载荷不可用时按失败处理(沿用环境操作结果的归一口径)。
|
|
253
|
+
*/
|
|
254
|
+
export declare function normalizeTrialCleanup(raw: unknown): TrialCleanupView;
|
|
255
|
+
/**
|
|
256
|
+
* 归一技能与预设列表。
|
|
257
|
+
*
|
|
258
|
+
* @param raw - listKinds op 的结果。
|
|
259
|
+
* @returns 结果;载荷不可用时 undefined。
|
|
260
|
+
*/
|
|
261
|
+
export declare function normalizeKindList(raw: unknown): KindListResult | undefined;
|
|
262
|
+
/**
|
|
263
|
+
* 归一后的插件配置。
|
|
264
|
+
*
|
|
265
|
+
* 与 host 侧 {@link CompanionConfig} 的唯一差别:`trial` 在 host 类型里是**可选**字段
|
|
266
|
+
* (见 src/settings.ts 的说明),而归一后它一定存在——客户端不假设宿主给了它。
|
|
267
|
+
*/
|
|
268
|
+
export type ClientConfig = CompanionConfig & {
|
|
269
|
+
readonly trial: TrialConfig;
|
|
270
|
+
};
|
|
271
|
+
/** 归一后的配置与"哪些字段是补出来的"。 */
|
|
272
|
+
export interface NormalizedConfig {
|
|
273
|
+
readonly config: ClientConfig;
|
|
274
|
+
/** 由默认值补上的字段路径(用于如实告诉用户:这不是宿主给的完整配置)。 */
|
|
275
|
+
readonly filled: readonly string[];
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 归一插件配置:逐字段校验,缺失/类型不符的用客户端默认值补齐。
|
|
279
|
+
*
|
|
280
|
+
* 为什么是"补齐后渲染"而不是"提示尚未就绪":这个表单写的是**字段级** op(官方 settings
|
|
281
|
+
* 的 mutate 按路径写),补出来的字段不会被回写;而用户点开设置页的目的正是看到并改一个
|
|
282
|
+
* 具体字段——空白或只有一句提示等于什么都做不了。补的是默认值这一事实会通过
|
|
283
|
+
* `filled` 如实显示出来,不伪装成"宿主就是这么配的"。
|
|
284
|
+
*
|
|
285
|
+
* @param raw - settings 快照里的命名空间值(允许残缺)。
|
|
286
|
+
* @returns 归一后的配置与补出来的字段;`raw` 不是对象时 undefined(连一个字段都没有)。
|
|
287
|
+
*/
|
|
288
|
+
export declare function normalizeConfig(raw: unknown): NormalizedConfig | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* 一条 dist-tag 的客户端视图。
|
|
291
|
+
*
|
|
292
|
+
* line 与 preferred **原样保留字符串/布尔**,不在这里映射成已知集合:host 新增一档线时,
|
|
293
|
+
* 界面要能如实显示(未知线一律按"另一条线"提示,宁可多提醒一次也不谎报"同线")。
|
|
294
|
+
*/
|
|
295
|
+
export interface UpgradeTagView {
|
|
296
|
+
readonly tag: string;
|
|
297
|
+
readonly version: string;
|
|
298
|
+
readonly line: string;
|
|
299
|
+
readonly preferred: boolean;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* 一个升级单元的客户端视图。
|
|
303
|
+
*
|
|
304
|
+
* state 原样保留:四态由 host 判定(docs/REST-CONTRACT.md),客户端**不自己推断**
|
|
305
|
+
* (wire.ts 纪律 3:归一不吞发现)。未知状态值由 upgradeView.rowKindOf 归到 unknown,
|
|
306
|
+
* 界面据此显示"查不到"而不是"已是最新"。
|
|
307
|
+
*/
|
|
308
|
+
export interface UpgradeUnitView {
|
|
309
|
+
readonly name: string;
|
|
310
|
+
readonly kind: string;
|
|
311
|
+
readonly state: string;
|
|
312
|
+
readonly currentVersion: string | null;
|
|
313
|
+
readonly currentLine: string | null;
|
|
314
|
+
readonly spec?: string;
|
|
315
|
+
readonly targetVersion: string | null;
|
|
316
|
+
readonly targetTag: string | null;
|
|
317
|
+
readonly targetLine: string | null;
|
|
318
|
+
readonly tags: readonly UpgradeTagView[] | null;
|
|
319
|
+
readonly source?: string;
|
|
320
|
+
readonly at?: string;
|
|
321
|
+
readonly reason?: string;
|
|
322
|
+
readonly changesSource?: boolean;
|
|
323
|
+
readonly command?: string;
|
|
324
|
+
}
|
|
325
|
+
/** 升级检查结果的客户端视图(op: upgradeCheck)。 */
|
|
326
|
+
export interface UpgradeCheckView {
|
|
327
|
+
readonly environment: string;
|
|
328
|
+
readonly units: readonly UpgradeUnitView[];
|
|
329
|
+
readonly checked: boolean;
|
|
330
|
+
readonly lastCheckAt: string | null;
|
|
331
|
+
readonly notes: readonly string[];
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* 归一一次升级检查结果。
|
|
335
|
+
*
|
|
336
|
+
* 载荷不是对象时返回 undefined:控制器据此如实报"失败",**绝不**把它画成"没有可升级的包"
|
|
337
|
+
* (那是把故障说成结论)。units 缺失时给空数组——"宿主说了一个单元都没有"与"读不出来"
|
|
338
|
+
* 在契约里是两回事,前者合法。
|
|
339
|
+
*
|
|
340
|
+
* @param raw - upgradeCheck op 的结果。
|
|
341
|
+
* @returns 视图;载荷不可用时 undefined。
|
|
342
|
+
*/
|
|
343
|
+
export declare function normalizeUpgradeCheck(raw: unknown): UpgradeCheckView | undefined;
|
|
344
|
+
/**
|
|
345
|
+
* 一次升级的金丝雀(试装)结论的客户端视图。
|
|
346
|
+
*
|
|
347
|
+
* ran 与 conclusion **都保留**:ran===false 时 conclusion 缺省,界面必须能从 ran 看出
|
|
348
|
+
* "这次没验证"(DESIGN §5.5:没验证不等于通过)。
|
|
349
|
+
*/
|
|
350
|
+
export interface UpgradeCanaryView {
|
|
351
|
+
readonly ran: boolean;
|
|
352
|
+
readonly conclusion?: string;
|
|
353
|
+
readonly depth?: string;
|
|
354
|
+
readonly escalated: boolean;
|
|
355
|
+
readonly elapsedMs?: number;
|
|
356
|
+
readonly output?: string;
|
|
357
|
+
readonly skippedReason?: string;
|
|
358
|
+
readonly cleanup: string;
|
|
359
|
+
readonly activated?: boolean;
|
|
360
|
+
readonly bundles?: readonly string[];
|
|
361
|
+
}
|
|
362
|
+
/** 一次升级结果的客户端视图(op: upgrade)。 */
|
|
363
|
+
export interface UpgradeActionView extends EnvironmentResult {
|
|
364
|
+
readonly name: string;
|
|
365
|
+
readonly fromVersion: string | null;
|
|
366
|
+
readonly toVersion: string;
|
|
367
|
+
readonly spec: string;
|
|
368
|
+
readonly canary?: UpgradeCanaryView;
|
|
369
|
+
readonly diskFacts: readonly string[];
|
|
370
|
+
readonly restartRequired: boolean;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* 归一一次升级结果。
|
|
374
|
+
*
|
|
375
|
+
* 沿用环境操作结果的归一口径:载荷不可用时按失败处理(ok=false),绝不当作成功。
|
|
376
|
+
* canary 缺失时**不补**一个"没跑"——"宿主没给这个字段"与"金丝雀没跑"是两件事,
|
|
377
|
+
* 前者界面不许宣称任何验证结论。
|
|
378
|
+
*
|
|
379
|
+
* @param raw - upgrade op 的结果。
|
|
380
|
+
* @returns 视图。
|
|
381
|
+
*/
|
|
382
|
+
export declare function normalizeUpgradeAction(raw: unknown): UpgradeActionView;
|
|
383
|
+
/** 一次回滚结果的客户端视图(op: upgradeRollback)。 */
|
|
384
|
+
export interface UpgradeRollbackView extends EnvironmentResult {
|
|
385
|
+
readonly name: string;
|
|
386
|
+
readonly fromVersion: string | null;
|
|
387
|
+
readonly toVersion: string;
|
|
388
|
+
readonly diskFacts: readonly string[];
|
|
389
|
+
/** 盘上核对是否一致;读不出来时 undefined(**不是** false——"读不出来"不是"不干净")。 */
|
|
390
|
+
readonly clean: boolean | undefined;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* 归一一次回滚结果。
|
|
394
|
+
*
|
|
395
|
+
* clean 读不出来时给 undefined 而不是 false:false 是一个结论("有残留"),
|
|
396
|
+
* 不能拿它顶替"读不到"(DESIGN §12.3.3)。
|
|
397
|
+
*
|
|
398
|
+
* @param raw - upgradeRollback op 的结果。
|
|
399
|
+
* @returns 视图。
|
|
400
|
+
*/
|
|
401
|
+
export declare function normalizeUpgradeRollback(raw: unknown): UpgradeRollbackView;
|