Download packages/client/modules/tests/node-half.client.spec.ts from SaylorTwift/deepseek-harness: direct link, hf CLI and curl.
- Browser
- Download file 41.2 kB
-
https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/client/modules/tests/node-half.client.spec.ts
- Command line
-
hf download hf://SaylorTwift/deepseek-harness/packages/client/modules/tests/node-half.client.spec.ts
-
curl -L -o node-half.client.spec.ts https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/client/modules/tests/node-half.client.spec.ts
41.2 kB
| /** Node-half composition diagnostics for package metadata and built client bundles. */ | |
| import { mkdirSync, mkdtempSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs' | |
| import type { IncomingMessage, ServerResponse } from 'node:http' | |
| import { SourceMap } from 'node:module' | |
| import { tmpdir } from 'node:os' | |
| import { dirname, join } from 'node:path' | |
| import { pathToFileURL } from 'node:url' | |
| import { runInNewContext } from 'node:vm' | |
| import { Context, FiberState, type Fiber } from '@deepseek-ai/cordis' | |
| import { afterEach, describe, expect, it, vi } from 'vitest' | |
| import { renderIndexInjections, type WebServer, type WebRoute } from '@deepseek-ai/dsh-host-webserver' | |
| import * as modulesClient from '../src/client/index.ts' | |
| import { ClientModuleRegistry, bootInjections, orderByModuleGraph } from '../src/index.ts' | |
| import type { ClientModuleLoaderTarget, WebBootEntry, WebBootGraph } from '../src/client/index.ts' | |
| const MODULES_ID = '@deepseek-ai/dsh-client-modules' | |
| const UI_RENDERER_ID = '@deepseek-ai/dsh-client-ui-renderer' | |
| const comboUrl = (ids: readonly string[], rev: string): string => | |
| `/plugins/??${ids.map(id => `${id}/client.js`).join(',')}&rev=${rev}` | |
| const mapUrl = (url: string): string => url.replace(/\/client\.js(?=,|&rev=)/g, '/client.js.map') | |
| const BOOTSTRAP_URL = comboUrl([MODULES_ID], 'boot') | |
| const APPLICATION_URL = comboUrl([UI_RENDERER_ID], 'app') | |
| let root: string | undefined | |
| const contexts: { ctx: Context; ready?: Promise<WebRoute> }[] = [] | |
| afterEach(async () => { | |
| await Promise.all(contexts.splice(0).map(async ({ ctx, ready }) => { | |
| await ready | |
| await ctx.fiber.dispose() | |
| })) | |
| if (root !== undefined) rmSync(root, { recursive: true, force: true }) | |
| root = undefined | |
| }) | |
| it.each([false, true])('tracks the Web carrier lifetime when server-first is %s', async (serverFirst) => { | |
| const ctx = new Context() | |
| contexts.push({ ctx }) | |
| ctx.provide('loader', { entries: () => [] }) | |
| const routes = new Set<WebRoute>() | |
| const mountServer = () => ctx.plugin((serverCtx) => { | |
| serverCtx.provide('webServer', { | |
| register: (route: WebRoute) => { | |
| routes.add(route) | |
| return () => { routes.delete(route) } | |
| }, | |
| } as WebServer) | |
| }) | |
| let server = serverFirst ? await mountServer() : undefined | |
| const modules = await ctx.plugin(ClientModuleRegistry) | |
| const service = ctx.get('clientModules')! | |
| expect(service.graph().entries).toEqual([]) | |
| expect((await service.fetchBundle(new Request('http://localhost/plugins/missing'))).status).toBe(404) | |
| if (!serverFirst) { | |
| expect(routes.size).toBe(0) | |
| server = await mountServer() | |
| } | |
| await expect.poll(() => routes.size).toBe(1) | |
| await server!.dispose() | |
| await expect.poll(() => routes.size).toBe(0) | |
| expect(modules.state).toBe(FiberState.ACTIVE) | |
| expect((await service.fetchBundle(new Request('http://localhost/plugins/missing'))).status).toBe(404) | |
| await mountServer() | |
| await expect.poll(() => routes.size).toBe(1) | |
| await modules.dispose() | |
| expect(routes.size).toBe(0) | |
| }) | |
| /** Create a resolvable package whose client export points at the returned path. */ | |
| function writePackage( | |
| packageName: string, | |
| metadata: Record<string, unknown> = { dsh: { client: { platform: 'web' } } }, | |
| ): string { | |
| root ??= realpathSync(mkdtempSync(join(tmpdir(), 'dsh-client-modules-'))) | |
| const pkgRoot = join(root, 'node_modules', ...packageName.split('/')) | |
| const clientPath = join(pkgRoot, 'lib', 'client.js') | |
| mkdirSync(pkgRoot, { recursive: true }) | |
| writeFileSync(join(pkgRoot, 'package.json'), JSON.stringify({ | |
| name: packageName, | |
| exports: { | |
| './client': './lib/client.js', | |
| './package.json': './package.json', | |
| }, | |
| ...metadata, | |
| })) | |
| return clientPath | |
| } | |
| /** Create a built package with the supplied client declaration. */ | |
| function writeBuiltPackage(packageName: string, client: Record<string, unknown>): void { | |
| const clientPath = writePackage(packageName, { dsh: { client: { platform: 'web', ...client } } }) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| } | |
| /** Construct the node-half service and capture its plugin-bundle route. */ | |
| function constructWithRoute( | |
| packageNames: string[], | |
| options: { | |
| contextBaseUrl?: string | |
| entryBaseUrl?: string | |
| internal?: NonNullable<Context['loader']['internal']> | |
| } = {}, | |
| ): { context: Context; service: ClientModuleRegistry; route: Promise<WebRoute> } { | |
| const ctx = new Context() | |
| const owned: typeof contexts[number] = { ctx } | |
| contexts.push(owned) | |
| ctx.baseUrl = options.contextBaseUrl ?? pathToFileURL(root!).href + '/' | |
| ctx.provide('loader', { | |
| internal: options.internal, | |
| *entries() { | |
| for (const packageName of packageNames) { | |
| yield { | |
| options: { name: packageName }, | |
| fiber: {}, | |
| disabled: false, | |
| parent: { tree: { ctx: { baseUrl: options.entryBaseUrl ?? ctx.baseUrl } } }, | |
| } | |
| } | |
| }, | |
| }) | |
| const route = Promise.withResolvers<WebRoute>() | |
| const webServer: Pick<WebServer, 'port' | 'register' | 'tapIndex'> = { | |
| port: 0, | |
| register: (candidate) => { | |
| if (candidate.path === '/plugins') route.resolve(candidate) | |
| return () => {} | |
| }, | |
| tapIndex: () => () => {}, | |
| } | |
| ctx.provide('webServer', webServer as WebServer) | |
| const service = new ClientModuleRegistry(ctx) | |
| owned.ready = route.promise | |
| return { context: ctx, service, route: route.promise } | |
| } | |
| /** Construct the node-half service over the enabled fixture entries. */ | |
| function construct(packageNames: string[]): ClientModuleRegistry { | |
| return constructWithRoute(packageNames).service | |
| } | |
| /** Invoke the registered plugin route and capture status, headers, and bytes. */ | |
| async function routeRequest(route: Promise<WebRoute>, url: string, method = 'GET'): Promise<{ | |
| status: number | |
| headers: Record<string, string> | undefined | |
| body: Buffer | |
| }> { | |
| let status = 0 | |
| let headers: Record<string, string> | undefined | |
| let body = Buffer.alloc(0) | |
| const response = { | |
| writeHead(nextStatus: number, nextHeaders?: Record<string, string>) { | |
| status = nextStatus | |
| headers = nextHeaders | |
| return response | |
| }, | |
| end(chunk?: Uint8Array) { | |
| body = chunk === undefined ? Buffer.alloc(0) : Buffer.from(chunk) | |
| return response | |
| }, | |
| } as unknown as ServerResponse | |
| await (await route).handler({ method, url } as IncomingMessage, response) | |
| return { status, headers, body } | |
| } | |
| /** Execute the exact first inline script emitted by the Host boot rows. */ | |
| function injectedFacade(graph: WebBootGraph): { html: string; target: ClientModuleLoaderTarget } { | |
| const html = renderIndexInjections( | |
| '<html><head></head><body><script type="module" src="/index.js"></script></body></html>', | |
| bootInjections(graph), | |
| ) | |
| const source = /<head><script>([\s\S]*?)<\/script>/.exec(html)?.[1] | |
| if (source === undefined) throw new Error('missing injected ModuleLoader facade script') | |
| const window: { __ModuleLoader__?: ClientModuleLoaderTarget } = {} | |
| runInNewContext(source, { window }) | |
| if (window.__ModuleLoader__ === undefined) throw new Error('facade script did not install __ModuleLoader__') | |
| return { html, target: window.__ModuleLoader__ } | |
| } | |
| const bootGraph = (): WebBootGraph => ({ | |
| rev: 'graph', | |
| entries: [ | |
| { id: MODULES_ID, url: comboUrl([MODULES_ID], 'm'), rev: 'm' }, | |
| { id: UI_RENDERER_ID, url: comboUrl([UI_RENDERER_ID], 'r'), rev: 'r' }, | |
| ], | |
| batches: [ | |
| { | |
| phase: 'bootstrap', | |
| url: BOOTSTRAP_URL, | |
| rev: 'boot', | |
| entries: [MODULES_ID], | |
| }, | |
| { | |
| phase: 'application', | |
| url: APPLICATION_URL, | |
| rev: 'app', | |
| entries: [UI_RENDERER_ID], | |
| }, | |
| ], | |
| }) | |
| describe('HTML bootstrap facade', () => { | |
| it('precedes blocking preloads and the boot graph, then becomes the live registration target', async () => { | |
| const graph = bootGraph() | |
| const { html, target } = injectedFacade(graph) | |
| const facadeAt = html.indexOf('window.__ModuleLoader__=') | |
| const applicationAt = html.indexOf( | |
| `<link rel="preload" as="script" href="${APPLICATION_URL.replaceAll('&', '&')}">`, | |
| ) | |
| const bootstrapAt = html.indexOf(`<script src="${BOOTSTRAP_URL.replaceAll('&', '&')}"></script>`) | |
| const graphAt = html.indexOf('globalThis["__DSH_BOOT__"] = ') | |
| const entryAt = html.indexOf('<script type="module" src="/index.js"></script>') | |
| expect([facadeAt, applicationAt, bootstrapAt, graphAt, entryAt]).toEqual([...new Set([ | |
| facadeAt, applicationAt, bootstrapAt, graphAt, entryAt, | |
| ])].sort((a, b) => a - b)) | |
| target.load({ id: MODULES_ID, factory: () => modulesClient }) | |
| const system = target.create({ | |
| boot: graph, | |
| staticModules: {}, | |
| loadBundle: async (url) => { | |
| expect(url).toBe(APPLICATION_URL) | |
| target.load({ id: UI_RENDERER_ID, factory: () => ({ marker: 'ui-renderer' }) }) | |
| }, | |
| }) | |
| expect(target.mode).toBe('live') | |
| expect(target.pendingQueue).toEqual([]) | |
| expect(system.manifest.rev).toBe('graph') | |
| expect(await system.import(MODULES_ID)).toBe(modulesClient) | |
| expect(await system.import(`${UI_RENDERER_ID}/client`)).toEqual({ marker: 'ui-renderer' }) | |
| expect(() => target.create({ boot: graph, staticModules: {} })) | |
| .toThrow('create called after module-system boot') | |
| }) | |
| it('preloads every application combo', () => { | |
| const graph = bootGraph() | |
| const secondId = '@fixture/second-application-combo' | |
| const secondUrl = comboUrl([secondId], 'app-2') | |
| graph.entries.push({ id: secondId, url: comboUrl([secondId], 'row-2'), rev: 'row-2' }) | |
| graph.batches.push({ phase: 'application', url: secondUrl, rev: 'app-2', entries: [secondId] }) | |
| expect(bootInjections(graph).flatMap(row => row.kind === 'script-preload' ? [row.src] : [])) | |
| .toEqual([APPLICATION_URL, secondUrl]) | |
| }) | |
| it('rejects a page that did not preload the modules bundle', () => { | |
| const graph = bootGraph() | |
| const { target } = injectedFacade(graph) | |
| expect(() => target.create({ boot: graph, staticModules: {} })) | |
| .toThrow(`HTML did not preload ${MODULES_ID}/client.js`) | |
| }) | |
| it('rejects a bootstrap bundle with a runtime external', () => { | |
| const graph = bootGraph() | |
| const { target } = injectedFacade(graph) | |
| target.load({ | |
| id: MODULES_ID, | |
| factory: (require) => { | |
| require('react') | |
| return modulesClient | |
| }, | |
| }) | |
| expect(() => target.create({ boot: graph, staticModules: {} })) | |
| .toThrow(`${MODULES_ID}/client.js requested external "react"`) | |
| }) | |
| it.each([ | |
| null, | |
| { ...modulesClient, createClientModuleSystem: undefined }, | |
| { ...modulesClient, apply: undefined }, | |
| ])('rejects a bootstrap bundle without the complete module face', (exports) => { | |
| const graph = bootGraph() | |
| const { target } = injectedFacade(graph) | |
| target.load({ id: MODULES_ID, factory: () => exports as unknown as Record<string, unknown> }) | |
| expect(() => target.create({ boot: graph, staticModules: {} })) | |
| .toThrow(`${MODULES_ID}/client.js did not export the bootstrap module face`) | |
| }) | |
| }) | |
| describe('client bundle activation', () => { | |
| it.each(['v1', 'v2'] as const)( | |
| 'resolves %s package metadata from the owning entry tree', | |
| (version) => { | |
| const packageName = `@fixture/entry-base-${version}` | |
| const clientPath = writePackage(packageName) | |
| const hostPath = join(dirname(clientPath), 'index.js') | |
| mkdirSync(dirname(hostPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const contextBaseUrl = pathToFileURL(join(root!, 'profile')).href + '/' | |
| const entryBaseUrl = pathToFileURL(join(root!, 'overlay')).href + '/' | |
| const calls: unknown[][] = [] | |
| const resolveSync = (...args: unknown[]) => { | |
| calls.push(args) | |
| return { format: 'module' as const, url: pathToFileURL(hostPath).href } | |
| } | |
| const internal = { version, resolveSync } | |
| const { service } = constructWithRoute([packageName], { | |
| contextBaseUrl, | |
| entryBaseUrl, | |
| internal: internal as NonNullable<Context['loader']['internal']>, | |
| }) | |
| expect(calls).toEqual(version === 'v2' | |
| ? [[entryBaseUrl, { specifier: packageName, attributes: {} }]] | |
| : [[packageName, entryBaseUrl, {}]]) | |
| expect(service.clientPath(packageName)).toBe(clientPath) | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| }, | |
| ) | |
| it('derives the browser module id from a file entry owning manifest', () => { | |
| const packageName = '@fixture/file-entry' | |
| const clientPath = writePackage(packageName) | |
| const hostPath = join(dirname(clientPath), 'index.js') | |
| mkdirSync(dirname(hostPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const service = construct([pathToFileURL(hostPath).href]) | |
| expect(service.clientPath(packageName)).toBe(clientPath) | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| }) | |
| it.each(['relative', 'absolute'] as const)( | |
| 'finds the owning manifest through the %s-path fallback without Node loader internals', | |
| (kind) => { | |
| const packageName = `@fixture/${kind}-fallback-entry` | |
| const clientPath = writePackage(packageName) | |
| const packageRoot = dirname(dirname(clientPath)) | |
| const hostPath = join(packageRoot, 'index.js') | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const loaderName = kind === 'relative' ? './index.js' : hostPath | |
| const { service } = constructWithRoute([loaderName], { | |
| entryBaseUrl: pathToFileURL(packageRoot).href + '/', | |
| }) | |
| expect(service.clientPath(packageName)).toBe(clientPath) | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| }, | |
| ) | |
| it.each(['v1', 'v2', 'worker'] as const)( | |
| 'derives a file entry package id through the %s Loader resolver', | |
| (version) => { | |
| const packageName = `@fixture/file-entry-${version}` | |
| const clientPath = writePackage(packageName) | |
| const hostPath = join(dirname(clientPath), 'index.js') | |
| mkdirSync(dirname(hostPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const loaderName = pathToFileURL(hostPath).href | |
| const entryBaseUrl = pathToFileURL(join(root!, 'overlay')).href + '/' | |
| const calls: unknown[][] = [] | |
| const resolveSync = (...args: unknown[]) => { | |
| calls.push(args) | |
| return { format: 'module' as const, url: loaderName } | |
| } | |
| const internal = { version, resolveSync } | |
| const { service } = constructWithRoute([loaderName], { | |
| entryBaseUrl, | |
| internal: internal as NonNullable<Context['loader']['internal']>, | |
| }) | |
| expect(calls).toEqual(version === 'v2' | |
| ? [[entryBaseUrl, { specifier: loaderName, attributes: {} }]] | |
| : [[loaderName, entryBaseUrl, {}]]) | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| }, | |
| ) | |
| it('rejects distinct active Loader sources for one browser package', () => { | |
| const packageName = '@fixture/duplicate-source' | |
| const clientPath = writePackage(packageName) | |
| const hostPath = join(dirname(clientPath), 'index.js') | |
| mkdirSync(dirname(hostPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const alias = './duplicate-source.js' | |
| const internal = { | |
| version: 'v2' as const, | |
| resolveSync: () => ({ format: 'module' as const, url: pathToFileURL(hostPath).href }), | |
| } | |
| expect(() => constructWithRoute([packageName, alias], { | |
| internal: internal as unknown as NonNullable<Context['loader']['internal']>, | |
| })).toThrow( | |
| `client-modules: package ${packageName} resolves from multiple active Loader sources:`, | |
| ) | |
| }) | |
| it('promotes the remaining Loader source after the selected alias unloads', async () => { | |
| const packageName = '@fixture/duplicate-source-recovery' | |
| const clientPath = writePackage(packageName) | |
| const hostPath = join(dirname(clientPath), 'index.js') | |
| mkdirSync(dirname(hostPath), { recursive: true }) | |
| writeFileSync(hostPath, 'export default {}\n') | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| const alias = './duplicate-source-recovery.js' | |
| const entries = [packageName] | |
| const internal = { | |
| version: 'v2' as const, | |
| resolveSync: () => ({ format: 'module' as const, url: pathToFileURL(hostPath).href }), | |
| } | |
| const { context, service } = constructWithRoute(entries, { | |
| internal: internal as unknown as NonNullable<Context['loader']['internal']>, | |
| }) | |
| const firstRevision = service.graph().entries[0]!.rev | |
| const warning = vi.spyOn(context.logger, 'warn').mockImplementation(() => undefined) | |
| entries.push(alias) | |
| emitLoaderEntryChange(context, alias) | |
| await Promise.resolve() | |
| expect(warning).toHaveBeenCalledWith(expect.objectContaining({ | |
| message: expect.stringContaining(`package ${packageName} resolves from multiple active Loader sources`) as string, | |
| })) | |
| expect(service.graph().entries[0]!.rev).toBe(firstRevision) | |
| entries.splice(entries.indexOf(packageName), 1) | |
| emitLoaderEntryChange(context, packageName) | |
| await Promise.resolve() | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| expect(service.graph().entries[0]!.rev).not.toBe(firstRevision) | |
| expect(service.clientPath(packageName)).toBe(clientPath) | |
| }) | |
| it('uses owning-tree package resolution for an import-only Worker module loader', () => { | |
| const packageName = '@fixture/worker-loader' | |
| writeBuiltPackage(packageName, {}) | |
| const internal = { | |
| version: 'worker', | |
| import: async () => ({}), | |
| } as unknown as NonNullable<Context['loader']['internal']> | |
| const { service } = constructWithRoute([packageName], { internal }) | |
| expect(service.graph().entries.map(entry => entry.id)).toEqual([packageName]) | |
| }) | |
| it('allows sibling dsh roles', () => { | |
| const currentName = '@fixture/current-client-field' | |
| const clientPath = writePackage(currentName, { | |
| dsh: { | |
| bundle: { patch: './cordis.patch.yml' }, | |
| client: { platform: 'web' }, | |
| profile: { bundles: [] }, | |
| }, | |
| }) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| expect(construct([currentName]).graph().entries.map(entry => entry.id)).toEqual([currentName]) | |
| }) | |
| it('groups missing bundles under one source-build instruction with a package/path list', () => { | |
| const firstName = '@fixture/missing-first' | |
| const secondName = '@fixture/missing-second' | |
| const firstPath = writePackage(firstName) | |
| const secondPath = writePackage(secondName) | |
| expect(() => construct([firstName, secondName])).toThrow([ | |
| 'client-modules: 2 client packages failed to compose:', | |
| ' client bundles not found; run `pnpm run build` before launch:', | |
| ` - package: ${firstName}`, | |
| ` path: ${firstPath}`, | |
| ` - package: ${secondName}`, | |
| ` path: ${secondPath}`, | |
| ].join('\n')) | |
| }) | |
| it('does not report other bundle read failures as missing builds', () => { | |
| const packageName = '@fixture/unreadable-client' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(clientPath, { recursive: true }) | |
| let thrown: unknown | |
| try { | |
| construct([packageName]) | |
| } catch (error) { | |
| thrown = error | |
| } | |
| expect(String(thrown)).toContain('client-modules: 1 client package failed to compose:') | |
| expect(String(thrown)).toContain(' other failures:') | |
| expect(String(thrown)).toContain('EISDIR') | |
| expect(String(thrown)).not.toContain('pnpm run build') | |
| }) | |
| it('falls back to a generated-file map when an authored map is malformed', async () => { | |
| const packageName = '@fixture/malformed-source-map' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| writeFileSync(`${clientPath}.map`, '{') | |
| const torn = constructWithRoute([packageName]) | |
| const tornRow = torn.service.graph().entries[0]! | |
| expect((await routeRequest(torn.route, tornRow.url)).body.toString('utf8')) | |
| .toContain(`sourceMappingURL=${mapUrl(tornRow.url)}`) | |
| const fallback = await routeRequest(torn.route, mapUrl(torn.service.graph().batches[0]!.url)) | |
| expect(JSON.parse(fallback.body.toString('utf8'))).toMatchObject({ | |
| sections: [{ map: { sources: [`/plugins/${packageName}/client.js`] } }], | |
| }) | |
| writeFileSync(`${clientPath}.map`, '{"version":3,"sources":[null]}\n') | |
| expect(() => construct([packageName])).not.toThrow() | |
| writeFileSync(`${clientPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sourceRoot: 'http://[', | |
| sources: ['src/index.ts'], | |
| })) | |
| const invalidUrl = constructWithRoute([packageName]) | |
| const invalidMapUrl = mapUrl(invalidUrl.service.graph().batches[0]!.url) | |
| expect(JSON.parse((await routeRequest(invalidUrl.route, invalidMapUrl)).body.toString('utf8'))).toMatchObject({ | |
| sections: [{ map: { sources: [`/plugins/${packageName}/client.js`] } }], | |
| }) | |
| }) | |
| it('reads a source map only on its first map GET', async () => { | |
| const packageName = '@fixture/lazy-source-map' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n//# sourceMappingURL=client.js.map') | |
| writeFileSync(`${clientPath}.map`, '{') | |
| const { service, route } = constructWithRoute([packageName]) | |
| const batch = service.graph().batches[0]! | |
| const sourceMapUrl = mapUrl(batch.url) | |
| const script = await routeRequest(route, batch.url) | |
| expect(script.status).toBe(200) | |
| expect((await routeRequest(route, sourceMapUrl, 'HEAD')).body).toHaveLength(0) | |
| writeFileSync(`${clientPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['src/first.ts'], | |
| })) | |
| const first = await routeRequest(route, sourceMapUrl) | |
| expect(JSON.parse(first.body.toString('utf8'))).toMatchObject({ | |
| sections: [{ map: { sources: [`/plugins/${packageName}/src/first.ts`] } }], | |
| }) | |
| writeFileSync(`${clientPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['src/second.ts'], | |
| })) | |
| expect((await routeRequest(route, sourceMapUrl)).body).toEqual(first.body) | |
| expect((await routeRequest(route, batch.url)).body).toEqual(script.body) | |
| }) | |
| it('retains a materialized resource when an unrelated row recomposes the graph', async () => { | |
| const stablePackage = '@fixture/stable-source-map' | |
| const rebuiltPackage = '@fixture/rebuilt-neighbor' | |
| const stablePath = writePackage(stablePackage) | |
| const rebuiltPath = writePackage(rebuiltPackage) | |
| for (const clientPath of [stablePath, rebuiltPath]) { | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n//# sourceMappingURL=client.js.map') | |
| writeFileSync(`${clientPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['src/first.ts'], | |
| })) | |
| } | |
| const { service, route } = constructWithRoute([stablePackage, rebuiltPackage]) | |
| const stableUrl = service.graph().entries.find(entry => entry.id === stablePackage)!.url | |
| const stableMapUrl = mapUrl(stableUrl) | |
| const first = await routeRequest(route, stableMapUrl) | |
| writeFileSync(`${stablePath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['src/second.ts'], | |
| })) | |
| writeFileSync(rebuiltPath, 'module.exports = { rebuilt: true }\n') | |
| service.rebuilt(rebuiltPackage) | |
| expect((await routeRequest(route, stableMapUrl)).body).toEqual(first.body) | |
| }) | |
| it('maps packed combo sections back to each generated client bundle', async () => { | |
| const names = ['@fixture/generated-first', '@fixture/generated-second'] | |
| for (const [index, packageName] of names.entries()) { | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync( | |
| clientPath, | |
| `window.generation = ${String(index)}\n//# sourceURL=packages/client/generated-${String(index)}/lib/client.js`, | |
| ) | |
| } | |
| const { service, route } = constructWithRoute(names) | |
| const batch = service.graph().batches[0]! | |
| const script = (await routeRequest(route, batch.url)).body.toString('utf8') | |
| expect(script).not.toContain('//# sourceURL=') | |
| expect(script).toContain(`//# sourceMappingURL=${mapUrl(batch.url)}`) | |
| const payload = JSON.parse((await routeRequest(route, mapUrl(batch.url))).body.toString('utf8')) as { | |
| sections: { map: { mappings: string; sources: string[]; sourcesContent: string[] } }[] | |
| } | |
| expect(payload.sections.map(section => section.map)).toEqual([ | |
| { | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['/packages/client/generated-0/lib/client.js'], | |
| sourcesContent: ['window.generation = 0\n'], | |
| }, | |
| { | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['/packages/client/generated-1/lib/client.js'], | |
| sourcesContent: ['window.generation = 1\n'], | |
| }, | |
| ]) | |
| const consumer = new SourceMap(payload as unknown as ConstructorParameters<typeof SourceMap>[0]) | |
| expect(consumer.findEntry(0, 0)).toMatchObject({ | |
| originalSource: '/packages/client/generated-0/lib/client.js', | |
| }) | |
| expect(consumer.findEntry(2, 0)).toMatchObject({ | |
| originalSource: '/packages/client/generated-1/lib/client.js', | |
| }) | |
| }) | |
| it('retains one prior immutable batch generation across rebuild recomposition', async () => { | |
| const packageName = '@fixture/batch-rebuild-race' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = { generation: 1 }\n') | |
| const { service, route } = constructWithRoute([packageName]) | |
| const first = service.graph().batches[0]!.url | |
| const firstSize = service.artifactBaseline(packageName)!.size | |
| writeFileSync(clientPath, 'module.exports = { generation: 200 }\n') | |
| service.rebuilt(packageName) | |
| const second = service.graph().batches[0]!.url | |
| expect(second).not.toBe(first) | |
| expect(service.artifactBaseline(packageName)!.size).toBeGreaterThan(firstSize) | |
| expect((await routeRequest(route, first)).status).toBe(200) | |
| expect((await routeRequest(route, second)).status).toBe(200) | |
| writeFileSync(clientPath, 'module.exports = { generation: 3 }\n') | |
| service.rebuilt(packageName) | |
| const third = service.graph().batches[0]!.url | |
| expect((await routeRequest(route, first)).status).toBe(404) | |
| expect((await routeRequest(route, second)).status).toBe(200) | |
| expect((await routeRequest(route, third)).status).toBe(200) | |
| }) | |
| it('assigns opaque startup revisions instead of deriving them from artifact content', () => { | |
| const firstName = '@fixture/startup-revision-first' | |
| const secondName = '@fixture/startup-revision-second' | |
| writeBuiltPackage(firstName, {}) | |
| writeBuiltPackage(secondName, {}) | |
| const service = construct([firstName, secondName]) | |
| const [first, second] = service.graph().entries | |
| const firstMatch = /^(?<nonce>[a-f\d]{16})-(?<sequence>\d+)$/.exec(first!.rev) | |
| const secondMatch = /^(?<nonce>[a-f\d]{16})-(?<sequence>\d+)$/.exec(second!.rev) | |
| expect(firstMatch?.groups).toMatchObject({ sequence: '0' }) | |
| expect(secondMatch?.groups).toMatchObject({ nonce: firstMatch?.groups?.nonce, sequence: '1' }) | |
| const firstPath = service.clientPath(firstName)! | |
| const firstStat = statSync(firstPath) | |
| expect(service.artifactBaseline(firstName)).toEqual({ | |
| path: firstPath, | |
| mtimeMs: firstStat.mtimeMs, | |
| size: firstStat.size, | |
| }) | |
| expect(service.artifactBaseline('@fixture/unknown')).toBeUndefined() | |
| }) | |
| it('splits startup combos before the map-form URL exceeds 3 KiB', async () => { | |
| const packageNames = Array.from({ length: 48 }, (_, index) => ( | |
| `@fixture/combo-url-${String(index).padStart(3, '0')}-${'x'.repeat(40)}` | |
| )) | |
| const sourceMap = JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['src/index.ts'], | |
| }) | |
| for (const packageName of packageNames) { | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| writeFileSync(`${clientPath}.map`, sourceMap) | |
| } | |
| const { service, route } = constructWithRoute(packageNames) | |
| const batches = service.graph().batches.filter(batch => batch.phase === 'application') | |
| expect(batches.length).toBeGreaterThan(1) | |
| expect(batches.flatMap(batch => batch.entries)).toEqual(packageNames) | |
| for (const batch of batches) { | |
| expect(Buffer.byteLength(batch.url)).toBeLessThanOrEqual(3 * 1024) | |
| expect(Buffer.byteLength(mapUrl(batch.url))).toBeLessThanOrEqual(3 * 1024) | |
| expect((await routeRequest(route, batch.url)).status).toBe(200) | |
| expect((await routeRequest(route, mapUrl(batch.url))).status).toBe(200) | |
| } | |
| for (let index = 0; index < batches.length - 1; index += 1) { | |
| const entries = [...batches[index]!.entries, batches[index + 1]!.entries[0]!] | |
| expect(Buffer.byteLength(mapUrl(comboUrl(entries, '0'.repeat(12))))).toBeGreaterThan(3 * 1024) | |
| } | |
| }) | |
| it('serves the source map beside a registered client bundle', async () => { | |
| const packageName = '@fixture/source-map' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n//# sourceMappingURL=client.js.map') | |
| const map = '{"version":3,"names":[],"mappings":"AAAA","sources":["../../../packages/client/demo/src/index.tsx","https://cdn.example.test/library.js"]}\n' | |
| writeFileSync(`${clientPath}.map`, map) | |
| const { service, route } = constructWithRoute([packageName]) | |
| const row = service.graph().entries[0]! | |
| const singleScript = await routeRequest(route, row.url) | |
| expect(singleScript.body.toString('utf8')).toContain(`sourceMappingURL=${mapUrl(row.url)}`) | |
| const singleMap = await routeRequest(route, mapUrl(row.url)) | |
| expect(singleMap.status).toBe(200) | |
| expect(singleMap.headers).toEqual({ | |
| 'content-type': 'application/json; charset=utf-8', | |
| 'cache-control': 'public, max-age=31536000, immutable', | |
| }) | |
| expect(JSON.parse(singleMap.body.toString('utf8'))).toMatchObject({ | |
| version: 3, | |
| file: 'client.js', | |
| sections: [{ | |
| offset: { line: 0, column: 0 }, | |
| map: { | |
| ...(JSON.parse(map) as Record<string, unknown>), | |
| sources: ['/packages/client/demo/src/index.tsx', 'https://cdn.example.test/library.js'], | |
| }, | |
| }], | |
| }) | |
| const batch = service.graph().batches[0]! | |
| expect(batch).toMatchObject({ phase: 'application', entries: [packageName] }) | |
| const batchScript = await routeRequest(route, batch.url) | |
| expect(batchScript.status).toBe(200) | |
| expect(batchScript.headers?.['cache-control']).toBe('public, max-age=31536000, immutable') | |
| expect(batchScript.body.toString('utf8')).toContain(`//# sourceMappingURL=${mapUrl(batch.url)}`) | |
| const shellResponse = await service.fetchBundle(new Request(`dsh-app://app${batch.url}`)) | |
| expect(shellResponse.status).toBe(200) | |
| expect(shellResponse.headers.get('cache-control')).toBe('public, max-age=31536000, immutable') | |
| expect(await shellResponse.text()).toBe(batchScript.body.toString('utf8')) | |
| expect((await routeRequest(route, batch.url, 'HEAD')).body).toHaveLength(0) | |
| expect((await routeRequest(route, batch.url, 'POST')).status).toBe(405) | |
| const batchMap = await routeRequest(route, mapUrl(batch.url)) | |
| const parsedBatchMap = JSON.parse(batchMap.body.toString('utf8')) as unknown | |
| const parsedPluginMap = JSON.parse(map) as Record<string, unknown> | |
| expect(parsedBatchMap).toMatchObject({ | |
| version: 3, | |
| file: 'client.js', | |
| sections: [{ | |
| offset: { line: 0, column: 0 }, | |
| map: { | |
| ...parsedPluginMap, | |
| sources: ['/packages/client/demo/src/index.tsx', 'https://cdn.example.test/library.js'], | |
| }, | |
| }], | |
| }) | |
| expect((await routeRequest(route, `${row.url}&stale=1`.replace(`rev=${row.rev}`, 'rev=stale'))).status).toBe(404) | |
| writeFileSync(`${clientPath}.map`, '{"version":3,"names":[],"mappings":"AAAA","sources":["src/changed.tsx"]}\n') | |
| const nextRev = service.rebuilt(packageName) | |
| expect(nextRev).not.toBe(row.rev) | |
| const nextRow = service.graph().entries[0]! | |
| expect(nextRow.rev).toBe(nextRev) | |
| const nextMap = await routeRequest(route, mapUrl(nextRow.url)) | |
| expect(JSON.parse(nextMap.body.toString('utf8'))).toMatchObject({ | |
| sections: [{ map: { sources: ['/plugins/@fixture/source-map/src/changed.tsx'] } }], | |
| }) | |
| }) | |
| it('applies sourceRoot before relocating absolute-looking section sources', async () => { | |
| const packageName = '@fixture/source-root' | |
| const clientPath = writePackage(packageName) | |
| mkdirSync(dirname(clientPath), { recursive: true }) | |
| writeFileSync(clientPath, 'module.exports = {}\n') | |
| writeFileSync(`${clientPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sourceRoot: '../root', | |
| sources: ['/absolute.ts'], | |
| })) | |
| const { service, route } = constructWithRoute([packageName]) | |
| const response = await routeRequest(route, mapUrl(service.graph().batches[0]!.url)) | |
| const map = JSON.parse(response.body.toString('utf8')) as { | |
| sections: { map: { sourceRoot?: string; sources: string[] } }[] | |
| } | |
| expect(map.sections[0]?.map).toMatchObject({ | |
| sources: ['/plugins/@fixture/root/absolute.ts'], | |
| }) | |
| expect(map.sections[0]?.map).not.toHaveProperty('sourceRoot') | |
| }) | |
| it('maps a non-zero second batch section through a standard source-map consumer', async () => { | |
| const firstName = '@fixture/offset-first' | |
| const secondName = '@fixture/offset-second' | |
| const firstPath = writePackage(firstName) | |
| const secondPath = writePackage(secondName) | |
| for (const [path, source] of [ | |
| [firstPath, '../../../packages/demo/first.ts'], | |
| [secondPath, '../../../packages/demo/second.ts'], | |
| ] as const) { | |
| mkdirSync(dirname(path), { recursive: true }) | |
| writeFileSync(path, 'window.first = true\nwindow.second = true\n') | |
| writeFileSync(`${path}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: [source], | |
| sourcesContent: ['export {}\n'], | |
| })) | |
| } | |
| const { service, route } = constructWithRoute([firstName, secondName]) | |
| const response = await routeRequest(route, mapUrl(service.graph().batches[0]!.url)) | |
| const payload = JSON.parse(response.body.toString('utf8')) as ConstructorParameters<typeof SourceMap>[0] | |
| const sections = (payload as unknown as { | |
| sections: { offset: { line: number; column: number } }[] | |
| }).sections | |
| expect(sections.map(section => section.offset)).toEqual([ | |
| { line: 0, column: 0 }, | |
| { line: 3, column: 0 }, | |
| ]) | |
| const consumer = new SourceMap(payload) | |
| expect(consumer.findEntry(0, 0)).toMatchObject({ originalSource: '/packages/demo/first.ts' }) | |
| expect(consumer.findEntry(3, 0)).toMatchObject({ originalSource: '/packages/demo/second.ts' }) | |
| }) | |
| it('combines a generated-file fallback with a later authored map', async () => { | |
| const unmappedName = '@fixture/unmapped-first' | |
| const mappedName = '@fixture/mapped-second' | |
| const unmappedPath = writePackage(unmappedName) | |
| const mappedPath = writePackage(mappedName) | |
| mkdirSync(dirname(unmappedPath), { recursive: true }) | |
| mkdirSync(dirname(mappedPath), { recursive: true }) | |
| writeFileSync(unmappedPath, 'window.unmapped = true\n') | |
| writeFileSync(mappedPath, 'window.mapped = true\n') | |
| writeFileSync(`${mappedPath}.map`, JSON.stringify({ | |
| version: 3, | |
| names: [], | |
| mappings: 'AAAA', | |
| sources: ['../../../packages/demo/mapped.ts'], | |
| sourcesContent: ['export {}\n'], | |
| })) | |
| const { service, route } = constructWithRoute([unmappedName, mappedName]) | |
| const response = await routeRequest(route, mapUrl(service.graph().batches[0]!.url)) | |
| const payload = JSON.parse(response.body.toString('utf8')) as ConstructorParameters<typeof SourceMap>[0] | |
| const consumer = new SourceMap(payload) | |
| expect(consumer.findEntry(0, 0)).toMatchObject({ | |
| originalSource: `/plugins/${unmappedName}/client.js`, | |
| }) | |
| expect(consumer.findEntry(2, 0)).toMatchObject({ originalSource: '/packages/demo/mapped.ts' }) | |
| }) | |
| }) | |
| function emitLoaderEntryChange(context: Context, name: string): void { | |
| context.emit('internal/plugin', { | |
| entry: { options: { name } }, | |
| } as unknown as Fiber) | |
| } | |
| describe('shared module declarations', () => { | |
| it('accepts external requests and carries them onto the graph row', () => { | |
| const packageName = '@fixture/shared-declared' | |
| writeBuiltPackage(packageName, { external: ['react'] }) | |
| expect(construct([packageName]).graph().entries).toEqual([{ | |
| id: packageName, | |
| url: expect.stringContaining(`/plugins/??${packageName}/client.js&rev=`) as unknown as string, | |
| rev: expect.any(String) as unknown as string, | |
| external: ['react'], | |
| }]) | |
| }) | |
| it('omits external when the package declares no requests', () => { | |
| const packageName = '@fixture/shared-absent' | |
| writeBuiltPackage(packageName, {}) | |
| const [row] = construct([packageName]).graph().entries | |
| expect(row).not.toHaveProperty('external') | |
| }) | |
| it('rejects a non-array external', () => { | |
| const packageName = '@fixture/external-not-array' | |
| writeBuiltPackage(packageName, { external: 'react' }) | |
| expect(() => construct([packageName])) | |
| .toThrow(`client-modules: ${packageName} dsh.client.external must be a string array`) | |
| }) | |
| }) | |
| describe('module graph order', () => { | |
| const entry = (id: string, fields: Partial<WebBootEntry> = {}): WebBootEntry => | |
| ({ id, url: comboUrl([id], '0'), rev: '0', ...fields }) | |
| const ids = (entries: readonly WebBootEntry[]): string[] => entries.map(row => row.id) | |
| it('places every requested package row before its consumers along a chain', () => { | |
| expect(ids(orderByModuleGraph([ | |
| entry('ui', { external: ['slots'] }), | |
| entry('slots', { external: ['render'] }), | |
| entry('render'), | |
| ]))).toEqual(['render', 'slots', 'ui']) | |
| }) | |
| it('places a shared package row before both arms of a diamond', () => { | |
| expect(ids(orderByModuleGraph([ | |
| entry('app', { external: ['left', 'right'] }), | |
| entry('left', { external: ['vendor'] }), | |
| entry('right', { external: ['vendor'] }), | |
| entry('vendor'), | |
| ]))).toEqual(['vendor', 'left', 'right', 'app']) | |
| }) | |
| it('resolves a /client request onto the requested package row', () => { | |
| expect(ids(orderByModuleGraph([ | |
| entry('ui', { external: ['runtime/client'] }), | |
| entry('runtime'), | |
| ]))).toEqual(['runtime', 'ui']) | |
| }) | |
| it('leaves a request no row answers to the static assembly channel', () => { | |
| expect(ids(orderByModuleGraph([ | |
| entry('consumer', { external: ['@deepseek-ai/cordis'] }), | |
| entry('other'), | |
| ]))).toEqual(['consumer', 'other']) | |
| }) | |
| it('rejects a cycle and names the packages on it', () => { | |
| expect(() => orderByModuleGraph([ | |
| entry('a', { external: ['b'] }), | |
| entry('b', { external: ['a'] }), | |
| ])).toThrow('client-modules: module graph cycle a -> b -> a') | |
| }) | |
| it('rejects a row requesting its own package name', () => { | |
| expect(() => orderByModuleGraph([entry('solo', { external: ['solo'] })])) | |
| .toThrow('client-modules: "solo" requests module "solo" that it answers itself') | |
| }) | |
| it('composes the served graph in module-graph order', () => { | |
| const consumerName = '@fixture/order-consumer' | |
| const dependencyName = '@fixture/order-dependency' | |
| writeBuiltPackage(consumerName, { external: [dependencyName] }) | |
| writeBuiltPackage(dependencyName, {}) | |
| expect(ids(construct([consumerName, dependencyName]).graph().entries)) | |
| .toEqual([dependencyName, consumerName]) | |
| }) | |
| it('fails activation loud when scanned packages form a module cycle', () => { | |
| writeBuiltPackage('@fixture/cycle-a', { external: ['@fixture/cycle-b'] }) | |
| writeBuiltPackage('@fixture/cycle-b', { external: ['@fixture/cycle-a'] }) | |
| expect(() => construct(['@fixture/cycle-a', '@fixture/cycle-b'])) | |
| .toThrow('module graph cycle @fixture/cycle-a -> @fixture/cycle-b -> @fixture/cycle-a') | |
| }) | |
| }) | |