test(web): compare the worker Buffer with an unmodified Node reference

Vitest resolves the shim's `buffer` import to Node's built-in module, so
the differential spec patched and then compared the same implementation.
Mock node:buffer with the npm package the Worker bundle uses. Also keep the
UTF-8 encoding check symmetric, call the package methods directly, and
document that the patch applies to every Buffer user in the Worker.
This commit is contained in:
Turtle committed 2026-10-22 15:13:05 +08:00
1 parent 4b1d4c814e
commit 39d973322b
2 files changed
+30 -15

No files matched your search

@@ -3,11 +3,12 @@
* the matching `globalThis.Buffer` install. Node code treats Buffer as ambient,
* so the global must exist before any host module evaluates.
*
* Whole-buffer UTF-8 conversions, `Buffer.from(string)` and `buf.toString()`,
* use the browser's `TextEncoder` and `TextDecoder`: they apply Node's U+FFFD
* replacement rules and run several times faster than the package's JavaScript
* codec on multi-megabyte bundles and source maps. Other encodings and ranged
* `toString` calls keep the package's implementation.
* Loading this module replaces `Buffer.from`, `Buffer.prototype.toString`, and
* `toLocaleString` on the package's class, so every Buffer user in the Worker,
* including bundled third-party code, gets the same conversions. Whole-buffer
* UTF-8 conversions use `TextEncoder` and `TextDecoder` with Node's U+FFFD
* replacement and BOM retention; other encodings and ranged `toString` calls
* keep the package's implementation.
*/
import { Buffer, kMaxLength } from 'buffer'
@@ -15,24 +16,27 @@ const encoder = new TextEncoder()
// Node keeps a leading byte order mark in the decoded string.
const decoder = new TextDecoder('utf-8', { ignoreBOM: true })
const UTF8 = /^utf-?8$/i
const packageFrom = Buffer.from.bind(Buffer)
const packageFrom = Buffer.from.bind(Buffer) as (value: unknown, encodingOrOffset?: unknown, length?: unknown) => Buffer
const bufferPrototype = Buffer.prototype as Buffer
// oxlint-disable-next-line typescript/unbound-method -- Reflect.apply supplies the Buffer receiver.
// oxlint-disable-next-line typescript/unbound-method -- Called with .call and a Buffer receiver.
const packageToString = bufferPrototype.toString
/** Whether an omitted or string encoding argument names UTF-8. */
function isUtf8(encoding: unknown): boolean {
return encoding === undefined || (typeof encoding === 'string' && UTF8.test(encoding))
}
Buffer.from = function from(value: unknown, encodingOrOffset?: unknown, length?: unknown): Buffer {
if (typeof value === 'string' && (encodingOrOffset === undefined || (typeof encodingOrOffset === 'string' && UTF8.test(encodingOrOffset)))) {
if (typeof value === 'string' && isUtf8(encodingOrOffset)) {
const bytes = encoder.encode(value)
return packageFrom(bytes.buffer, bytes.byteOffset, bytes.byteLength)
}
return Reflect.apply(packageFrom, Buffer, [value, encodingOrOffset, length]) as Buffer
return packageFrom(value, encodingOrOffset, length)
} as typeof Buffer.from
bufferPrototype.toString = function toString(this: Buffer, encoding?: BufferEncoding, start?: number, end?: number): string {
if (start === undefined && end === undefined && (encoding === undefined || UTF8.test(encoding))) {
return decoder.decode(this)
}
return Reflect.apply(packageToString, this, [encoding, start, end])
if (start === undefined && end === undefined && isUtf8(encoding)) return decoder.decode(this)
return packageToString.call(this, encoding, start, end)
}
// oxlint-disable-next-line typescript/unbound-method -- Installed as a Buffer method; callers supply the receiver.
Reflect.set(bufferPrototype, 'toLocaleString', bufferPrototype.toString)
@@ -1,8 +1,14 @@
/**
* Differential check of the worker's `node:buffer` UTF-8 conversions against Node.
*/
import { Buffer as NodeBuffer } from 'node:buffer'
import { afterAll, expect, it } from 'vitest'
import { createRequire } from 'node:module'
import { afterAll, expect, it, vi } from 'vitest'
// The worker bundle resolves `buffer` to the npm package, while Vitest maps it to Node's built-in module.
// Mock that module so the shim patches the npm package and Node's own Buffer stays the unmodified reference.
const require = createRequire(import.meta.url)
const NodeBuffer = (require('node:buffer') as typeof import('node:buffer')).Buffer
vi.mock('node:buffer', () => require('buffer/') as object)
const ambient = globalThis.Buffer
const { Buffer: WorkerBuffer } = await import('@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/buffer.ts')
@@ -10,6 +16,11 @@ afterAll(() => {
Object.defineProperty(globalThis, 'Buffer', { value: ambient, writable: true, configurable: true })
})
it('tests the npm package against an unmodified Node reference', () => {
expect(WorkerBuffer).not.toBe(NodeBuffer)
expect(NodeBuffer.from('\uFEFF').toString()).toBe('\uFEFF')
})
const STRINGS = ['', 'a', '中文字符串', '\uFEFFbom', 'lone \uD800 high', 'lone \uDC00 low', '😀 pair', 'x'.repeat(100_000)]
const BYTES = [
[0xEF, 0xBB, 0xBF, 0x61],