dsh-plugin-update 0.2.0 → 0.3.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.
@@ -0,0 +1,256 @@
1
+ import { type VisibleQueue } from './queue.js';
2
+ import type { BlockedReason, UpdateSnapshot } from './ports.js';
3
+ /** 摆放形态:默认内嵌,切弹窗走同一参数。 */
4
+ export type UpdatePanelMode = 'embedded' | 'dialog';
5
+ /** 面板主题:默认最小可用样式;`d5-paper` 为可选 D5 档案卷专业主题(只换肤,不换 DOM 顺序)。 */
6
+ export type UpdatePanelTheme = 'default' | 'd5-paper';
7
+ /** 与宿主通话的传输函数:面板只认这个签名,不认任何宿主对象的具体形状。 */
8
+ export type UpdatePanelCall = (phoneName: string, args: Record<string, unknown>) => Promise<Record<string, unknown>>;
9
+ /** 复制文本的出口:不传即尝试浏览器剪贴板,都没有则只留“已生成、请手动复制”提示。 */
10
+ export type UpdatePanelCopyText = (text: string) => void | Promise<void>;
11
+ /** 面板可点的动作(与 HTML 里 data-action 一一对应,测试走同一条路)。 */
12
+ export type UpdatePanelActionKind = 'check' | 'install' | 'skip' | 'reset-skip' | 'copy-manual' | 'copy-diag' | 'toggle-queue' | 'restart-hint' | 'close-view';
13
+ export interface UpdatePanelOptions {
14
+ /** 插件标识:必填,与宿主侧 createHostUpdate 传的 pluginId 一致。 */
15
+ pluginId: string;
16
+ /** 电话名前缀:默认 wf,必须与宿主侧一致(取值只从这里算,不写字面量)。 */
17
+ prefix?: string;
18
+ /** 摆放形态:默认内嵌。 */
19
+ mode?: UpdatePanelMode;
20
+ /** 面板主题:默认 `default`(最小可用样式,一字不动);传 `d5-paper` 切 D5 档案卷可选主题。 */
21
+ theme?: UpdatePanelTheme;
22
+ /** 是否看他人排队明细:默认只看自己的(他人仅露正忙占位,位置照给)。 */
23
+ showOthers?: boolean;
24
+ /** 轮询间隔毫秒:默认 1000,不得小于 250。 */
25
+ pollMs?: number;
26
+ /** 与宿主通话的函数(面板侧唯一的宿主接触面)。 */
27
+ call: UpdatePanelCall;
28
+ copyText?: UpdatePanelCopyText;
29
+ skipStore?: PanelSkipStore;
30
+ /** 宿主种类(调用方知道就传进诊断文本;不传就用宿主 includeEnv 回的宿主种类,再没有显示“未知”)。 */
31
+ hostKind?: string | null;
32
+ /**
33
+ * 使用范围名(profile):**装到哪个范围**的展示面。不传就用宿主 includeEnv 回的真值;
34
+ * 两者都没有时显示“未知”,绝不猜(猜错会让人以为更新装到了别的范围)。
35
+ */
36
+ profileName?: string | null;
37
+ /**
38
+ * 「重启宿主」按钮的落地(可选):宿主没有重启自己的电话,默认点击只提示手动重启;
39
+ * 调用方能把重启流程接进来(拉起自己的重启脚本/提示用户),按钮就交给它。
40
+ */
41
+ onRestartRequested?: () => void | Promise<void>;
42
+ diagCopyFormat?: DiagCopyFormat;
43
+ /**
44
+ * 更新日志 Markdown(#23 包内 CHANGELOG 展示):
45
+ * 调用方按“已装版离线读、新版按需取 tarball”备好后传入(取不到传 null/空串即中性提示);
46
+ * 面板只渲染不取数,缺日志永不挡安装、不写 blockedReason。
47
+ */
48
+ changelogMarkdown?: string | null;
49
+ }
50
+ /** 挂载点:只要有 innerHTML 的容器即可(浏览器元素或测试替身都行)。 */
51
+ export interface UpdatePanelContainer {
52
+ innerHTML: string;
53
+ addEventListener?: (type: string, listener: (ev: unknown) => void) => void;
54
+ removeEventListener?: (type: string, listener: (ev: unknown) => void) => void;
55
+ }
56
+ export interface UpdatePanelController {
57
+ /** 立刻重查一次并重绘(挂载时自动调一次,重开面板即恢复进度)。 */
58
+ refresh(): Promise<void>;
59
+ /** 点一次按钮(DOM 监听只是它的薄适配,测试直接调它)。 */
60
+ act(kind: UpdatePanelActionKind, arg?: string): Promise<void>;
61
+ /** 切换摆放(同一状态重绘同一内核,只换外层包裹)。 */
62
+ setMode(mode: UpdatePanelMode): Promise<void>;
63
+ /** 切换主题(同一内核重绘,只换肤;默认主题输出与旧版一字不差)。 */
64
+ setTheme(theme: UpdatePanelTheme): Promise<void>;
65
+ /** 切换排队可见性(重查一次,位置口径不变)。 */
66
+ setShowOthers(show: boolean): Promise<void>;
67
+ /**
68
+ * 换一版更新日志后重绘(#23:宿主侧备好新文本后调用;传 null/空串即回中性提示)。
69
+ * 只换日志节,安装门控只跟快照,一字不动。
70
+ */
71
+ setChangelogMarkdown(markdown: string | null): void;
72
+ /** 卸载:只停轮询、只拆监听,绝不中断安装。 */
73
+ unmount(): void;
74
+ }
75
+ /** 面板本地的跳过读写承诺:按版本记、可重置(宿主侧 skipped.json 是另一套落盘,互不干扰)。 */
76
+ export interface PanelSkipStore {
77
+ list(): string[];
78
+ has(version: string): boolean;
79
+ skip(version: string): void;
80
+ reset(version?: string): void;
81
+ }
82
+ export interface BlockedCopy {
83
+ title: string;
84
+ action: string;
85
+ }
86
+ /** 装不了的原因 → 中文一句话(能装传 null 即回 null,不猜)。 */
87
+ export declare function blockedCopy(reason: BlockedReason | null): BlockedCopy | null;
88
+ export interface FailureCopy {
89
+ zh: string;
90
+ act: string;
91
+ }
92
+ /** 14 码全表是否包含该码(8 阻塞 + 5 电话 + internal;未来码不在此列,走兜底)。 */
93
+ export declare function isKnownFailureCode(code: unknown): boolean;
94
+ /**
95
+ * 稳定码 → 中文一句话(14 码全覆盖;未知码回未来兜底,空码回 null)。
96
+ * 8 阻塞行复用 BLOCKED_COPY 原文,不另起措辞;面板分支只认返回值,不碰 diag。
97
+ */
98
+ export declare function failureCopy(code: unknown): FailureCopy | null;
99
+ /**
100
+ * 失败回包取稳定码:只认 errorKind,error 仅作回退(#22 验收:errorKind=internal 时
101
+ * 不许误判为查新版失败;包内已把未知码收敛为 error=check-failed/errorKind=internal,
102
+ * 此时 error 那格不可信)。缺省回 internal,不抛错。
103
+ */
104
+ export declare function failureCodeOf(reply: {
105
+ error?: unknown;
106
+ errorKind?: unknown;
107
+ } | null | undefined, fallback?: unknown): string;
108
+ /** 诊断复制预算(单源:值取 redaction.COPY_BUDGET_CHARS,此处只转出口,老名保留兼容)。 */
109
+ export declare const PANEL_DIAG_MAX_CHARS = 1500;
110
+ /** 复制前把一行文本收干净(确定性:同一输入两次运行逐字节相同;实现见 redaction.sanitizeForCopy)。 */
111
+ export declare function redactForCopy(text: unknown): string;
112
+ export interface PanelDiagnosticInput {
113
+ pluginId: string;
114
+ /** 稳定码(失败回包的 errorKind 优先,error 仅回退;任务 message 取前缀码)。 */
115
+ code: string;
116
+ /** 脱敏前的详情(任务 message 去掉前缀码剩下的部分,可空)。 */
117
+ detail?: string | null;
118
+ runningVersion?: string | null;
119
+ installedVersion?: string | null;
120
+ latestVersion?: string | null;
121
+ hostKind?: string | null;
122
+ /** 使用范围名:装到哪个 profile 是排错第一信息;不传则那一段不出现(输出与旧版一字不差)。 */
123
+ profileName?: string | null;
124
+ queuePosition?: number | null;
125
+ requestId?: string | null;
126
+ manual?: string | null;
127
+ /** 电话侧 diag(#21 落定前多半没有;有则宽容读,无则走显式字段,缺省说人话)。 */
128
+ diag?: unknown;
129
+ /** 显式来源(diag 没有时用;有 diag 时显式优先,缺省仍说人话,不留白)。 */
130
+ route?: string | null;
131
+ checkId?: string | null;
132
+ }
133
+ /** 组出一段自包含诊断(调用方直接拿去粘工单/issue;粘之前已脱敏,不必手检)。 */
134
+ export declare function buildDiagnosticText(input: PanelDiagnosticInput): string;
135
+ export type DiagCopyFormat = 'block' | 'line';
136
+ export interface TolerantDiag {
137
+ stage: string | null;
138
+ route: string | null;
139
+ method: string | null;
140
+ httpStatus: number | null;
141
+ exitCode: number | null;
142
+ latencyMs: number | null;
143
+ detail: string | null;
144
+ targetPackageName: string | null;
145
+ runningVersion: string | null;
146
+ latestVersion: string | null;
147
+ environmentKind: string | null;
148
+ requestId: string | null;
149
+ checkId: string | null;
150
+ registryHost: string | null;
151
+ action: string | null;
152
+ /** 目录外的键(如 queuePos 转正前)一律忽略,记在这里仅供排错,不进渲染。 */
153
+ unknownKeys: string[];
154
+ }
155
+ /** 宽容读 diag:未知键忽略、错类型忽略、缺省当正常(永不抛;面板分支永不用它)。 */
156
+ export declare function readDiagTolerant(diag: unknown): TolerantDiag;
157
+ export interface UpdateDiagCopyInput extends PanelDiagnosticInput {
158
+ /** 复制形态:默认三行块;单行用于单行输入框,内容顺序与块完全一致。 */
159
+ format?: DiagCopyFormat;
160
+ }
161
+ /**
162
+ * 组出 [update-diag] 复制块(双形态同序;调用方直接拿去粘工单/issue,已脱敏)。
163
+ * 第一性:顺序码→摘要→来源(#18 定),怎么办是面板页脚放最后,不插断三元组;
164
+ * 缺省即省略(#18):路由/请求/检查/阶段等缺失即不出现,不占位“未知”;
165
+ * 插件/版本/宿主/队列恒显(面板侧显式值兜底),摘要缺省给人话,源缺省给人话(省略本身即信息,人读不懂所以必须说)。
166
+ */
167
+ export declare function buildUpdateDiagCopy(input: UpdateDiagCopyInput): string;
168
+ export declare function createMemorySkipStore(now?: () => number): PanelSkipStore;
169
+ /** 浏览器跳过存储(localStorage 形状即可;没有或写坏退内存,绝不因跳过挡住更新)。 */
170
+ export declare function createBrowserSkipStore(pluginId: string, storage?: unknown, now?: () => number): PanelSkipStore;
171
+ export interface PanelBanner {
172
+ kind: 'loading' | 'restart' | 'blocked' | 'update' | 'busy' | 'failed' | 'done' | 'idle';
173
+ title: string;
174
+ action: string;
175
+ }
176
+ export interface PanelView {
177
+ banner: PanelBanner;
178
+ installEnabled: boolean;
179
+ installLabel: string;
180
+ skippedLatest: boolean;
181
+ showManual: boolean;
182
+ showReset: boolean;
183
+ queueNote: string | null;
184
+ /** 大印章(状态词)与小印章(一字):取值与原型 `d5-paper.html:431-432` 的状态映射一一对应。 */
185
+ seal: PanelSeal;
186
+ }
187
+ /** 印章色调:与原型 `seal-ink / seal-green / seal-yellow / seal-red` 四个类同名同义。 */
188
+ export type PanelSealTone = 'ink' | 'green' | 'yellow' | 'red';
189
+ export interface PanelSeal {
190
+ /** 大印章文字:待查 / 可装 / 安装中 / 待重启 / 受阻 / 已最新。 */
191
+ text: string;
192
+ /** 小印章文字:查 / 装 / 启 / 阻 / 定(一字,与原型 sealmini 同口径)。 */
193
+ mini: string;
194
+ tone: PanelSealTone;
195
+ }
196
+ export interface PanelViewInput {
197
+ snapshot: UpdateSnapshot | null;
198
+ manual: string | null;
199
+ queue: VisibleQueue | null;
200
+ skippedLatest: boolean;
201
+ lastError: string | null;
202
+ /** 失败回包的 errorKind(有则优先于 lastError;internal 时不许误判为查新版失败)。 */
203
+ errorKind?: string | null;
204
+ /**
205
+ * 更新日志 Markdown(#23):只影响日志节的渲染,不参与安装门控。
206
+ * 缺省/空串即中性提示;快照六字段与 blockedReason 一字不动。
207
+ */
208
+ changelogMarkdown?: string | null;
209
+ }
210
+ export declare function panelViewModel(input: PanelViewInput): PanelView;
211
+ export declare const UPDATE_PANEL_CSS: string;
212
+ export declare const UPDATE_PANEL_D5_CSS: string;
213
+ export interface PanelRenderInput extends PanelViewInput {
214
+ mode: UpdatePanelMode;
215
+ showOthers: boolean;
216
+ pluginId: string;
217
+ copyNotice: string | null;
218
+ /** 可选主题:不传即默认(输出与旧版一字不差);`d5-paper` 切 D5 档案卷。 */
219
+ theme?: UpdatePanelTheme;
220
+ /** 使用范围名(profile):面板「使用范围」一栏的唯一来源,缺省显示“未知”,不猜。 */
221
+ profileName?: string | null;
222
+ /** 宿主种类:进诊断文本;缺省显示“未知”。 */
223
+ hostKind?: string | null;
224
+ /**
225
+ * 动作面归谁:`default`(缺省)由内核画动作按钮;`none` 只画内容、不画按钮。
226
+ * 给「调用方自己提供动作面」的场景(如批量面板的详情:动作由批量面板经自己的通道提供)。
227
+ * 只读渲染下五章内容、进度条、「已跳过」提示一字不减,只是没有动作按钮。
228
+ */
229
+ actions?: 'default' | 'none';
230
+ }
231
+ /** 内核 HTML(双形态行为等价的根:同一视图产出同一内核,只换外层)。 */
232
+ export declare function renderUpdatePanelKernel(input: PanelRenderInput, view: PanelView): string;
233
+ /** 整面板 HTML(含样式;重绘即整体替换 innerHTML,故每次都带 style 也只留一份)。 */
234
+ export declare function renderUpdatePanelHTML(input: PanelRenderInput): string;
235
+ /**
236
+ * 挂载整组件(面板侧一行即跑):
237
+ * ```js
238
+ * const panel = mountUpdatePanel(document.getElementById('upd'), { pluginId: 'my-plugin', prefix: 'notes', call: host.call })
239
+ * // …离开时 panel.unmount()(只停轮询,安装在宿主侧继续跑)
240
+ * ```
241
+ */
242
+ export declare function mountUpdatePanel(container: UpdatePanelContainer, options: UpdatePanelOptions): UpdatePanelController;
243
+ export { buildPhoneNames as buildPanelPhoneNames } from './config.js';
244
+ export type { PhoneAction as PanelPhoneAction } from './config.js';
245
+ /** 面板轮询口径(默认 1 秒、下限 250 毫秒,与宿主侧同一套)。 */
246
+ export declare const PANEL_POLL: {
247
+ readonly defaultMs: 1000;
248
+ readonly minMs: 250;
249
+ };
250
+ export { QUEUE_INTENT_TTL_MS, emptyQueueState, normalizeQueueState, queuePositionOf, isHeadOfQueue, isQueueBusy, visibleQueueFor, } from './queue.js';
251
+ export type { QueuedEntry, QueueOwner, UpdateQueueState, VisibleQueue as PanelVisibleQueue, VisibleQueueOwner } from './queue.js';
252
+ export type { EnvironmentKind as PanelEnvironmentKind } from './ports.js';
253
+ export type { UpdateSnapshot as PanelSnapshot } from './ports.js';
254
+ export type { BlockedReason as PanelBlockedReason } from './ports.js';
255
+ export { CHANGELOG_ALL_CATEGORIES, CHANGELOG_FILENAME, CHANGELOG_FOLDED, CHANGELOG_MAX_BULLETS_PER_SECTION, CHANGELOG_MAX_BULLET_CHARS, CHANGELOG_MAX_CHARS, CHANGELOG_MAX_ENTRIES, CHANGELOG_MUST_SHOW, CHANGELOG_NEUTRAL_HINT, CHANGELOG_NEUTRAL_LINE, changelogForUpdate, hasVisibleSections, isUnreleasedVersion, parseChangelog, renderChangelogHTML, renderChangelogNeutral, renderChangelogSection, selectChangelogEntries, } from './changelog.js';
256
+ export type { ChangelogCategory, ChangelogEntry } from './changelog.js';