Files
deepseek-harness/packages/experimental/webworker-runtime/tests/client/load-bundle.spec.ts
T
Turtle 4b1d4c814e fix(web): speed up lazy bundle loads in the Web preview
The Web preview Worker converted multi-megabyte combo scripts and source
maps with the buffer package's JavaScript UTF-8 codec, and the page
re-encoded each source map to Base64 in JavaScript before running the
bundle. Use TextEncoder/TextDecoder for whole-buffer UTF-8 conversions
and FileReader for the inline source-map data URL.
2026-10-22 14:32:35 +08:00

110 lines
4.2 KiB
TypeScript

// @vitest-environment jsdom
import { afterEach, expect, it, vi } from 'vitest'
import { WorkerTunnel } from '../../src/client/client.ts'
import { packTar } from '../../src/storage/tar.ts'
type StubListener = (event: { data?: unknown }) => void
function stubWorker(): {
worker: Worker
sent: { t: string; id: number; url: string }[]
deliver: (frame: unknown) => void
} {
const listeners: StubListener[] = []
const sent: { t: string; id: number; url: string }[] = []
return {
worker: {
addEventListener: (type: string, listener: StubListener) => {
if (type === 'message') listeners.push(listener)
},
postMessage: (frame: unknown) => { sent.push(frame as { t: string; id: number; url: string }) },
} as unknown as Worker,
sent,
deliver: (frame) => { for (const listener of listeners) listener({ data: frame }) },
}
}
afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllGlobals()
document.head.innerHTML = ''
})
it('loads a combo map through the tunnel and embeds it in the blob script', async () => {
document.head.innerHTML = '<base href="/static/preview/">'
const { worker, sent, deliver } = stubWorker()
const tunnel = new WorkerTunnel(worker)
const blobs: Blob[] = []
const revoked: string[] = []
const NativeURL = URL
class StubURL extends NativeURL {
static override createObjectURL(blob: Blob): string {
blobs.push(blob)
return `blob:fixture-${String(blobs.length)}`
}
static override revokeObjectURL(url: string): void {
revoked.push(url)
}
}
vi.stubGlobal('URL', StubURL)
vi.spyOn(document.head, 'append').mockImplementation((...nodes) => {
for (const node of nodes) {
if (typeof node !== 'string') queueMicrotask(() => { node.dispatchEvent(new Event('load')) })
}
})
// The worker's Host starts at '/', independently of the preview document directory.
const scriptUrl = 'plugins/??a/client.js,b/client.js&rev=abc'
const mapUrl = '??a/client.js.map,b/client.js.map&rev=abc'
const loading = tunnel.loadBundle(scriptUrl)
expect(sent[0]?.url).toBe(`http://localhost:3000/${scriptUrl}`)
deliver({
t: 'res',
id: 1,
status: 200,
headers: { 'content-type': 'text/javascript' },
body: new TextEncoder().encode(`factory();\n//# sourceMappingURL=${mapUrl}\n`).buffer,
})
await vi.waitFor(() => { expect(sent).toHaveLength(2) })
expect(sent[1]?.url).toBe(`http://localhost:3000/plugins/${mapUrl}`)
const map = '{"version":3,"names":["中文"],"sections":[]}'
deliver({
t: 'res',
id: 2,
status: 200,
headers: { 'content-type': 'application/json' },
body: new TextEncoder().encode(map).buffer,
})
await loading
const source = await blobs[0]?.text()
const encoded = /sourceMappingURL=data:application\/json;charset=utf-8;base64,([^\s]+)/.exec(source ?? '')?.[1]
if (encoded === undefined) throw new Error('localized bundle has no inline source map')
const decoded = Uint8Array.from(atob(encoded), char => char.charCodeAt(0))
expect(new TextDecoder().decode(decoded)).toBe(map)
expect(revoked).toEqual(['blob:fixture-1'])
})
it('reads the package-asset archive through the tunnel into a synchronous resolver', async () => {
const { worker, sent, deliver } = stubWorker()
const tunnel = new WorkerTunnel(worker)
const NativeURL = URL
class StubURL extends NativeURL {
static override createObjectURL(): string { return 'blob:fixture-asset' }
}
vi.stubGlobal('URL', StubURL)
const reference = 'plugins/@fixture/ui/assets/mark-0123456789ab.svg'
const reading = tunnel.packageAssets()
expect(sent[0]?.url).toBe('http://localhost:3000/__assets__')
deliver({ t: 'res', id: sent[0]!.id, status: 200, headers: { 'content-type': 'application/x-tar' }, body: packTar({ [reference]: new TextEncoder().encode('<svg/>') }).slice().buffer })
const resolve = await reading
expect(resolve(reference)).toBe('blob:fixture-asset')
expect(resolve('plugins/other/assets/a.svg')).toBe('plugins/other/assets/a.svg')
const failing = tunnel.packageAssets()
deliver({ t: 'res', id: sent[1]!.id, status: 503, headers: { 'content-type': 'text/plain' }, body: new TextEncoder().encode('host failed').buffer })
await expect(failing).rejects.toThrow('web-preview tunnel: package assets failed with HTTP 503: host failed')
})