fix(client): preload injected module factories
This commit is contained in:
parent
91b545daf5
commit
5549b9add5
3 changed files with 41 additions and 13 deletions
|
|
@ -42,11 +42,10 @@ declare module '@deepseek-ai/cordis' {
|
|||
/**
|
||||
* One composed client entry pushed by the host (a graph row). Wire
|
||||
* single source: the host node half (package root) produces this same shape.
|
||||
* `immediately` marks stage-one prefetch; `inject` is informational graph
|
||||
* metadata (the authoritative edges live in each package's `dsh.client`
|
||||
* declaration and reach fibers through entry creation). `external` carries
|
||||
* module-graph edges: unlike `inject`, they constrain code arrival because
|
||||
* `require` is synchronous (see {@link WebBootGraph.entries}).
|
||||
* `immediately` marks stage-one prefetch. `inject` names package rows whose
|
||||
* factories must arrive before this row materializes, while Cordis separately
|
||||
* uses the same package edges to compose entries. `external` carries exact
|
||||
* non-inject module requests (see {@link WebBootGraph.entries}).
|
||||
*/
|
||||
export interface WebBootEntry {
|
||||
/** Entry name == package name. */
|
||||
|
|
@ -55,7 +54,7 @@ export interface WebBootEntry {
|
|||
url: string
|
||||
/** Bundle content hash (cache-busting consistency anchor). */
|
||||
rev: string
|
||||
/** Package-name dependency edges, informational (preflight display / HMR diffing). */
|
||||
/** Package-name dependency edges used for factory arrival and plugin composition. */
|
||||
inject?: string[]
|
||||
/** Stage-one prefetch mark: load the script for factory registration during module-face boot. */
|
||||
immediately?: boolean
|
||||
|
|
@ -83,6 +82,8 @@ export interface BootModuleRow {
|
|||
url: string
|
||||
/** Bundle content hash. */
|
||||
rev: string
|
||||
/** Injected package rows whose factories arrive before this row materializes. */
|
||||
inject: string[]
|
||||
/** Module specifiers this row requests from the module table ([] when the wire omits them). */
|
||||
external: string[]
|
||||
}
|
||||
|
|
@ -176,6 +177,7 @@ export function parseBootManifest(wire: unknown): BootManifest {
|
|||
id: row.id,
|
||||
url: row.url,
|
||||
rev: row.rev,
|
||||
inject: inject === undefined ? [] : [...inject],
|
||||
external: external === undefined ? [] : [...external],
|
||||
})
|
||||
plugins.push({
|
||||
|
|
|
|||
|
|
@ -124,8 +124,12 @@ export class ClientModuleSystem implements ClientModuleLoader {
|
|||
return task
|
||||
}
|
||||
|
||||
/** Register each unresolved dynamic request before registering its consumer. */
|
||||
private async arriveGraphRow(row: BootModuleRow, open: readonly string[] = []): Promise<void> {
|
||||
/** Register each injected package and unresolved dynamic request before its consumer. */
|
||||
private async arriveGraphRow(
|
||||
row: BootModuleRow,
|
||||
open: readonly string[] = [],
|
||||
visited = new Set<string>(),
|
||||
): Promise<void> {
|
||||
const cycleStart = open.indexOf(row.id)
|
||||
if (cycleStart !== -1) {
|
||||
throw new Error(
|
||||
|
|
@ -133,12 +137,18 @@ export class ClientModuleSystem implements ClientModuleLoader {
|
|||
+ '(the host must reject this graph before serving it)',
|
||||
)
|
||||
}
|
||||
if (visited.has(row.id)) return
|
||||
visited.add(row.id)
|
||||
const next = [...open, row.id]
|
||||
for (const request of row.external) {
|
||||
const id = stripClientSuffix(request)
|
||||
if (this.seed.has(request) || this.loadCache.has(id)) continue
|
||||
const dependency = this.graphRows.get(id)
|
||||
if (dependency !== undefined) await this.arriveGraphRow(dependency, next)
|
||||
if (dependency !== undefined) await this.arriveGraphRow(dependency, next, visited)
|
||||
}
|
||||
for (const packageName of row.inject) {
|
||||
const dependency = this.graphRows.get(packageName)
|
||||
if (dependency !== undefined) await this.arriveGraphRow(dependency, [], visited)
|
||||
}
|
||||
await this.arrive(row)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ afterEach(() => {
|
|||
})
|
||||
|
||||
const row = (id: string, fields: Partial<BootModuleRow> = {}): BootModuleRow =>
|
||||
({ id, url: `/plugins/${id}/client.js?rev=0`, rev: '0', external: [], ...fields })
|
||||
({ id, url: `/plugins/${id}/client.js?rev=0`, rev: '0', inject: [], external: [], ...fields })
|
||||
|
||||
interface Bench {
|
||||
loader: ClientModuleLoader
|
||||
|
|
@ -147,6 +147,22 @@ describe('lazy CJS arrival', () => {
|
|||
expect(exports.react.marker).toBe('react')
|
||||
})
|
||||
|
||||
it('registers injected package factories before materializing a consumer', async () => {
|
||||
const b = bench([
|
||||
row('consumer', { inject: ['provider'] }),
|
||||
row('provider', { inject: ['consumer'] }),
|
||||
], {
|
||||
consumer: req => ({ provider: req('provider/client') }),
|
||||
provider: () => ({ marker: 'provider' }),
|
||||
})
|
||||
const exports = await b.loader.import('consumer', '', {}) as { provider: { marker: string } }
|
||||
expect(b.fetched).toEqual([
|
||||
'/plugins/provider/client.js?rev=0',
|
||||
'/plugins/consumer/client.js?rev=0',
|
||||
])
|
||||
expect(exports.provider.marker).toBe('provider')
|
||||
})
|
||||
|
||||
it('concurrent callers share one in-flight arrival and materialize once', async () => {
|
||||
const ran: string[] = []
|
||||
const url = '/plugins/a/client.js?rev=0'
|
||||
|
|
@ -306,13 +322,13 @@ describe('boot manifest wire', () => {
|
|||
const manifest = parseBootManifest({
|
||||
rev: 'graph',
|
||||
entries: [
|
||||
{ id: 'a', url: '/plugins/a/client.js', rev: '1' },
|
||||
{ id: 'a', url: '/plugins/a/client.js', rev: '1', inject: ['b'] },
|
||||
{ id: 'b', url: '/plugins/b/client.js', rev: '2', external: ['react'] },
|
||||
],
|
||||
})
|
||||
expect(manifest.modules).toEqual([
|
||||
{ id: 'a', url: '/plugins/a/client.js', rev: '1', external: [] },
|
||||
{ id: 'b', url: '/plugins/b/client.js', rev: '2', external: ['react'] },
|
||||
{ id: 'a', url: '/plugins/a/client.js', rev: '1', inject: ['b'], external: [] },
|
||||
{ id: 'b', url: '/plugins/b/client.js', rev: '2', inject: [], external: ['react'] },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue