dsh-plugin-update 0.3.1 → 0.5.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/CHANGELOG.md +21 -0
- package/README.md +57 -13
- package/dist/client.d.ts +4 -4
- package/dist/client.js +2 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.js +8 -1
- package/dist/entry.d.ts +9 -2
- package/dist/entry.js +47 -12
- package/dist/host.d.ts +4 -4
- package/dist/host.js +77 -6
- package/dist/http.d.ts +2 -2
- package/dist/http.js +3 -2
- package/dist/panel-batch.d.ts +9 -0
- package/dist/panel-batch.js +125 -13
- package/dist/panel.d.ts +29 -2
- package/dist/panel.js +170 -25
- package/dist/service.d.ts +14 -0
- package/dist/service.js +50 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
格式为 Keep-a-Changelog 子集:`Added/Fixed/Changed` 必写,`Deprecated/Removed/Security` 有则透传,`Unreleased` 面板忽略,禁止 git-log 直倒。面板缺日志时中性提示,不挡安装。
|
|
4
4
|
|
|
5
|
+
## [0.5.0] - 2026-10-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 主题首选名 `archive`(档案卷纸面浅色):`d5-paper` 为旧别名仍可用,渲染逐字相同;入口件与批量面板同一套取值。
|
|
9
|
+
- 弹窗关闭落地 `onCloseRequested`:dialog 下点「关闭」/按 Esc 先交调用方撤 DOM 再停轮询;入口件打开的 dialog 已内置(收 dialog + 还原按钮 + 重查一次)。
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- 弹窗点「关闭」没反应:此前只停轮询不撤 DOM(入口件打开的 dialog 还会卡死),现走关闭落地。
|
|
13
|
+
- 悬停/聚焦闪烁:轮询每秒整树重写 innerHTML 打断 hover/focus,现输出逐字相同时不碰 DOM(状态变化仍即时重绘;单面板与批量面板同口径)。
|
|
14
|
+
|
|
15
|
+
## [0.4.0] - 2026-10-05
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- 更新日志无脑接入(#38):宿主新增 `updateChangelog` 电话(入参版本号,回该版 tarball 内全文,取不到回空;同源 + 精确路径 + `integrity` + 三墙复用,按版本记住结果);面板 `autoChangelog` 默认自动展示(有新版调一次,显式文本仍赢,`false` 退回手动);入口件透传跟上;批量详情行按行自动取(无新增批量电话,单行失败只影响该行);HTTP 版白名单放行,老三电话形状不变。
|
|
19
|
+
- 按钮交互反馈:查新版/安装在途置忙(禁用 + 文案 + `aria-busy` + 纯 CSS 转圈,并发连点只认第一次);默认主题补 hover / 按下下沉 / 过渡;busy 脉冲与 `prefers-reduced-motion` 关闭。
|
|
20
|
+
- 原生 `title` 说明:单面板全部动作按钮、批量宏按钮带悬停一句话(零成本 tooltip,不引入浮层组件)。
|
|
21
|
+
- Toast 语义:复制/重启类回执 5 秒后自动过期,不靠下次点击才消失。
|
|
22
|
+
- 首帧骨架:快照没到之前版本条画微光占位(`aria-hidden`,不进语义)。
|
|
23
|
+
- 批量「取消这一批」鼠标两步确认(第一次上膛红框,点别的自动卸膛;程序调 `act('cancel')` 仍一次即执行)。
|
|
24
|
+
- Esc 关弹窗:单面板 / 批量面板 dialog 与入口件 dialog 均支持(容器契约内,只用 innerHTML + 事件)。
|
|
25
|
+
|
|
5
26
|
## [0.3.1] - 2026-10-05
|
|
6
27
|
|
|
7
28
|
### Added
|
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
给 DSH 插件加「检查更新 / 安装更新」能力的 npm 包。宿主侧一段接线,面板侧挂一个现成组件,装不上时给用户一条可复制的手工命令。
|
|
4
4
|
|
|
5
|
-
要求 Node 22 或更高,零运行时依赖。当前版本 `0.
|
|
5
|
+
要求 Node 22 或更高,零运行时依赖。当前版本 `0.5.0`。
|
|
6
6
|
|
|
7
7
|
装上它你会拿到六样东西:
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- **四个电话**:查状态(只读本地)、查新版(用户点了才联网一次)、装更新(拿凭证提交)、取日志(按版取更新日志,全文或空)。电话指宿主对外提供的方法。
|
|
10
10
|
- **一套落盘**:任务状态、安装锁、回滚凭据,按「插件标识 + 使用范围」隔离,多插件互不干扰。
|
|
11
11
|
- **面板要的派生取值**:电话名与轮询间隔,构建期从本包生成,面板里不写死。
|
|
12
|
-
-
|
|
12
|
+
- **一个现成整组件**:默认内嵌、可切弹窗,调用者传参指定;轮询、安装门控、中文一句话、待重启横幅、手工命令展示与复制、排队可见开关、跳过与恢复、诊断一键复制、**更新日志自动展示**全在组件内部消化。
|
|
13
13
|
- **一个更新入口件**:配置页一行挂上就是「检查更新」按钮(或只给状态点的徽标、或整块内嵌),按钮文案随状态自己变;见第 2.5 节。
|
|
14
14
|
- **一套多目标批量更新**:一个插件管 N 个插件的更新(总账 + 明细 + 动作,一家收尾才起下一家,会话落盘可断点续跑);见第 2.6 节。
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ for (const [name, handler] of Object.entries(update.handlers)) {
|
|
|
52
52
|
}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
这一步得到四个电话名:`notes.updateStatus`、`notes.updateCheck`、`notes.updateInstall`、`notes.updateChangelog`(取日志:给版本号,回该版 tarball 里的更新日志全文,取不到回空)。
|
|
56
56
|
|
|
57
57
|
`pluginId` 必填(非空字符串,不含路径分隔符)。单例复用键强制含插件标识,多插件不串内存状态与锁。
|
|
58
58
|
除 `pluginId` 之外的配置都可选并带默认值,不传即走默认(见第 3 节)。
|
|
@@ -69,7 +69,7 @@ createHostUpdate(
|
|
|
69
69
|
|
|
70
70
|
本包是 ESM(`"type": "module"`):ESM 插件直接 `import`;CJS 插件用 `await import('dsh-plugin-update')`。
|
|
71
71
|
|
|
72
|
-
###
|
|
72
|
+
### 四个电话的入参与回参
|
|
73
73
|
|
|
74
74
|
面板与宿主两侧共用这一份契约(`…` 是你的前缀):
|
|
75
75
|
|
|
@@ -78,6 +78,7 @@ createHostUpdate(
|
|
|
78
78
|
| `….updateStatus` | `{}` | `{ ok: true, snapshot, manual, receipt: null }` | `{ ok: false, error, errorKind, diag? }` |
|
|
79
79
|
| `….updateCheck` | `{}` | `{ ok: true, snapshot, manual, receipt }` | 同上 |
|
|
80
80
|
| `….updateInstall` | `{ checkId, requestId }` | `{ ok: true, snapshot, manual, receipt: null }` | 同上 |
|
|
81
|
+
| `….updateChangelog` | `{ version }` | `{ ok: true, version, markdown: string \| null }` | 同上 |
|
|
81
82
|
|
|
82
83
|
- `snapshot` 恒为第 5.1 节那六个字段;`manual` 是第 5.3 节那条手工命令(能给则给,不能给为 `null`)。
|
|
83
84
|
- `receipt` 只有查新版给(`{ checkId, checkedAt, expiresAt }`);装更新时把 `checkId` 原样带回来,`requestId` 由面板自己生成(同一个编号重复提交直接返回旧结果)。
|
|
@@ -127,6 +128,7 @@ const panel = mountUpdatePanel(document.getElementById('update-slot'), {
|
|
|
127
128
|
call: (name, args) => host.call(name, args),
|
|
128
129
|
})
|
|
129
130
|
// 离开时 panel.unmount():只停轮询,安装在宿主侧继续跑;重开面板立刻重查,1 秒内恢复显示。
|
|
131
|
+
// 弹窗版想让「关闭」按钮真把弹窗撤掉:传 onCloseRequested(面板点关闭/Esc 时先调它撤 DOM,再停轮询;入口件打开的 dialog 已内置)。
|
|
130
132
|
```
|
|
131
133
|
|
|
132
134
|
组件内部消化的事(调用者不再写):按 `panelPollMs` 轮询查状态(下限 250 毫秒);安装按钮状态跟随快照的 `canInstall`,不另写门控规则;装不了的原因按第 5.2 节展示中文一句话;`pending-restart` 单独横幅加重启指引,不再给安装按钮;手工命令展示与复制;排队位置展示与 `showOthers` 开关;跳过按版本记(“已跳过 X.Y.Z · 恢复”在同一行,不藏进设置页);失败时旁边的“复制诊断”一键给出脱敏后的自包含文本(稳定码、版本、宿主、队列位置),深挖仍看日志(组件里留着第 6 节第 11 条的过滤口径)。
|
|
@@ -140,7 +142,7 @@ mountUpdatePanel(slot, { pluginId: 'my-notes-plugin', prefix: 'notes', call: hos
|
|
|
140
142
|
|
|
141
143
|
面板画成档案五章:**01 检查与安装、02 更新日志、03 更新队列、04 错误信息、05 手工命令**(五章恒在,缺内容给中性提示,不跳号)。档案头一行是「插件名 + 使用范围 + `profile` 牌」:使用范围由面板自动向宿主索取(电话入参 `includeEnv`,宿主只回**范围名与宿主种类**、不回任何路径),显示的就是更新要落到的那个 profile——`web` 与 `desktop` 各装一份,装错范围是严重故障,所以这一栏宁可显示“未知”也不猜。调用方知道得更准时可以用 `profileName` 显式覆盖。
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
可选档案卷主题(纸面浅色案卷风,不替换默认):挂载时加 `theme: 'archive'` 即换肤(右上大印章「待查/可装/安装中/待重启/受阻/已最新」+ 横幅小印章一字 + profile 牌 + 待重启衬线横幅配手绘 SVG 标 + 窄屏印章固定 + 省略号逐字折叠 + 浅深双主题跟随系统),内核 DOM 顺序不动、复制诊断常在;不传即最小可用默认深色样式。运行时用 `panel.setTheme('archive' | 'default')` 可切。旧值 `theme: 'd5-paper'` 仍可用(同一套渲染)。入口件与批量面板是同一个 `theme` 参数,取值同一套。
|
|
144
146
|
|
|
145
147
|
类型定义随包分发(`dsh-plugin-update/panel` 的 `.d.ts`),不用自编译;面板离线可读,与包版本绑定。
|
|
146
148
|
|
|
@@ -215,7 +217,7 @@ for (const [name, handler] of Object.entries(multi.handlers)) registry.set(name,
|
|
|
215
217
|
```
|
|
216
218
|
|
|
217
219
|
五个批量电话(`<prefix>` 即上面的 `life`):`batchStatus` / `batchCheck` / `batchInstall` / `batchResume` / `batchCancel`;
|
|
218
|
-
|
|
220
|
+
每个目标的四个单插件电话照旧以**各自前缀**暴露(`ilife-bill.updateStatus` 等)。
|
|
219
221
|
|
|
220
222
|
回包形状(成功恰好四项,失败只有三项):
|
|
221
223
|
|
|
@@ -232,7 +234,7 @@ import { mountUpdateBatchPanel } from 'dsh-plugin-update/panel-batch'
|
|
|
232
234
|
const panel = mountUpdateBatchPanel(el, {
|
|
233
235
|
prefix: 'life',
|
|
234
236
|
call: (name, args) => host.call(name, args),
|
|
235
|
-
theme: '
|
|
237
|
+
theme: 'archive', // 与单插件面板同一套皮肤(archive = 档案卷,旧值 d5-paper 仍可用)
|
|
236
238
|
})
|
|
237
239
|
```
|
|
238
240
|
|
|
@@ -252,7 +254,7 @@ mountUpdateBatchPanelHttp(document.querySelector('#batch'), {
|
|
|
252
254
|
三条硬约束(都在实现里):**一行只回答一个问题**(这家的下一步是什么,状态词全中文可执行);
|
|
253
255
|
**行内动作只作用于该行**,「全部更新」是宏而不是第二个状态机;**待重启与失败常驻横幅**,不藏进展开里。
|
|
254
256
|
|
|
255
|
-
|
|
257
|
+
点任意一行展开该家详情:**内容是单插件那套五章内核的只读渲染,动作由批量面板自己提供**。第 02 章日志由批量面板按行自动取该行自己的 `updateChangelog` 电话(按行+版本记住结果,取不到即中性提示,不挡安装;`autoChangelog: false` 可关)。
|
|
256
258
|
内核渲染时传 `actions: 'none'`——五章内容、进度条、「已跳过」提示一字不减,**动作按钮一个都不画**
|
|
257
259
|
(内核里那些按钮带的是 `data-action`,批量面板只认 `data-act`;照搬 markup 而不接管行为,
|
|
258
260
|
就会得到「可点却没反应」的死按钮——这条缝现在由渲染开关焊死)。详情里的动作行是批量面板自己的:
|
|
@@ -274,11 +276,33 @@ mountUpdateBatchPanelHttp(document.querySelector('#batch'), {
|
|
|
274
276
|
**跨使用范围如实拒绝**:`web` 与 `desktop` 各自排队(装的是不同落点),混在一起的目标会回
|
|
275
277
|
`cross-scope`,本包不会替你跨范围抢锁。
|
|
276
278
|
|
|
279
|
+
### 第 2.7 节:更新日志自动展示(告诉用户更新了什么)
|
|
280
|
+
|
|
281
|
+
任何插件的升级能力都是两件事:**① 给一套面板**(上面第 4 步 / 2.5 / 2.6,挂载一行即跑);**② 告诉用户这次更新了什么**(本节,写 md 即显示,零新增代码)。
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
import { mountUpdatePanel } from 'dsh-plugin-update/panel'
|
|
285
|
+
|
|
286
|
+
// 与第 4 步同一行,不用加任何参数:有新版时第 02 章“更新日志”自动出现
|
|
287
|
+
const panel = mountUpdatePanel(document.getElementById('update-slot'), {
|
|
288
|
+
pluginId: 'my-notes-plugin',
|
|
289
|
+
prefix: 'notes',
|
|
290
|
+
call: (name, args) => host.call(name, args),
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
作者侧只写文件,不写代码(发布后用户自动看到):
|
|
295
|
+
|
|
296
|
+
1. 包根放 `CHANGELOG.md`(文件名全大写,小写视为没有),版本节形如 `## [1.2.0] - 2026-10-05`(最新在前),分类用 `### Added/Fixed/Changed`(面板展开;`Deprecated/Removed/Security` 折叠;`Unreleased` 与空节忽略)。
|
|
297
|
+
2. `package.json` 的 `files` 白名单加上 `CHANGELOG.md`(否则发出去的包里没有它,面板永远读不到),发布前跑一次 `npm publish --dry-run` 核对。
|
|
298
|
+
|
|
299
|
+
行为:有新版时面板按新版号自动取一次该版 tarball 里的全文(复用官方源 + `integrity` 校验,同一版本只取一次);删了文件或取不到即中性提示,**安装永远不受影响**。想自己接管就传 `autoChangelog: false` 退回手动(备好文本后 `mountUpdatePanel({ changelogMarkdown })` 或 `setChangelogMarkdown` 传入,显式文本永不被覆盖)。入口件与批量面板同样自动:前者透传,后者展开行按行取。细节与边界见第 5.8 节。
|
|
300
|
+
|
|
277
301
|
### 升级本包(已经接入过的项目)
|
|
278
302
|
|
|
279
303
|
宿主种类与安装出口都由本包自己探测和选择,**升级依赖即可,宿主侧与面板侧都不用改代码**:
|
|
280
304
|
|
|
281
|
-
1. 依赖版本提到 `^0.
|
|
305
|
+
1. 依赖版本提到 `^0.5.0`。
|
|
282
306
|
2. 重新装 / 发一版你自己的插件,让新依赖进当前使用范围(运行时用的是 `node_modules` 里那份)。
|
|
283
307
|
3. 如果你自己接过一版宿主安装出口,把它删掉——它会挡在本包的路由前面。
|
|
284
308
|
|
|
@@ -426,7 +450,8 @@ dsh plugin --profile my-web add --save-exact my-notes-plugin@1.2.3 --registry=ht
|
|
|
426
450
|
|
|
427
451
|
- 有新版时面板在横幅下方展示“更新说明(当前版 → 新版):”,按目标包内 `CHANGELOG.md`(Keep-a-Changelog 子集)渲染:`Added/Fixed/Changed` 展开,`Deprecated/Removed/Security` 折叠,`Unreleased` 与空节不展示。
|
|
428
452
|
- 作者未提供说明时显示“作者未提供更新说明,安装不受影响。”——缺日志永不挡安装,不改变 `canInstall` 与 `blockedReason`。
|
|
429
|
-
-
|
|
453
|
+
- 默认自动:面板看到有新版即按新版号调一次 `….updateChangelog`,回来自己填进第 02 章;同一版本只取一次,取不到即中性提示。作者侧只要写好包根 `CHANGELOG.md` 并随包发布,零新增代码。
|
|
454
|
+
- 手动模式(老用法照旧):传 `autoChangelog: false` 即退回手动——已装版离线读本机 `node_modules/<目标包>/CHANGELOG.md`,新版按需取新版 tarball 内同名文件(复用官方源与 `integrity` 校验,取不到即回落中性提示;参考包根导出的 `readInstalledChangelogText` / `fetchReleaseChangelogText`),经 `mountUpdatePanel({ changelogMarkdown })` 或 `setChangelogMarkdown` 交给面板;显式传过的文本自动链路永不覆盖。
|
|
430
455
|
|
|
431
456
|
## 6. 排错
|
|
432
457
|
|
|
@@ -450,7 +475,7 @@ dsh plugin --profile my-web add --save-exact my-notes-plugin@1.2.3 --registry=ht
|
|
|
450
475
|
|
|
451
476
|
照第 2 节把名字换成自己的之后,逐条打勾:
|
|
452
477
|
|
|
453
|
-
1. 宿主启动不报错,`update.phoneNames`
|
|
478
|
+
1. 宿主启动不报错,`update.phoneNames` 读到自家前缀的四个电话名,查状态返回六字段快照。
|
|
454
479
|
2. 面板按间隔轮询到快照,能展示第 5.2 节的原因文案;有新版时安装按钮可用,无新版与待重启时按钮状态正确。
|
|
455
480
|
3. 模拟一次 `pending-restart` 能看到第 5.4 节的横幅,模拟一次自动装失败能看到第 5.3 节的命令可复制执行。
|
|
456
481
|
4. 第二家同机隔离与串行:两家各传自己的插件标识与电话名前缀,电话名、落盘目录、锁文件逐个不同;安装执行跨插件串行——同范围撞上时后到者报 `update-busy`,凭队列位置(第 5.7 节)重试。
|
|
@@ -466,6 +491,25 @@ import { mountUpdatePanel } from 'dsh-plugin-update/panel'
|
|
|
466
491
|
mountUpdatePanel(slot, { pluginId: 'notes', prefix: 'notes', call: host.call })
|
|
467
492
|
```
|
|
468
493
|
|
|
494
|
+
**更新日志自动**(#38:写 md 即显示,零新增代码):
|
|
495
|
+
|
|
496
|
+
```js
|
|
497
|
+
// 宿主侧:createHostUpdate 顺手注册第 4 个电话,无新增配置
|
|
498
|
+
// 入参 { version }(发行版号,先验 validReleaseVersion + 通道门禁)
|
|
499
|
+
// 成功 { ok: true, version, markdown: string | null }(取不到即 null,中性提示)
|
|
500
|
+
// 失败复用 error / errorKind 体系;回包无路径、无快照;按版本记住结果
|
|
501
|
+
import { buildChangelogPhoneName } from 'dsh-plugin-update'
|
|
502
|
+
|
|
503
|
+
buildChangelogPhoneName('notes') // 'notes.updateChangelog',与宿主侧同一套拼法
|
|
504
|
+
|
|
505
|
+
// 面板侧:默认自动,有新版调一次;显式文本仍赢,false 退回手动
|
|
506
|
+
mountUpdatePanel(slot, { pluginId: 'my-notes-plugin', prefix: 'notes', call: host.call })
|
|
507
|
+
// mountUpdatePanel(slot, { pluginId, prefix, call, autoChangelog: false })
|
|
508
|
+
// mountUpdatePanel(slot, { pluginId, prefix, call, changelogMarkdown }) // 手动模式
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
- 入口件(`dsh-plugin-update/entry`)把 `autoChangelog / changelogMarkdown` 透传给面板 dialog/inline;批量面板(`dsh-plugin-update/panel-batch`)展开行自动调该行自己的 `updateChangelog`(无新增批量电话,`autoChangelog: false` 可关整批);HTTP 版(`dsh-plugin-update/http`)白名单已放行,默认路径即达。
|
|
512
|
+
|
|
469
513
|
**客户端入口**(`dist/client.js`,构建期打包用):
|
|
470
514
|
|
|
471
515
|
```js
|
|
@@ -506,6 +550,6 @@ queuePositionOf(queueState, 'my-plugin', requestId) // 0 = 在装,1..n = 顺
|
|
|
506
550
|
|
|
507
551
|
## 9. 兼容与稳定性
|
|
508
552
|
|
|
509
|
-
|
|
553
|
+
这些形状稳定,可以放心依赖:四个电话名与入参回参(老三电话一字不动,`updateChangelog` 是新增)、快照六字段、任务公开形状、配置只经函数入参注入、安装配方五键、日志事件字段基线(三个事件各带必填 `pluginId`)、历史落盘路径。
|
|
510
554
|
|
|
511
555
|
向后兼容的扩展:新增可选配置键、新增可选 `readerOverrides`(如 `targetPackageDir`)、新增宿主种类与路由取值。调用方不认新取值时按普通宿主处理即可,不会因此报错。
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type PhoneAction } from './config.js';
|
|
2
|
-
export { buildPhoneName, buildPhoneNames } from './config.js';
|
|
3
|
-
export type { PhoneAction } from './config.js';
|
|
1
|
+
import { type ChangelogPhoneAction, type PhoneAction } from './config.js';
|
|
2
|
+
export { buildChangelogPhoneName, buildPhoneName, buildPhoneNames } from './config.js';
|
|
3
|
+
export type { ChangelogPhoneAction, PhoneAction } from './config.js';
|
|
4
4
|
export { manualCommand } from './commands.js';
|
|
5
5
|
export { QUEUE_INTENT_TTL_MS, cancelEnqueuedInQueue, emptyQueueState, enqueueInQueue, isHeadOfQueue, isQueueBusy, normalizeQueueState, pruneExpiredIntents, queuePositionOf, releaseOwnerInQueue, setOwnerIfFree, visibleQueueFor, } from './queue.js';
|
|
6
6
|
export type { QueuedEntry, QueueOwner, UpdateQueueState, VisibleQueue, VisibleQueueOwner } from './queue.js';
|
|
@@ -12,5 +12,5 @@ export declare const CLIENT_POLL: {
|
|
|
12
12
|
readonly defaultMs: 1000;
|
|
13
13
|
readonly minMs: 250;
|
|
14
14
|
};
|
|
15
|
-
export declare function buildClientPhoneNames(prefix: string): Record<PhoneAction, string>;
|
|
15
|
+
export declare function buildClientPhoneNames(prefix: string): Record<PhoneAction, string> & Record<ChangelogPhoneAction, string>;
|
|
16
16
|
export declare function assertPollInterval(ms: unknown): number;
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/client.ts
|
|
2
2
|
import { buildPhoneNames, DEFAULT_PANEL_POLL_MS, MIN_PANEL_POLL_MS } from "./config.js";
|
|
3
|
-
import { buildPhoneName, buildPhoneNames as buildPhoneNames2 } from "./config.js";
|
|
3
|
+
import { buildChangelogPhoneName, buildPhoneName, buildPhoneNames as buildPhoneNames2 } from "./config.js";
|
|
4
4
|
import { manualCommand as manualCommand2 } from "./commands.js";
|
|
5
5
|
import {
|
|
6
6
|
QUEUE_INTENT_TTL_MS,
|
|
@@ -66,6 +66,7 @@ export {
|
|
|
66
66
|
batchEntryOf,
|
|
67
67
|
batchProgress,
|
|
68
68
|
batchRequestId,
|
|
69
|
+
buildChangelogPhoneName,
|
|
69
70
|
buildClientPhoneNames,
|
|
70
71
|
buildPhoneName,
|
|
71
72
|
buildPhoneNames2 as buildPhoneNames,
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const DEFAULT_PREFIX = "wf";
|
|
2
2
|
export declare const PHONE_ACTIONS: readonly ["updateStatus", "updateCheck", "updateInstall"];
|
|
3
3
|
export type PhoneAction = (typeof PHONE_ACTIONS)[number];
|
|
4
|
+
export declare const CHANGELOG_PHONE_ACTION: "updateChangelog";
|
|
5
|
+
export type ChangelogPhoneAction = typeof CHANGELOG_PHONE_ACTION;
|
|
4
6
|
export declare const DEFAULT_TARGET_PACKAGE = "dsh-mattpocock-skills-deck";
|
|
5
7
|
export declare const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
6
8
|
export declare const LEGACY_PLUGIN_ID = "dsh-mattpocock-skills-deck";
|
|
@@ -44,5 +46,6 @@ export interface ResolvedUpdateConfig {
|
|
|
44
46
|
export declare function assertPluginId(value: unknown): string;
|
|
45
47
|
export declare function assertPrefix(value: unknown, role: string): string;
|
|
46
48
|
export declare function resolveUpdateConfig(input: UpdateConfigInput): ResolvedUpdateConfig;
|
|
47
|
-
export declare function buildPhoneNames(prefix: string): Record<PhoneAction, string>;
|
|
49
|
+
export declare function buildPhoneNames(prefix: string): Record<PhoneAction, string> & Record<ChangelogPhoneAction, string>;
|
|
50
|
+
export declare function buildChangelogPhoneName(prefix: string): string;
|
|
48
51
|
export declare function buildPhoneName(prefix: string, action: PhoneAction): string;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/config.ts
|
|
2
2
|
const DEFAULT_PREFIX = "wf";
|
|
3
3
|
const PHONE_ACTIONS = ["updateStatus", "updateCheck", "updateInstall"];
|
|
4
|
+
const CHANGELOG_PHONE_ACTION = "updateChangelog";
|
|
4
5
|
const DEFAULT_TARGET_PACKAGE = "dsh-mattpocock-skills-deck";
|
|
5
6
|
const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
6
7
|
const LEGACY_PLUGIN_ID = "dsh-mattpocock-skills-deck";
|
|
@@ -73,14 +74,19 @@ function buildPhoneNames(prefix) {
|
|
|
73
74
|
return {
|
|
74
75
|
updateStatus: checked + ".updateStatus",
|
|
75
76
|
updateCheck: checked + ".updateCheck",
|
|
76
|
-
updateInstall: checked + ".updateInstall"
|
|
77
|
+
updateInstall: checked + ".updateInstall",
|
|
78
|
+
updateChangelog: checked + ".updateChangelog"
|
|
77
79
|
};
|
|
78
80
|
}
|
|
81
|
+
function buildChangelogPhoneName(prefix) {
|
|
82
|
+
return buildPhoneNames(prefix).updateChangelog;
|
|
83
|
+
}
|
|
79
84
|
function buildPhoneName(prefix, action) {
|
|
80
85
|
return buildPhoneNames(prefix)[action];
|
|
81
86
|
}
|
|
82
87
|
export {
|
|
83
88
|
BACKUP_FILE,
|
|
89
|
+
CHANGELOG_PHONE_ACTION,
|
|
84
90
|
DEFAULT_CHECK_TIMEOUT_MS,
|
|
85
91
|
DEFAULT_CONFIRMATION_TTL_MS,
|
|
86
92
|
DEFAULT_INSTALL_TIMEOUT_MS,
|
|
@@ -99,6 +105,7 @@ export {
|
|
|
99
105
|
STATE_FILE,
|
|
100
106
|
assertPluginId,
|
|
101
107
|
assertPrefix,
|
|
108
|
+
buildChangelogPhoneName,
|
|
102
109
|
buildPhoneName,
|
|
103
110
|
buildPhoneNames,
|
|
104
111
|
resolveUpdateConfig
|
package/dist/entry.d.ts
CHANGED
|
@@ -22,7 +22,8 @@ export interface UpdateEntryOptions {
|
|
|
22
22
|
/** 调宿主电话:(phoneName, args) => Promise<reply>。 */
|
|
23
23
|
call: (name: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
24
24
|
variant?: EntryVariant;
|
|
25
|
-
|
|
25
|
+
/** 主题:`default` 最小可用深色,`archive` 档案卷纸面浅色(`d5-paper` 为旧别名仍可用)。 */
|
|
26
|
+
theme?: UpdatePanelTheme;
|
|
26
27
|
/** 缺省 'mount':进页面静默查一次(只读)。'never' 则只在用户点击时查。 */
|
|
27
28
|
autoCheck?: EntryAutoCheck;
|
|
28
29
|
/** 缺省 'has-update':有新版才开面板,没有就只在原地给一句提示。 */
|
|
@@ -31,6 +32,12 @@ export interface UpdateEntryOptions {
|
|
|
31
32
|
label?: string;
|
|
32
33
|
profileName?: string;
|
|
33
34
|
pollMs?: number;
|
|
35
|
+
/**
|
|
36
|
+
* 更新日志自动/手动(#38):透传给面板(dialog 与 inline 同走 mountPanel),默认自动;
|
|
37
|
+
* 显式文本仍赢,false 退回手动。入口件自己不取数。
|
|
38
|
+
*/
|
|
39
|
+
autoChangelog?: boolean;
|
|
40
|
+
changelogMarkdown?: string | null;
|
|
34
41
|
/** variant='badge' 或 openOn='manual' 时,点击交给接入方(自己跳自己的页面)。 */
|
|
35
42
|
onActivate?: (state: {
|
|
36
43
|
hasUpdate: boolean;
|
|
@@ -46,7 +53,7 @@ export interface UpdateEntryController {
|
|
|
46
53
|
close(): void;
|
|
47
54
|
/** 当前按钮上的状态文案(接入方做自定义排版时读它)。 */
|
|
48
55
|
label(): string;
|
|
49
|
-
setTheme(theme:
|
|
56
|
+
setTheme(theme: UpdatePanelTheme): void;
|
|
50
57
|
unmount(): void;
|
|
51
58
|
}
|
|
52
59
|
/**
|
package/dist/entry.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { DEFAULT_PREFIX, MIN_PANEL_POLL_MS, buildPhoneNames } from "./config.js";
|
|
3
3
|
import {
|
|
4
4
|
failureCodeOf,
|
|
5
|
-
mountUpdatePanel
|
|
5
|
+
mountUpdatePanel,
|
|
6
|
+
normalizePanelTheme
|
|
6
7
|
} from "./panel.js";
|
|
7
8
|
const LABEL_IDLE = "\u68C0\u67E5\u66F4\u65B0";
|
|
8
9
|
const LABEL_FAILED = "\u66F4\u65B0\u5931\u8D25\uFF0C\u70B9\u6B64\u67E5\u770B";
|
|
@@ -48,6 +49,13 @@ const UPDATE_ENTRY_CSS = [
|
|
|
48
49
|
".dsh-upd-entry-btn{font:inherit;border:1px solid var(--dsh-upd-line,#d1d5db);border-radius:6px;",
|
|
49
50
|
"background:var(--dsh-upd-btn,#f9fafb);color:inherit;padding:4px 12px;cursor:pointer}",
|
|
50
51
|
".dsh-upd-entry-btn:hover{border-color:var(--dsh-upd-primary,#2563eb)}",
|
|
52
|
+
".dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .06s ease}",
|
|
53
|
+
".dsh-upd-entry-btn:active:not(:disabled){transform:translateY(1px)}",
|
|
54
|
+
".dsh-upd-entry-btn:disabled,.dsh-upd-entry-dot:disabled{opacity:.55;cursor:wait}",
|
|
55
|
+
'.dsh-upd-entry-btn[aria-busy="true"]::after{content:"";display:inline-block;width:10px;height:10px;margin-left:7px;vertical-align:-1px;',
|
|
56
|
+
"border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:dsh-upd-entry-spin .8s linear infinite}",
|
|
57
|
+
"@keyframes dsh-upd-entry-spin{to{transform:rotate(360deg)}}",
|
|
58
|
+
'@media (prefers-reduced-motion: reduce){.dsh-upd-entry-btn,.dsh-upd-entry-dot{transition:none}.dsh-upd-entry-btn[aria-busy="true"]::after{animation:none}}',
|
|
51
59
|
".dsh-upd-entry-btn:focus-visible,.dsh-upd-entry-dot:focus-visible{outline:2px solid var(--dsh-upd-focus,#2563eb);outline-offset:1px}",
|
|
52
60
|
'.dsh-upd-entry[data-state="update"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-ok-line,#059669);color:var(--dsh-upd-ok-line,#059669)}',
|
|
53
61
|
'.dsh-upd-entry[data-state="restart"] .dsh-upd-entry-btn{border-color:var(--dsh-upd-warn-line,#d97706);color:var(--dsh-upd-warn-line,#d97706)}',
|
|
@@ -106,10 +114,10 @@ function mountUpdateEntry(container, options) {
|
|
|
106
114
|
if (openOn !== "has-update" && openOn !== "always" && openOn !== "manual") {
|
|
107
115
|
throw new Error(`[dsh-plugin-update] \u70B9\u51FB\u53BB\u5411\u975E\u6CD5\uFF1A\u53EA\u6536 has-update / always / manual\uFF08\u6536\u5230 ${JSON.stringify(options.openOn)}\uFF09`);
|
|
108
116
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 d5-paper\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
|
|
117
|
+
if (options.theme !== void 0 && options.theme !== "default" && options.theme !== "archive" && options.theme !== "d5-paper") {
|
|
118
|
+
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 ${JSON.stringify(options.theme)}\uFF09`);
|
|
112
119
|
}
|
|
120
|
+
let theme = normalizePanelTheme(options.theme ?? "default");
|
|
113
121
|
const pollMs = options.pollMs;
|
|
114
122
|
if (pollMs !== void 0 && (typeof pollMs !== "number" || !Number.isFinite(pollMs) || pollMs < MIN_PANEL_POLL_MS)) {
|
|
115
123
|
throw new Error(`[dsh-plugin-update] \u9762\u677F\u8F6E\u8BE2\u95F4\u9694\u975E\u6CD5\uFF1A\u4E0D\u5F97\u5C0F\u4E8E 250 \u6BEB\u79D2\uFF08\u6536\u5230 ${JSON.stringify(options.pollMs)}\uFF09`);
|
|
@@ -121,6 +129,7 @@ function mountUpdateEntry(container, options) {
|
|
|
121
129
|
let snapshot = null;
|
|
122
130
|
let error = null;
|
|
123
131
|
let note = null;
|
|
132
|
+
let activating = false;
|
|
124
133
|
let mounted = true;
|
|
125
134
|
let panel = null;
|
|
126
135
|
let panelMode = null;
|
|
@@ -156,9 +165,10 @@ function mountUpdateEntry(container, options) {
|
|
|
156
165
|
}
|
|
157
166
|
function entryHTML() {
|
|
158
167
|
const kind = entryStateKind(stateOf());
|
|
159
|
-
const text = currentLabel();
|
|
168
|
+
const text = activating ? "\u6B63\u5728\u67E5\u65B0\u7248\u2026" : currentLabel();
|
|
169
|
+
const busyAttr = activating ? ' disabled aria-busy="true"' : "";
|
|
160
170
|
const themeAttr = theme === "d5-paper" ? ' data-theme="d5-paper"' : "";
|
|
161
|
-
const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(text)}" aria-label="${escapeHtml(text)}"></button>` : `<button type="button" class="dsh-upd-entry-btn" ${ENTRY_ATTR}="activate">${escapeHtml(text)}</button>`;
|
|
171
|
+
const control = variant === "badge" ? `<button type="button" class="dsh-upd-entry-dot" ${ENTRY_ATTR}="activate" title="${escapeHtml(text)}" aria-label="${escapeHtml(text)}"${busyAttr}></button>` : `<button type="button" class="dsh-upd-entry-btn" ${ENTRY_ATTR}="activate"${busyAttr}>${escapeHtml(text)}</button>`;
|
|
162
172
|
const noteHTML = note ? `<span class="dsh-upd-entry-note" data-dsh-upd-note="1">${escapeHtml(note)}</span>` : "";
|
|
163
173
|
return `<style>${UPDATE_ENTRY_CSS}</style>
|
|
164
174
|
<span class="dsh-upd-entry" data-variant="${variant}" data-state="${kind}"${themeAttr}>${control}${noteHTML}</span>`;
|
|
@@ -208,6 +218,10 @@ function mountUpdateEntry(container, options) {
|
|
|
208
218
|
theme,
|
|
209
219
|
pollMs,
|
|
210
220
|
profileName,
|
|
221
|
+
autoChangelog: options.autoChangelog,
|
|
222
|
+
changelogMarkdown: options.changelogMarkdown ?? null,
|
|
223
|
+
// 面板点「关闭」即走入口件的完整关闭(收 dialog + 还原按钮 + 重查一次),不再是面板自己停轮询。
|
|
224
|
+
onCloseRequested: () => close(),
|
|
211
225
|
call: panelCall
|
|
212
226
|
});
|
|
213
227
|
panelMode = mode;
|
|
@@ -233,10 +247,15 @@ function mountUpdateEntry(container, options) {
|
|
|
233
247
|
void refresh();
|
|
234
248
|
}
|
|
235
249
|
async function activate() {
|
|
236
|
-
if (!mounted || panelMode !== null) return;
|
|
250
|
+
if (!mounted || panelMode !== null || activating) return;
|
|
251
|
+
activating = true;
|
|
237
252
|
note = null;
|
|
238
253
|
render();
|
|
239
|
-
|
|
254
|
+
try {
|
|
255
|
+
await checkNow();
|
|
256
|
+
} finally {
|
|
257
|
+
activating = false;
|
|
258
|
+
}
|
|
240
259
|
if (!mounted) return;
|
|
241
260
|
if (variant === "badge" || openOn === "manual") {
|
|
242
261
|
if (onActivate) onActivate({ hasUpdate: hasUpdate(), latestVersion: snapshot?.latestVersion ?? null });
|
|
@@ -252,11 +271,11 @@ function mountUpdateEntry(container, options) {
|
|
|
252
271
|
render();
|
|
253
272
|
}
|
|
254
273
|
function setTheme(next) {
|
|
255
|
-
if (next !== "default" && next !== "d5-paper") {
|
|
256
|
-
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216
|
|
274
|
+
if (next !== "default" && next !== "archive" && next !== "d5-paper") {
|
|
275
|
+
throw new Error(`[dsh-plugin-update] \u4E3B\u9898\u975E\u6CD5\uFF1A\u53EA\u6536 default \u6216 archive\uFF08d5-paper \u4E3A\u65E7\u522B\u540D\u4ECD\u53EF\u7528\uFF09\uFF08\u6536\u5230 ${JSON.stringify(next)}\uFF09`);
|
|
257
276
|
}
|
|
258
|
-
theme = next;
|
|
259
|
-
if (panel) void panel.setTheme(
|
|
277
|
+
theme = normalizePanelTheme(next);
|
|
278
|
+
if (panel) void panel.setTheme(theme);
|
|
260
279
|
else render();
|
|
261
280
|
}
|
|
262
281
|
function onClick(ev) {
|
|
@@ -273,6 +292,14 @@ function mountUpdateEntry(container, options) {
|
|
|
273
292
|
} catch {
|
|
274
293
|
}
|
|
275
294
|
}
|
|
295
|
+
function onKeyDown(ev) {
|
|
296
|
+
try {
|
|
297
|
+
const e = ev;
|
|
298
|
+
if (!mounted || panelMode !== "dialog" || !e || e.key !== "Escape") return;
|
|
299
|
+
close();
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
}
|
|
276
303
|
function unmount() {
|
|
277
304
|
if (!mounted) return;
|
|
278
305
|
mounted = false;
|
|
@@ -287,11 +314,19 @@ function mountUpdateEntry(container, options) {
|
|
|
287
314
|
container.removeEventListener?.("click", onClick);
|
|
288
315
|
} catch {
|
|
289
316
|
}
|
|
317
|
+
try {
|
|
318
|
+
container.removeEventListener?.("keydown", onKeyDown);
|
|
319
|
+
} catch {
|
|
320
|
+
}
|
|
290
321
|
}
|
|
291
322
|
try {
|
|
292
323
|
container.addEventListener?.("click", onClick);
|
|
293
324
|
} catch {
|
|
294
325
|
}
|
|
326
|
+
try {
|
|
327
|
+
container.addEventListener?.("keydown", onKeyDown);
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
295
330
|
if (variant === "inline") mountPanel("embedded");
|
|
296
331
|
else render();
|
|
297
332
|
if (autoCheck === "mount") void refresh();
|
package/dist/host.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type PhoneAction, type UpdateConfigInput } from './config.js';
|
|
1
|
+
import { type ChangelogPhoneAction, type PhoneAction, type UpdateConfigInput } from './config.js';
|
|
2
2
|
import type { EnvironmentKind, ReleaseChannel } from './ports.js';
|
|
3
3
|
import { type UpdateQueueState } from './queue.js';
|
|
4
|
-
export { buildPhoneName, buildPhoneNames, resolveUpdateConfig } from './config.js';
|
|
5
|
-
export type { PhoneAction, UpdateConfigInput } from './config.js';
|
|
4
|
+
export { buildChangelogPhoneName, buildPhoneName, buildPhoneNames, resolveUpdateConfig } from './config.js';
|
|
5
|
+
export type { ChangelogPhoneAction, PhoneAction, UpdateConfigInput } from './config.js';
|
|
6
6
|
export { parseEventListManifest, checkEventFields, checkEventCounts } from './gate.js';
|
|
7
7
|
export type { GateEventLevel, GateEventKind, GateEventEntry, GateEventCounts, GateEventList, GateFieldCheck, GateCountCheck } from './gate.js';
|
|
8
8
|
export { containingPackage, defaultHomeDir, profileNameValid, registrySpec, resolveTargetPackage } from './reader.js';
|
|
@@ -111,7 +111,7 @@ type LogCtx = {
|
|
|
111
111
|
fire: (level: string, event: string, fields: Record<string, unknown>) => void;
|
|
112
112
|
} | null;
|
|
113
113
|
export interface HostUpdate {
|
|
114
|
-
phoneNames: Record<PhoneAction, string>;
|
|
114
|
+
phoneNames: Record<PhoneAction, string> & Record<ChangelogPhoneAction, string>;
|
|
115
115
|
handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>>;
|
|
116
116
|
}
|
|
117
117
|
/**
|
package/dist/host.js
CHANGED
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
resolveUpdateConfig
|
|
11
11
|
} from "./config.js";
|
|
12
12
|
import { manualCommand } from "./commands.js";
|
|
13
|
+
import { fetchReleaseChangelogText } from "./changelog-io.js";
|
|
13
14
|
import { buildDiag } from "./diag.js";
|
|
14
|
-
import { isVersionAllowedInChannel, validRequestId } from "./service.js";
|
|
15
|
+
import { fetchNpmVersionRelease, isVersionAllowedInChannel, updateError, validReleaseVersion, validRequestId } from "./service.js";
|
|
15
16
|
import { createUpdateDiskPorts, createUpdateExecutor, createUpdateQueuePorts } from "./store.js";
|
|
16
17
|
import { createUpdateReader, defaultHomeDir, resolveProfileName, resolveTargetPackage } from "./reader.js";
|
|
17
18
|
import {
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
setOwnerIfFree,
|
|
28
29
|
visibleQueueFor
|
|
29
30
|
} from "./queue.js";
|
|
30
|
-
import { buildPhoneName, buildPhoneNames as buildPhoneNames2, resolveUpdateConfig as resolveUpdateConfig2 } from "./config.js";
|
|
31
|
+
import { buildChangelogPhoneName, buildPhoneName, buildPhoneNames as buildPhoneNames2, resolveUpdateConfig as resolveUpdateConfig2 } from "./config.js";
|
|
31
32
|
import { parseEventListManifest, checkEventFields, checkEventCounts } from "./gate.js";
|
|
32
33
|
import { containingPackage, defaultHomeDir as defaultHomeDir2, profileNameValid, registrySpec, resolveTargetPackage as resolveTargetPackage2 } from "./reader.js";
|
|
33
34
|
import {
|
|
@@ -87,7 +88,7 @@ import {
|
|
|
87
88
|
CHANGELOG_TAR_MAX_BYTES,
|
|
88
89
|
CHANGELOG_TARBALL_MAX_BYTES,
|
|
89
90
|
extractChangelogFromTar,
|
|
90
|
-
fetchReleaseChangelogText,
|
|
91
|
+
fetchReleaseChangelogText as fetchReleaseChangelogText2,
|
|
91
92
|
readInstalledChangelogText
|
|
92
93
|
} from "./changelog-io.js";
|
|
93
94
|
import { createUpdateDiskPorts as createUpdateDiskPorts2, createUpdateExecutor as createUpdateExecutor2, pathsForUpdate, resolveCliEntry } from "./store.js";
|
|
@@ -105,7 +106,7 @@ import {
|
|
|
105
106
|
compareReleaseVersions,
|
|
106
107
|
isPrereleaseVersion,
|
|
107
108
|
isVersionAllowedInChannel as isVersionAllowedInChannel2,
|
|
108
|
-
validReleaseVersion,
|
|
109
|
+
validReleaseVersion as validReleaseVersion2,
|
|
109
110
|
validVersion,
|
|
110
111
|
compareVersions
|
|
111
112
|
} from "./service.js";
|
|
@@ -524,6 +525,49 @@ function loggedPhone(method, kind, pluginId, fn, diagCtx) {
|
|
|
524
525
|
}
|
|
525
526
|
};
|
|
526
527
|
}
|
|
528
|
+
function loggedChangelogPhone(method, pluginId, fn, diagCtx) {
|
|
529
|
+
return async function(args) {
|
|
530
|
+
const t0 = Date.now();
|
|
531
|
+
const emit = (level, event, fields) => {
|
|
532
|
+
try {
|
|
533
|
+
if (phoneLogCtx && typeof phoneLogCtx.fire === "function") phoneLogCtx.fire(level, event, fields);
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
const safeArgs = args && typeof args === "object" ? args : {};
|
|
538
|
+
try {
|
|
539
|
+
const out = await fn(safeArgs);
|
|
540
|
+
emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind: "update-changelog", pluginId });
|
|
541
|
+
return { ok: true, version: out.version, markdown: out.markdown };
|
|
542
|
+
} catch (error) {
|
|
543
|
+
const payload = toUpdateErrorPayload(error);
|
|
544
|
+
emit("warn", "host.call.fail", { method, kind: "update-changelog", errorHash: hash8(String(error?.message || payload.error)), pluginId });
|
|
545
|
+
let diag;
|
|
546
|
+
try {
|
|
547
|
+
const cfg = diagCtx?.config;
|
|
548
|
+
const overrides = diagCtx?.readerOverrides ?? {};
|
|
549
|
+
const envRaw = overrides?.environmentKind;
|
|
550
|
+
const runningRaw = overrides?.runningVersion;
|
|
551
|
+
const built = buildDiag({
|
|
552
|
+
errorCode: payload.error,
|
|
553
|
+
phoneKind: "update-changelog",
|
|
554
|
+
error,
|
|
555
|
+
args: safeArgs,
|
|
556
|
+
targetPackageName: cfg?.targetPackageName,
|
|
557
|
+
registryUrl: cfg?.registryUrl,
|
|
558
|
+
runningVersion: typeof runningRaw === "string" ? runningRaw : void 0,
|
|
559
|
+
latestVersion: void 0,
|
|
560
|
+
environmentKind: typeof envRaw === "string" ? envRaw : void 0,
|
|
561
|
+
latencyMs: Date.now() - t0
|
|
562
|
+
});
|
|
563
|
+
if (built && typeof built === "object") diag = built;
|
|
564
|
+
} catch {
|
|
565
|
+
diag = void 0;
|
|
566
|
+
}
|
|
567
|
+
return { ok: false, ...payload, ...diag ? { diag } : {} };
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
527
571
|
function createHostUpdate(deps = {}, configInput) {
|
|
528
572
|
const config = resolveUpdateConfig(configInput);
|
|
529
573
|
const pluginId = config.pluginId;
|
|
@@ -533,6 +577,28 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
533
577
|
else watchDesktopPnpm(ctx);
|
|
534
578
|
const readerOverrides = { ...deps.readerOverrides ?? {}, ctx, ...deps.pluginManager ? { pluginManager: deps.pluginManager } : {} };
|
|
535
579
|
const phoneNames = buildPhoneNames(config.prefix);
|
|
580
|
+
const changelogCache = /* @__PURE__ */ new Map();
|
|
581
|
+
async function readChangelog(args) {
|
|
582
|
+
const raw = args ? args["version"] : void 0;
|
|
583
|
+
if (!validReleaseVersion(raw)) throw updateError("invalid-release");
|
|
584
|
+
const version = raw;
|
|
585
|
+
if (!isVersionAllowedInChannel(version, config.releaseChannel)) throw updateError("invalid-release");
|
|
586
|
+
if (changelogCache.has(version)) return { version, markdown: changelogCache.get(version) ?? null };
|
|
587
|
+
const fetchImpl = readerOverrides.fetchImpl ?? globalThis.fetch;
|
|
588
|
+
const release = await fetchNpmVersionRelease(fetchImpl, config.checkTimeoutMs, {
|
|
589
|
+
targetPackageName: config.targetPackageName,
|
|
590
|
+
registryUrl: config.registryUrl,
|
|
591
|
+
releaseChannel: config.releaseChannel,
|
|
592
|
+
version
|
|
593
|
+
});
|
|
594
|
+
const markdown = await fetchReleaseChangelogText(
|
|
595
|
+
fetchImpl,
|
|
596
|
+
{ tarball: release.tarball, integrity: release.integrity, version },
|
|
597
|
+
{ targetPackageName: config.targetPackageName, registryUrl: config.registryUrl, timeoutMs: config.checkTimeoutMs }
|
|
598
|
+
);
|
|
599
|
+
changelogCache.set(version, markdown);
|
|
600
|
+
return { version, markdown };
|
|
601
|
+
}
|
|
536
602
|
async function readStatus(args) {
|
|
537
603
|
const reader = await getSharedReader(pluginId, config, {
|
|
538
604
|
...readerOverrides,
|
|
@@ -630,6 +696,10 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
630
696
|
[phoneNames.updateInstall]: loggedPhone(phoneNames.updateInstall, "update-install", pluginId, runInstall, {
|
|
631
697
|
config,
|
|
632
698
|
readerOverrides
|
|
699
|
+
}),
|
|
700
|
+
[phoneNames.updateChangelog]: loggedChangelogPhone(phoneNames.updateChangelog, pluginId, readChangelog, {
|
|
701
|
+
config,
|
|
702
|
+
readerOverrides
|
|
633
703
|
})
|
|
634
704
|
};
|
|
635
705
|
return { phoneNames, handlers };
|
|
@@ -682,6 +752,7 @@ export {
|
|
|
682
752
|
SKIPPED_FILE,
|
|
683
753
|
__resetSharedUpdateReaderForTests,
|
|
684
754
|
addSkipped,
|
|
755
|
+
buildChangelogPhoneName,
|
|
685
756
|
buildDiag2 as buildDiag,
|
|
686
757
|
buildPhoneName,
|
|
687
758
|
buildPhoneNames2 as buildPhoneNames,
|
|
@@ -709,7 +780,7 @@ export {
|
|
|
709
780
|
enforceBudget as enforceDiagBudgetForPhone,
|
|
710
781
|
enqueueInQueue2 as enqueueInQueue,
|
|
711
782
|
extractChangelogFromTar,
|
|
712
|
-
fetchReleaseChangelogText,
|
|
783
|
+
fetchReleaseChangelogText2 as fetchReleaseChangelogText,
|
|
713
784
|
hasVisibleSections,
|
|
714
785
|
isHeadOfQueue2 as isHeadOfQueue,
|
|
715
786
|
isPrereleaseVersion,
|
|
@@ -741,7 +812,7 @@ export {
|
|
|
741
812
|
setOwnerIfFree2 as setOwnerIfFree,
|
|
742
813
|
skipKey,
|
|
743
814
|
truncateToWordBoundary,
|
|
744
|
-
validReleaseVersion,
|
|
815
|
+
validReleaseVersion2 as validReleaseVersion,
|
|
745
816
|
validVersion,
|
|
746
817
|
visibleQueueFor2 as visibleQueueFor
|
|
747
818
|
};
|