Files
imccyu 93d2ad2d25 feat(webworker): boot the preview from the shipped profile and fetch lazy chunks on demand
The packer images the web template bundles, every optional bundle, and an installation manifest instead of a dumped composition, and the Worker boots that profile through app-boot like dsh web. Package-local lazy client chunks move into content-addressed archives that the VFS fetches on first asynchronous read; client-modules reads chunks asynchronously.

The Worker replaces node-addon-require-builtin with Node v24.18.0 internal-module interfaces and reports Node 24, so plugin metadata, the Cordis Loader, and HMR reach one cascaded loader backed by the Worker loader. Resolution failures carry Node error codes, the empty load cache lets HMR apply profile changes, and every optional bundle switches on and off live. Platform stubs throw ERR_FEATURE_UNAVAILABLE_ON_PLATFORM.
2026-10-15 11:30:45 +08:00

117 lines
5.3 KiB
TypeScript

/**
* Differential check of the worker's crypto/url/util shims against Node.
*/
import { expect, test } from 'vitest'
import { createHash as nodeCreateHash } from 'node:crypto'
import { fileURLToPath as nodeFileURLToPath, pathToFileURL as nodePathToFileURL } from 'node:url'
import { inherits as nodeInherits, isDeepStrictEqual as nodeIsDeepStrictEqual, promisify as nodePromisify } from 'node:util'
import * as shimCrypto from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/crypto.ts'
import * as shimUrl from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/url.ts'
import * as shimUtil from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/util.ts'
const compare = (label: string, actual: unknown, expected: unknown): void => {
const [shimmed, node] = [JSON.stringify(actual), JSON.stringify(expected)]
test(label, () => { expect(shimmed).toBe(node) })
}
const INPUTS = ['', 'a', 'hello world', '中文字符串', 'ÿ', 'x'.repeat(100_000)]
for (const algorithm of ['sha1', 'sha256', 'sha512']) {
for (const input of INPUTS) {
compare(
`${algorithm}(${input.slice(0, 12)}… len ${String(input.length)})`,
shimCrypto.createHash(algorithm).update(input).digest('hex'),
nodeCreateHash(algorithm).update(input).digest('hex'),
)
}
// Chained updates must equal the concatenated input.
compare(
`${algorithm} chained update`,
shimCrypto.createHash(algorithm).update('ab').update('cd').digest('hex'),
nodeCreateHash(algorithm).update('abcd').digest('hex'),
)
compare(
`${algorithm} base64`,
shimCrypto.createHash(algorithm).update('abc').digest('base64'),
nodeCreateHash(algorithm).update('abc').digest('base64'),
)
compare(
`${algorithm} bytes`,
[...shimCrypto.createHash(algorithm).update(new Uint8Array([1, 2, 3])).digest()],
[...nodeCreateHash(algorithm).update(new Uint8Array([1, 2, 3])).digest()],
)
}
const PATHS = [
'/dsh/config/cordis.yml', '/a b/c', '/中文/x.md', '/a%b', '/dsh/node_modules/@scope/pkg/lib/index.js',
]
for (const path of PATHS) {
compare(`pathToFileURL(${path})`, shimUrl.pathToFileURL(path).href, nodePathToFileURL(path).href)
compare(
`fileURLToPath(pathToFileURL(${path}))`,
shimUrl.fileURLToPath(shimUrl.pathToFileURL(path)),
nodeFileURLToPath(nodePathToFileURL(path)),
)
}
const PAIRS: [unknown, unknown][] = [
[JSON.parse('{"constructor":{"value":1}}'), JSON.parse('{"constructor":{"value":1}}')],
[JSON.parse('{"constructor":{"value":1}}'), { other: { value: 1 } }],
[JSON.parse('{"__proto__":{}}'), { other: {} }],
[{ other: {} }, JSON.parse('{"__proto__":{}}')],
[JSON.parse('{"__proto__":{}}'), JSON.parse('{"__proto__":{}}')],
[JSON.parse('{"nested":{"__proto__":{}}}'), { nested: { other: {} } }],
[1, 1], [1, '1'], [{ a: 1 }, { a: 1 }], [{ a: 1 }, { a: 2 }], [{ a: 1 }, { a: 1, b: undefined }],
[[1, 2], [1, 2]], [[1, 2], [2, 1]], [null, undefined], [{ a: { b: [1, { c: 2 }] } }, { a: { b: [1, { c: 2 }] } }],
]
for (const [left, right] of PAIRS) {
compare(
`isDeepStrictEqual(${JSON.stringify(left)}, ${JSON.stringify(right)})`,
shimUtil.isDeepStrictEqual(left, right),
nodeIsDeepStrictEqual(left, right),
)
}
const callbackStyle = (value: number, callback: (error: unknown, result?: number) => void): void => {
if (value < 0) callback(new Error('negative'))
else callback(null, value * 2)
}
const shimPromised = shimUtil.promisify(callbackStyle)
const nodePromised = nodePromisify(callbackStyle)
compare('promisify success', await shimPromised(21), await nodePromised(21))
const shimError = await shimPromised(-1).then(() => undefined, (error: unknown) => (error as Error).message)
const nodeError = await nodePromised(-1).then(() => undefined, (error: unknown) => (error as Error).message)
compare('promisify rejection', shimError, nodeError)
/** Observable result of one `inherits(ctor, superCtor)` call, or the code it refused with. */
function inheritance(inherits: (ctor: unknown, superCtor: unknown) => void, make: () => [unknown, unknown]): unknown {
const [ctor, superCtor] = make()
try {
inherits(ctor, superCtor)
} catch (error) {
return { refused: (error as { code?: unknown }).code }
}
const child = ctor as { prototype: object; super_?: unknown }
const parent = superCtor as { prototype: object }
const descriptor = Object.getOwnPropertyDescriptor(child, 'super_')
return {
chained: Object.getPrototypeOf(child.prototype) === parent.prototype,
superLinked: child.super_ === superCtor,
descriptor: { writable: descriptor?.writable, enumerable: descriptor?.enumerable, configurable: descriptor?.configurable },
}
}
const INHERITANCE_CASES: Record<string, () => [unknown, unknown]> = {
'two constructors': () => [function Child() {}, function Parent() {}],
'class parent': () => [function Child() {}, class Parent { readonly kind = 'parent' }],
'null ctor': () => [null, function Parent() {}],
'undefined superCtor': () => [function Child() {}, undefined],
'superCtor without prototype': () => [function Child() {}, () => undefined],
}
for (const [label, make] of Object.entries(INHERITANCE_CASES)) {
compare(
`inherits(${label})`,
inheritance(shimUtil.inherits, make),
inheritance(nodeInherits, make),
)
}