fix(boot): expose profile recomposition settlement to observers
This commit is contained in:
1 parent
1144cf1e8e
commit
8c33df5d8e
9 files changed
+44
-7
No files matched your search
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/core.md
|
||||
core.md: 5b1a249d4d0b2df05750e62044ee539a24b32b0e
|
||||
core.zh.md: 2c38ffe6ec9956fc436d4a4f7e2fcc3d1f7420f8
|
||||
core.md: a5f61dddd033ad5332fdc2f940b48029c1eedd27
|
||||
core.zh.md: 1b0599e9121702dae5163fe93438044491574072
|
||||
@@ -948,6 +948,14 @@ userDisables(entry: Entry): boolean
|
||||
* @throws when preparation fails or the root Include cannot accept the update.
|
||||
*/
|
||||
async recompose(options: { reloadBundles?: boolean } = {}): Promise<readonly EntryIssue[]>
|
||||
|
||||
/**
|
||||
* Wait for recompositions already queued when called, including removed-fiber cleanup.
|
||||
* Observers may read accepted composition facts afterwards; a failed operation
|
||||
* still reports its error to its caller and does not reject this observation.
|
||||
* @returns after the current recomposition queue settles.
|
||||
*/
|
||||
whenIdle(): Promise<void>
|
||||
```
|
||||
|
||||
Source: [`packages/boot/app-boot/src/profile-runtime.ts`](../../packages/boot/app-boot/src/profile-runtime.ts)
|
||||
|
||||
@@ -958,6 +958,14 @@ userDisables(entry: Entry): boolean
|
||||
* @throws when preparation fails or the root Include cannot accept the update.
|
||||
*/
|
||||
async recompose(options: { reloadBundles?: boolean } = {}): Promise<readonly EntryIssue[]>
|
||||
|
||||
/**
|
||||
* Wait for recompositions already queued when called, including removed-fiber cleanup.
|
||||
* Observers may read accepted composition facts afterwards; a failed operation
|
||||
* still reports its error to its caller and does not reject this observation.
|
||||
* @returns after the current recomposition queue settles.
|
||||
*/
|
||||
whenIdle(): Promise<void>
|
||||
```
|
||||
|
||||
Source: [`packages/boot/app-boot/src/profile-runtime.ts`](../../packages/boot/app-boot/src/profile-runtime.ts)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/boot/app-boot/README.md
|
||||
README.md: 2bcdeff9ca96f8ee54e0a8ef2474496e8cf0776e
|
||||
README.zh.md: 2cbb7e7e022abe6cebc6b4f5ae468598040476ae
|
||||
README.md: 455b55910f8089a8833b46d99673fd55928f5f01
|
||||
README.zh.md: c941328f77e663b5bfd75fde20fa7ca3f213c219
|
||||
@@ -60,7 +60,7 @@ Inserted plugin names may be absolute filesystem paths, file URLs, or package sp
|
||||
|
||||
Installed dependencies default to `external`; template bundles and packages in `dsh.profile.firstParty` are `builtin`. Effective stage is `dsh.profile.stages[package]`, then the package’s `dsh.bundle.stage`, then `runtime`. Stage selects startup failure policy, not a later execution phase. Bundle patches retain their declared ids, parents, and ordering. Built-in and boot-staged layers claim ids first; an optional bundle with a duplicate id is omitted whole and reported, while a conflicting user insert is omitted per row. `dependencies` records installation; `dsh.profile.bundles` selects enabled layers, including all their inserts and overrides.
|
||||
|
||||
The launcher provides `ctx.profileRuntime` before any configuration entry mounts. It owns row provenance, accepted composition, conflicts, and user-disabled rows. Watchers and management operations share its serial recomposition queue. Recomposition waits for current entries and removed fibers, then publishes accepted options and reports per-entry issues; a failed update can leave a fiber running its previous valid config. `installFailLoud` remains installed until shutdown for process-level unhandled rejections.
|
||||
The launcher provides `ctx.profileRuntime` before any configuration entry mounts. It owns row provenance, accepted composition, conflicts, and user-disabled rows. Watchers and management operations share its serial recomposition queue. Recomposition waits for current entries and removed fibers, then publishes accepted options and reports per-entry issues; a failed update can leave a fiber running its previous valid config. `installFailLoud` remains installed until shutdown for process-level unhandled rejections. Observers can await `whenIdle()` before publishing a refreshed view of composition ownership.
|
||||
|
||||
### Previewing the effective configuration
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
|
||||
|
||||
已安装依赖默认为 `external`;模板组合包和 `dsh.profile.firstParty` 中的包为 `builtin`。有效 stage 依次取 `dsh.profile.stages[package]`、包的 `dsh.bundle.stage`,最后默认为 `runtime`。stage 决定启动失败策略,不表示稍后执行。组合包 patch 保留声明的 id、父组和顺序。内置与 boot 阶段层优先占有 id;可选组合包有重复 id 时整层被排除并报告,冲突的用户插入则逐行排除。`dependencies` 记录安装;`dsh.profile.bundles` 选择启用的层,包括它们的全部插入和覆盖。
|
||||
|
||||
launcher 在任何配置行挂载前提供 `ctx.profileRuntime`。它拥有行来源、已接受的组合、冲突和用户禁用行信息。文件监听与管理操作共用它的串行重组队列。重组等待当前条目和已移除 fiber 完成后,发布已接受的选项并报告逐行问题;更新失败时,fiber 可能仍使用先前的有效配置运行。`installFailLoud` 保持到应用关闭,处理进程级未处理 rejection。
|
||||
launcher 在任何配置行挂载前提供 `ctx.profileRuntime`。它拥有行来源、已接受的组合、冲突和用户禁用行信息。文件监听与管理操作共用它的串行重组队列。重组等待当前条目和已移除 fiber 完成后,发布已接受的选项并报告逐行问题;更新失败时,fiber 可能仍使用先前的有效配置运行。`installFailLoud` 保持到应用关闭,处理进程级未处理 rejection。 观测方可等待 `whenIdle()` 后,再发布包含组合归属的刷新视图。
|
||||
|
||||
### 预览生效配置
|
||||
|
||||
|
||||
@@ -183,6 +183,16 @@ export class ProfileRuntime extends Service {
|
||||
this.committed = { profile, stack }
|
||||
return inspectEntryIssues(this.ctx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for recompositions already queued when called, including removed-fiber cleanup.
|
||||
* Observers may read accepted composition facts afterwards; a failed operation
|
||||
* still reports its error to its caller and does not reject this observation.
|
||||
* @returns after the current recomposition queue settles.
|
||||
*/
|
||||
whenIdle(): Promise<void> {
|
||||
return this.queue
|
||||
}
|
||||
}
|
||||
|
||||
/** The walk behind {@link ProfileRuntime.userDisables}: the entry, then each group holding it, outward. */
|
||||
|
||||
@@ -147,10 +147,14 @@ describe('ProfileRuntime', () => {
|
||||
// Enabling a bundle re-reads the profile and waits on the tree; a watcher fires meanwhile.
|
||||
const enabling = runtime.recompose({ reloadBundles: true })
|
||||
const watching = runtime.recompose()
|
||||
const observed = vi.fn()
|
||||
const idle = runtime.whenIdle().then(observed)
|
||||
await Promise.resolve()
|
||||
expect(applied).toEqual(['composed-for-2'])
|
||||
expect(observed).not.toHaveBeenCalled()
|
||||
release()
|
||||
await Promise.all([enabling, watching])
|
||||
await Promise.all([enabling, watching, idle])
|
||||
expect(observed).toHaveBeenCalledOnce()
|
||||
// The watcher composed from the profile the enable committed, not the one before it.
|
||||
expect(applied).toEqual(['composed-for-2', 'composed-for-2'])
|
||||
expect(compose.mock.calls.at(-1)?.[0]).toBe(reloaded)
|
||||
@@ -169,6 +173,7 @@ describe('ProfileRuntime', () => {
|
||||
const second = runtime.recompose()
|
||||
await expect(first).rejects.toThrow('rejected')
|
||||
await expect(second).resolves.toEqual([])
|
||||
await expect(runtime.whenIdle()).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('keeps the committed profile, provenance, and conflicts when the root include rejects the update', async () => {
|
||||
|
||||
@@ -1434,6 +1434,12 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
returns: 'current entry issues after application, without rolling back successful siblings.',
|
||||
throws: ['when preparation fails or the root Include cannot accept the update.'],
|
||||
},
|
||||
{
|
||||
signature: 'whenIdle(): Promise<void>',
|
||||
description: 'Wait for recompositions already queued when called, including removed-fiber cleanup. Observers may read accepted composition facts afterwards; a failed operation still reports its error to its caller and does not reject this observation.',
|
||||
parameters: [],
|
||||
returns: 'after the current recomposition queue settles.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user