dsh-deferred-boot 0.0.0-stage → 0.3.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/CHANGELOG.md +80 -0
- package/LICENSE +21 -0
- package/README.md +95 -2
- package/cordis.patch.yml +22 -0
- package/docs/PUBLISHING.md +58 -0
- package/docs/design.md +78 -0
- package/icon.svg +7 -0
- package/lib/client.js +593 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/market/index.json +50 -0
- package/package.json +69 -4
- package/scripts/gen-market-index.mjs +55 -0
- package/src/index.js +60 -0
- package/src/scheduler.js +419 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.3.1] - 2026-11-07
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Plugin list inside the configuration form: installed plugins are grouped into
|
|
11
|
+
third-party and official entries (official collapsed by default), each with a
|
|
12
|
+
lightweight / medium / heavy startup-cost grade and an estimated mount cost.
|
|
13
|
+
- Checkbox selection writes `deferredPlugins` entries (`id`, `name`,
|
|
14
|
+
`strategy: "idle"`); the plugin itself and `@deepseek-ai/*` core rows are locked.
|
|
15
|
+
- Estimated extra startup wait shown at the bottom of the form.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- The plugin page item renders a one-line summary in the list and the full form
|
|
19
|
+
when opened (view-aware: `summary` vs `page`).
|
|
20
|
+
- Right-side columns of the plugin list are fixed-width so grades, state and
|
|
21
|
+
cost line up.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- The form no longer crashes (and disappears) before the settings controller is
|
|
25
|
+
ready: snapshot reads, strict-context access and JSON edits are guarded.
|
|
26
|
+
- The list entry is registered through `plugins.item` so settings → plugins
|
|
27
|
+
shows the card again.
|
|
28
|
+
|
|
29
|
+
## [0.3.0] - 2026-11-07
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Per-field hints in the configuration form (zh/en), explaining what each
|
|
33
|
+
value changes.
|
|
34
|
+
- Plugin-market publishing: `scripts/gen-market-index.mjs` generates a
|
|
35
|
+
format-1 market index (`market/index.json`), and `docs/PUBLISHING.md`
|
|
36
|
+
documents the npm + index-source flow.
|
|
37
|
+
- npm metadata: keywords, author, engines, files whitelist, and
|
|
38
|
+
`@deepseek-ai/schemastery` as a runtime dependency.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- The form no longer depends on `props.ctx`; it reads the ctx captured in
|
|
42
|
+
`apply()`, matching the reference web-search card.
|
|
43
|
+
- README rewritten for the plugin page and npm: badges, install steps,
|
|
44
|
+
configuration table, development layout.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
- Configuration form showing "配置服务暂不可用" on newer DSH clients.
|
|
48
|
+
|
|
49
|
+
## [0.2.0] - 2026-11-07
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
- Graphical configuration form on the plugin page (`plugins.bundle.config`),
|
|
53
|
+
backed by the `deferred-boot` settings namespace; values are saved to the
|
|
54
|
+
current profile's Cordis patch, so `cordis.patch.yml` no longer has to be
|
|
55
|
+
edited by hand.
|
|
56
|
+
- `Config` schema with four volatile fields: `idleDelay`, `disposeTimeout`,
|
|
57
|
+
`readyFallbackMs`, `deferredPlugins`.
|
|
58
|
+
- Locale dictionaries (zh/en) for the plugin page and the form.
|
|
59
|
+
- Icon, LICENSE (MIT), CI workflow, and packaging scripts.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- `cannot set property "deferredBoot" without provide`: the service is now
|
|
63
|
+
exposed with `ctx.provide('deferredBoot', api)`, which the strict Cordis 4
|
|
64
|
+
context requires.
|
|
65
|
+
|
|
66
|
+
## [0.1.0] - 2026-11-07
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
- Initial scheduler: mounts deferred plugins after the main UI is ready, one
|
|
70
|
+
target per idle slice.
|
|
71
|
+
- Existence check before `loader.create`, per-target failure isolation,
|
|
72
|
+
`dispose` raced against `disposeTimeout`, three failed attempts freeze a
|
|
73
|
+
target as `failed`, and manual `ctx.deferredBoot.ensure(id)` mounting.
|
|
74
|
+
- Strategies: `idle`, `manual`, `inline-lazy`, `disabled`.
|
|
75
|
+
- Tests for the scheduler, config normalization, and the client module.
|
|
76
|
+
|
|
77
|
+
[0.3.1]: https://github.com/Bu-sheng/dsh-deferred-boot/releases/tag/v0.3.1
|
|
78
|
+
[0.3.0]: https://github.com/Bu-sheng/dsh-deferred-boot/releases/tag/v0.3.0
|
|
79
|
+
[0.2.0]: https://github.com/Bu-sheng/dsh-deferred-boot/releases/tag/v0.2.0
|
|
80
|
+
[0.1.0]: https://github.com/Bu-sheng/dsh-deferred-boot/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BuSheng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-deferred-boot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](#)
|
|
5
|
+
[](#)
|
|
6
|
+
|
|
7
|
+
把指定插件推迟到**主界面就绪之后**再挂载,一次空闲切片只挂一个,避免插件成批初始化拖住首屏。只搬运挂载时机,不修改目标插件。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- 就绪后再调度:优先 `connection.hostDescription`,退化为 `connection.host`、`app.ready` 轮询与兜底超时
|
|
12
|
+
- 分段空闲调度:`scheduler.postTask(background)` → `requestIdleCallback` → `setTimeout`,一次回调只挂一个目标
|
|
13
|
+
- 安全挂载:`loader.create` 前检查同名/同 id 条目,已存在则跳过并标记 `mounted`
|
|
14
|
+
- 故障隔离:每个目标独立 `try/catch`,连续 3 次失败冻结为 `failed`,不影响其余目标
|
|
15
|
+
- 卸载不挂死:`dispose` 与 `disposeTimeout` 竞速,超时后移出活跃表,迟到的 rejection 挂空处理
|
|
16
|
+
- 手动模式:`ctx.deferredBoot.ensure(id)` 随时补挂 `manual` 目标
|
|
17
|
+
- 图形配置:插件详情页直接改参数,保存写入当前 profile 的 Cordis patch
|
|
18
|
+
- 中英双语:插件页标题/描述与表单文案随界面语言切换
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
**本地包**(自带 `node_modules`,离线可用):
|
|
23
|
+
|
|
24
|
+
1. 解压到任意目录
|
|
25
|
+
2. 运行 `install.ps1`(自动定位 profile、建 `node_modules` 链接、写入 profile 的 `package.json`)
|
|
26
|
+
3. 重启 DSH
|
|
27
|
+
|
|
28
|
+
**npm**(发布后):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dsh plugin add dsh-deferred-boot
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
发布到插件市场(npm + 索引源)见 [docs/PUBLISHING.md](docs/PUBLISHING.md)。
|
|
35
|
+
|
|
36
|
+
## 配置
|
|
37
|
+
|
|
38
|
+
侧栏「插件」→ **dsh-deferred-boot** 详情页 → 页面描述下方的配置表单,保存即生效,不用手改配置文件。表单里每个字段下方都有同样的用途说明。
|
|
39
|
+
|
|
40
|
+
| 字段 | 默认 | 说明 |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `idleDelay` | 1200 | 就绪后到首次调度的延迟(毫秒)。调大更晚开始、首屏更稳;调小更早挂载目标。 |
|
|
43
|
+
| `disposeTimeout` | 10000 | 单个目标 dispose 的硬上界(毫秒)。超时视为已卸载并继续,防止热重载卡住调度。 |
|
|
44
|
+
| `readyFallbackMs` | 5000 | 就绪信号兜底超时(毫秒)。没有就绪信号时,最多等这么久就按已就绪处理。 |
|
|
45
|
+
| `deferredPlugins` | `[]` | JSON 数组,每项 `{ id, name, strategy, config }`。 |
|
|
46
|
+
|
|
47
|
+
`strategy`:
|
|
48
|
+
|
|
49
|
+
- `idle`:就绪后由分段空闲调度自动挂载
|
|
50
|
+
- `manual`:不自动挂载,调用 `ctx.deferredBoot.ensure(id)` 才挂载
|
|
51
|
+
- `inline-lazy`:不搬,只在日志里提示「目标应在自身 apply 内做 idle 初始化」
|
|
52
|
+
- `disabled`:跳过
|
|
53
|
+
|
|
54
|
+
保存后切换插件开关重载(或重启 DSH)使调度参数生效。等价 YAML:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
- id: deferred-boot
|
|
58
|
+
name: dsh-deferred-boot
|
|
59
|
+
config:
|
|
60
|
+
idleDelay: 1200
|
|
61
|
+
disposeTimeout: 10000
|
|
62
|
+
readyFallbackMs: 5000
|
|
63
|
+
deferredPlugins:
|
|
64
|
+
- id: heavy-plugin
|
|
65
|
+
name: heavy-plugin
|
|
66
|
+
strategy: idle
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 边界
|
|
70
|
+
|
|
71
|
+
本插件只搬挂载时机。目标插件 `apply` **内部**无法拆分的同步 CPU 长任务不会被拆分——那需要目标插件自己改成 idle 初始化。
|
|
72
|
+
|
|
73
|
+
## 开发
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
├── src/ host 半侧:index.js(apply / Config)、scheduler.js(调度器)
|
|
77
|
+
├── lib/client.js 浏览器半侧:插件详情页配置表单
|
|
78
|
+
├── locale/ zh/en 插件页标题与描述
|
|
79
|
+
├── test/ node:test 单元测试(调度器 / 配置 / 客户端模块)
|
|
80
|
+
├── docs/design.md 实现取舍、存在性检查、dispose 竞速、验收自检
|
|
81
|
+
├── cordis.patch.yml bundle 补丁
|
|
82
|
+
├── install.ps1 本地安装
|
|
83
|
+
├── pack.ps1 打包 zip
|
|
84
|
+
└── icon.svg 插件图标
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
node --test test/*.test.mjs # 运行测试
|
|
89
|
+
./pack.cmd # 重新打 zip 包
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
实现细节与已知限制见 [docs/design.md](docs/design.md)。
|
|
93
|
+
|
|
94
|
+
## 许可证
|
|
95
|
+
|
|
96
|
+
[MIT](LICENSE)
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# dsh-deferred-boot bundle patch.
|
|
2
|
+
#
|
|
3
|
+
# One insert layer over the profile root, following the bundle patch dialect:
|
|
4
|
+
# a patch is a mapping with `insert: [rows]`. The row mounts the scheduler
|
|
5
|
+
# itself; it is deliberately NOT marked `immediately: true`, so mounting this
|
|
6
|
+
# plugin never pulls heavy work into the startup window.
|
|
7
|
+
#
|
|
8
|
+
# Target plugins are listed under `deferredPlugins` and are mounted by the
|
|
9
|
+
# scheduler after the main UI is ready. Replace the empty list with real rows:
|
|
10
|
+
#
|
|
11
|
+
# - id: stats-panel
|
|
12
|
+
# name: "@scope/plugin-stats"
|
|
13
|
+
# config: {}
|
|
14
|
+
# strategy: idle
|
|
15
|
+
- insert:
|
|
16
|
+
- id: deferred-boot
|
|
17
|
+
name: dsh-deferred-boot
|
|
18
|
+
config:
|
|
19
|
+
idleDelay: 1200
|
|
20
|
+
disposeTimeout: 10000
|
|
21
|
+
readyFallbackMs: 5000
|
|
22
|
+
deferredPlugins: []
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 发布到插件市场
|
|
2
|
+
|
|
3
|
+
DSH 的「插件 → 发现」不是中心化官方市场:每个用户自己添加**插件索引源**(一个 HTTPS URL),市场读取其中的 JSON(格式 1)并列表、安装其中的组合包。所以「发布出去让别人能下载」= **发布包** + **托管一份索引 JSON** + **把索引 URL 给用户**。
|
|
4
|
+
|
|
5
|
+
目前没有官方插件索引;索引由发布者自己托管(GitHub Pages、gist raw、对象存储、任意 HTTPS 静态服务)。
|
|
6
|
+
|
|
7
|
+
## 1. 发布到 npm
|
|
8
|
+
|
|
9
|
+
包名 `dsh-deferred-boot` 已确认在 npm 上空闲。
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm login # 或配置 npm token / .npmrc
|
|
13
|
+
npm publish --access public # 首次发布
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- `package.json` 已声明 `dependencies: { "@deepseek-ai/schemastery": "^3.18.5" }`,npm/pnpm 安装时会自动拉取;本地 `node_modules` 不进 npm 包(`files` 白名单里没有)。
|
|
17
|
+
- 以后升级:`npm version patch`(或 minor/major)→ `npm publish` → 重新生成索引(见第 2 步)。
|
|
18
|
+
- 若临时不发 npm,也可以把索引的 `source` 改成 `{ "type": "git", "url": "https://github.com/<you>/dsh-deferred-boot", "commit": "<40 位 sha>" }`,但 git 方式要求仓库里带 `node_modules`(否则 `@deepseek-ai/schemastery` 缺失),体积较大,不推荐。
|
|
19
|
+
|
|
20
|
+
## 2. 生成市场索引
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node scripts/gen-market-index.mjs
|
|
24
|
+
# 可选:node scripts/gen-market-index.mjs --index-url https://<you>.github.io/dsh-deferred-boot/index.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
输出 `market/index.json`,格式 1,包含插件名、中英文标题/描述、内联图标(data: URL)、作者、关键词、版本列表(`source.type = "npm"`)与 `engines.dsh`。
|
|
28
|
+
|
|
29
|
+
## 3. 托管索引
|
|
30
|
+
|
|
31
|
+
任选一种:
|
|
32
|
+
|
|
33
|
+
- **GitHub Pages**:新建仓库 → 把 `market/index.json` 放到根目录 → Settings → Pages → 选分支 → 得到 `https://<you>.github.io/<repo>/index.json`。
|
|
34
|
+
- **Gist**:新建 gist 放入 `index.json` → 用 `https://gist.githubusercontent.com/.../raw/index.json`。
|
|
35
|
+
- **任意静态托管**:对象存储 / nginx / Cloudflare Pages 等,要求 HTTPS、可被匿名 GET。
|
|
36
|
+
|
|
37
|
+
## 4. 别人怎么安装
|
|
38
|
+
|
|
39
|
+
1. DSH 侧栏 **插件** → **发现** 标签页;
|
|
40
|
+
2. 点 **添加索引源**,粘贴第 3 步的索引 URL;
|
|
41
|
+
3. 在列表里找到 **延迟启动 / Deferred Boot**,点 **安装**,确认后安装并重启 DSH。
|
|
42
|
+
|
|
43
|
+
## 5. 版本更新
|
|
44
|
+
|
|
45
|
+
1. 改版本号并 `npm publish`;
|
|
46
|
+
2. `node scripts/gen-market-index.mjs`(会写入新的 `publishTimestamp` 与版本);
|
|
47
|
+
3. 推送新的 `market/index.json` 到托管位置;
|
|
48
|
+
4. 用户刷新「发现」即可看到新版本,点「有新版本」升级。
|
|
49
|
+
|
|
50
|
+
## 校验索引
|
|
51
|
+
|
|
52
|
+
DSH 用 `@deepseek-ai/dsh-plugin-market-index` 的 `parseMarketIndex` 校验索引(格式 1)。发布前可用任意安装该包的 Node 环境自检:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import { parseMarketIndex } from '@deepseek-ai/dsh-plugin-market-index'
|
|
56
|
+
const index = parseMarketIndex(JSON.parse(await readFile('market/index.json', 'utf8')), 'https://example.com/index.json')
|
|
57
|
+
console.log(index.plugins.map((p) => p.name))
|
|
58
|
+
```
|
package/docs/design.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# dsh-deferred-boot 实现细节
|
|
2
|
+
|
|
3
|
+
## 工程约束与实现取舍
|
|
4
|
+
|
|
5
|
+
### 挂载前存在性检查
|
|
6
|
+
|
|
7
|
+
`duplicate loader entry id` 在 DSH 中是硬错误:Loader 应用配置后一旦出现重复 id 会直接拒绝启动,没有可捕获的恢复路径。因此 `create()` 之前必须检查:
|
|
8
|
+
|
|
9
|
+
1. `loader.store[target.id]`
|
|
10
|
+
2. `loader.entries()` 中 `options.id === target.id` 或 `options.name === target.name`
|
|
11
|
+
3. `loader.resolve(target.id)`(缺失会抛错,属预期)
|
|
12
|
+
|
|
13
|
+
命中任一即标记 `mounted` 并跳过,绝不再挂第二份。
|
|
14
|
+
|
|
15
|
+
### 每个 create 独立 try/catch
|
|
16
|
+
|
|
17
|
+
单个目标失败只记录 `attempts`/`lastError` 并继续下一个;连续 3 次失败标记 `failed` 后停止重试,调度循环不中断、异常不冒泡到启动路径。
|
|
18
|
+
|
|
19
|
+
### dispose 与超时竞速
|
|
20
|
+
|
|
21
|
+
`fiber.dispose()` 会等到所有异步清理兑现才 resolve;目标卡在等一个永不返回的服务时,`dispose()` 会无限挂起。卡死的 dispose 持有 mutation lock 时,整个界面会「点了没反应」。
|
|
22
|
+
|
|
23
|
+
本插件的做法:先把目标从活跃表移除(`target.handle = null`),再让 `dispose()` 与 `disposeTimeout` 竞速;超时只记日志并继续。原始 Promise 始终挂一个 `catch`,晚到的 rejection 不会变成 unhandled rejection。
|
|
24
|
+
|
|
25
|
+
### 分段调度
|
|
26
|
+
|
|
27
|
+
一次空闲回调**只处理一个目标**,剩余目标留给下一次调度。调度原语优先级:
|
|
28
|
+
|
|
29
|
+
1. `scheduler.postTask({ priority: "background" })`
|
|
30
|
+
2. `requestIdleCallback(fn, { timeout: 1000 })`
|
|
31
|
+
3. `setTimeout(fn, 0)`(host 端 Node 没有前两者时使用)
|
|
32
|
+
|
|
33
|
+
### 就绪等待的多重兜底
|
|
34
|
+
|
|
35
|
+
1. `ctx.connection.hostDescription`(字符串或函数)
|
|
36
|
+
2. `ctx.connection.host`(ready 握手发布的 `{ home }` 对象,流一断即清空)
|
|
37
|
+
3. 任一暴露 `ready === true` 的服务(`app` / `webApp`)
|
|
38
|
+
4. `readyFallbackMs` 计时兜底
|
|
39
|
+
|
|
40
|
+
不硬编码任何宿主事件名。
|
|
41
|
+
|
|
42
|
+
### HMR 重载后的状态重建
|
|
43
|
+
|
|
44
|
+
插件重载后从当前 loader 的存在性检查重建状态(`status()` 内部调用 `rebuildFromLoader()`),不会把已挂载目标再挂一遍。
|
|
45
|
+
|
|
46
|
+
### 目标依赖在延迟窗口内消失
|
|
47
|
+
|
|
48
|
+
调度器不负责替目标等待依赖;目标自身必须正确声明 `inject`。延迟挂载会暴露「假设服务启动时一定在」的脆弱性,这是目标插件的问题。
|
|
49
|
+
|
|
50
|
+
### 卸载清理
|
|
51
|
+
|
|
52
|
+
通过 `ctx.effect(() => () => scheduler.stop())` 注册清理,插件卸载后无残留定时器/观察者。
|
|
53
|
+
|
|
54
|
+
## 图形界面实现
|
|
55
|
+
|
|
56
|
+
- Host 侧 `Config` 的四个字段声明为 `.volatile()`;`src/scheduler.js` 在 `normalizeConfig` 里解包 volatile 引用(`get()`)后再使用。
|
|
57
|
+
- 客户端 `lib/client.js` 通过 `ctx.configForms.whileServed(['deferred-boot'], ...)` 等 settings 命名空间就绪,再向 `plugins.bundle.config` 槽位注册表单(key 为包名 `dsh-deferred-boot`);保存走 `ctx.configForms.mutate`,一次提交四个字段。
|
|
58
|
+
- `package.json` 的 `dsh.client.inject` 列出客户端依赖包,`exports["./client"]` 指向 `lib/client.js`。
|
|
59
|
+
|
|
60
|
+
## 验收自检
|
|
61
|
+
|
|
62
|
+
`test/scheduler.test.mjs` 覆盖:
|
|
63
|
+
|
|
64
|
+
- [x] 本插件不设 `immediately: true`;`apply` 内无 I/O、计算、`await`,同步返回
|
|
65
|
+
- [x] 目标已存在于 loader:跳过并标记 `mounted`,不产生重复 entry
|
|
66
|
+
- [x] 单个目标 `loader.create` 抛错:其余目标正常挂载,该目标为 `failed`
|
|
67
|
+
- [x] 目标 `dispose()` 永不结算:`disposeTimeout` 后调度器继续,晚到 reject 不变成 unhandled rejection
|
|
68
|
+
- [x] 就绪信号缺失时,`readyFallbackMs` 后仍能继续调度
|
|
69
|
+
- [x] `manual` 目标在 `ensure(id)` 前不挂载
|
|
70
|
+
- [x] 调度器自身被重载后,不会把已挂载目标再挂一遍
|
|
71
|
+
|
|
72
|
+
`test/config.test.mjs` 覆盖 Config 的 volatile 解析与调度器归一化;`test/client.test.mjs` 覆盖客户端模块加载、slot 注册与表单首次渲染。
|
|
73
|
+
|
|
74
|
+
## 已知限制
|
|
75
|
+
|
|
76
|
+
- `hostDescription` 在当前 checkout 的 connection 服务上未以该名字导出;实际可用信号是 `host`(ready 握手对象)。探测顺序已按仓库现状适配。
|
|
77
|
+
- 调度器只负责挂载时机;目标插件在 `apply` 内的同步重活不会被本插件拆分。
|
|
78
|
+
- 图形界面依赖 settings 服务;settings 不可用时表单显示「配置服务尚未就绪」,插件本身仍正常工作。
|
package/icon.svg
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none">
|
|
2
|
+
<rect x="2" y="2" width="60" height="60" rx="14" fill="#4D6BFE"/>
|
|
3
|
+
<circle cx="32" cy="34" r="13" stroke="#FFFFFF" stroke-width="3.5"/>
|
|
4
|
+
<path d="M32 26.5V34L37 37" stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
|
+
<path d="M13 18H23M13 24H20" stroke="#FFFFFF" stroke-opacity="0.55" stroke-width="3" stroke-linecap="round"/>
|
|
6
|
+
<path d="M43 22L47 18M47 22L43 18" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round"/>
|
|
7
|
+
</svg>
|