refactor(gui): host graph from dshClient discovery; webserver self-watches bundles for HMR
The registry scans mounted Loader entries' dshClient declarations and
composes __DSH_BOOT__ {rev, entries} — inject edges and the immediately
mark come from manifests, never hand-copied; malformed fields fail loud
at load. The composing app owns one flat roster plus the --dev switch
(hmr row and bundle watching are dev-graph decisions).
The rebuild signal is the webserver's own observation: in dev mode the
registry stat-polls each scanned bundle (fs.watchFile; polling because
network mounts deliver no inotify), re-hashes on change, and broadcasts
a rebuilt frame on the /plugins/events SSE channel only when the rev
actually changed. Watch membership follows the table across rescans;
dispose drops all watches; a torn read self-heals on the next tick.
The POST /plugins/rebuilt endpoint is gone — builders and the host
share zero protocol. dsh web --dev logs the watched bundle list and
each rebuilt id with its rev transition.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Webserver invariant companion: the boot-manifest consistency audit — every
|
||||
* registry snapshot row must resolve a clientPath, checked on fiber lifecycle
|
||||
* events against the assembly-published 'webPlugins' context key.
|
||||
* Webserver invariant companion: the boot-graph consistency audit — every
|
||||
* fetch-arrival graph row must resolve a clientPath, checked on fiber
|
||||
* lifecycle events against the assembly-published 'webPlugins' context key.
|
||||
*/
|
||||
import { Context } from 'cordis'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
@@ -9,7 +9,7 @@ import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import * as WebserverInvariant from '../src/invariant.ts'
|
||||
|
||||
interface RegistryStub {
|
||||
snapshot(): { id: string; url: string }[]
|
||||
graph(): { entries: { id: string; url: string }[] }
|
||||
clientPath(id: string): string | undefined
|
||||
}
|
||||
|
||||
@@ -33,18 +33,18 @@ describe('webserver manifest invariant', () => {
|
||||
expect(() => { trigger(bare) }).not.toThrow() // no 'webPlugins' key published
|
||||
|
||||
const consistent = await setup({
|
||||
snapshot: () => [{ id: 'p1', url: '/plugins/p1/client.js' }],
|
||||
clientPath: () => '/tmp/p1/lib/client.js',
|
||||
graph: () => ({ entries: [{ id: 'p1', url: '/plugins/p1/client.js?rev=abc' }] }),
|
||||
clientPath: id => id === 'p1' ? '/tmp/p1/lib/client.js' : undefined,
|
||||
})
|
||||
expect(() => { trigger(consistent) }).not.toThrow()
|
||||
})
|
||||
|
||||
it('throws on a manifest row whose bundle path no longer resolves', async () => {
|
||||
it('throws on a graph row whose bundle path no longer resolves', async () => {
|
||||
const ctx = await setup({
|
||||
snapshot: () => [{ id: 'ghost', url: '/plugins/ghost/client.js' }],
|
||||
graph: () => ({ entries: [{ id: 'ghost', url: '/plugins/ghost/client.js?rev=abc' }] }),
|
||||
clientPath: () => undefined,
|
||||
})
|
||||
expect(() => { trigger(ctx) })
|
||||
.toThrow(/manifest row "ghost".*resolves no client bundle path/)
|
||||
.toThrow(/graph row "ghost".*resolves no client bundle path/)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user