From 40befd8770e90ef2cb74d65bb1e2ff9911e2a2f7 Mon Sep 17 00:00:00 2001 From: jhmaster <32803471+jhmaster2000@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:17:39 -0300 Subject: [PATCH] Sync bun-polyfills branch (#4081) * bun-polyfills: initial impl. & baseline refactor * move @types/ws dep from root to /test/ * bun-types: remove ReadableStream.forEach method (this does not exist, probably added by mistake) * bun-polyfills: remove extraneous stream utils * bun-polyfills: add types syncing file * bun-polyfills: re-arrange global polyfills * bun-polyfills: fix FileBlob streams types again * bun-polyfills: sync all of @types/node * bun-polyfills: typeguard all current polyfills * bun-polyfills: fix import paths * bun-polyfills: switch to wasm impl. of farmhash * bun-polyfills: support default import of bun obj * bun-polyfills: transpiler placeholder file * bun-polyfills: loaderless import.meta polyfill * bun-polyfills: refactor import.meta polyfill * bun-polyfills: repl entrypoint & todo list index * bun-types: Add null to return type of Bun.which * bun-types: match Bun.sha with Bun.hash.SHA512_256 * bun-polyfills: new "repl" package.json script * bun-polyfills: full refactor of toplevel hashes * bun-polyfills: these are fixed * bun-types: NODE_ENV is optional * bun-polyfills: fix Bun.env types * bun-types+polyfills: fix HeapSnapshot.version type * bun-polyfills: fix some web streams type conflicts * bun-polyfills: update internal FileBlob.slice * bun-polyfills: fix subproc stdin conversions * bun-polyfills: better internal fileblob types * bun-polyfills: try to sync global performance type * bun-polyfills: working zig wasm polyfills setup * bun-polyfills: update scripts * bun-polyfills: fix wasm file location resolution * bun-polyfills: goodbye farmhash (replaced by zig) * bun-polyfills: move all Bun.hash polyfills to zig * bun-polyfills: reimpl. seeding of seeded hashes * bun-polyfills: impl. undocumented murmur32v2 * bun-polyfills: switch zighash from jsdoc to .d.ts * bun-types: partial fix of Hash types * bun-polyfills: documented Hash.murmur32v2 * bun-polyfills: misc updates * bun-polyfills: enable sourcemaps * bun-polyfills: handle empty inputs to hash funcs * bun-types: narrow down hash func types * bun-polyfills: remove unnecessary bigint casts * bun-polyfills: impl. Bun.isMainThread * bun-polyfills: impl. Bun.sleep and fix sleepSync * bun-polyfills: impl. indexOfLine * bun-polyfills: impl. Bun.peek.status * bun-types: fix hashing test --------- Co-authored-by: Jarred Sumner --- bun.lockb | Bin 72575 -> 72574 bytes packages/bun-polyfills/.gitignore | 172 ++++++ packages/bun-polyfills/README.md | 9 + packages/bun-polyfills/bun.lockb | Bin 0 -> 27871 bytes packages/bun-polyfills/lib/zighash/index.mjs | 95 ++++ .../bun-polyfills/lib/zighash/package.json | 10 + .../bun-polyfills/lib/zighash/src/main.zig | 58 +++ packages/bun-polyfills/lib/zighash/types.d.ts | 25 + .../bun-polyfills/lib/zighash/zighash.wasm | Bin 0 -> 14943 bytes packages/bun-polyfills/package.json | 27 + packages/bun-polyfills/src/global/console.ts | 31 ++ .../bun-polyfills/src/global/importmeta.ts | 34 ++ packages/bun-polyfills/src/global/index.ts | 45 ++ packages/bun-polyfills/src/global/process.ts | 19 + packages/bun-polyfills/src/index.ts | 3 + packages/bun-polyfills/src/modules/bun.ts | 489 ++++++++++++++++++ .../src/modules/bun/arraybuffersink.ts | 67 +++ packages/bun-polyfills/src/modules/bun/dns.ts | 21 + .../bun-polyfills/src/modules/bun/fileblob.ts | 195 +++++++ .../bun-polyfills/src/modules/bun/filesink.ts | 87 ++++ .../bun-polyfills/src/modules/bun/hashes.ts | 185 +++++++ .../src/modules/bun/transpiler.ts | 103 ++++ packages/bun-polyfills/src/repl.ts | 29 ++ packages/bun-polyfills/src/types/helpers.d.ts | 13 + packages/bun-polyfills/src/types/md4.d.ts | 72 +++ packages/bun-polyfills/src/types/sync.d.ts | 15 + .../src/types/v8heapsnapshot.d.ts | 24 + packages/bun-polyfills/src/utils/errors.ts | 221 ++++++++ packages/bun-polyfills/src/utils/misc.ts | 36 ++ packages/bun-polyfills/tsconfig.json | 19 + packages/bun-types/bun.d.ts | 43 +- packages/bun-types/globals.d.ts | 4 - packages/bun-types/tests/hashing.test-d.ts | 2 +- test/bun.lockb | Bin 149736 -> 149700 bytes 34 files changed, 2127 insertions(+), 26 deletions(-) create mode 100644 packages/bun-polyfills/.gitignore create mode 100644 packages/bun-polyfills/README.md create mode 100755 packages/bun-polyfills/bun.lockb create mode 100644 packages/bun-polyfills/lib/zighash/index.mjs create mode 100644 packages/bun-polyfills/lib/zighash/package.json create mode 100644 packages/bun-polyfills/lib/zighash/src/main.zig create mode 100644 packages/bun-polyfills/lib/zighash/types.d.ts create mode 100755 packages/bun-polyfills/lib/zighash/zighash.wasm create mode 100644 packages/bun-polyfills/package.json create mode 100644 packages/bun-polyfills/src/global/console.ts create mode 100644 packages/bun-polyfills/src/global/importmeta.ts create mode 100644 packages/bun-polyfills/src/global/index.ts create mode 100644 packages/bun-polyfills/src/global/process.ts create mode 100644 packages/bun-polyfills/src/index.ts create mode 100644 packages/bun-polyfills/src/modules/bun.ts create mode 100644 packages/bun-polyfills/src/modules/bun/arraybuffersink.ts create mode 100644 packages/bun-polyfills/src/modules/bun/dns.ts create mode 100644 packages/bun-polyfills/src/modules/bun/fileblob.ts create mode 100644 packages/bun-polyfills/src/modules/bun/filesink.ts create mode 100644 packages/bun-polyfills/src/modules/bun/hashes.ts create mode 100644 packages/bun-polyfills/src/modules/bun/transpiler.ts create mode 100644 packages/bun-polyfills/src/repl.ts create mode 100644 packages/bun-polyfills/src/types/helpers.d.ts create mode 100644 packages/bun-polyfills/src/types/md4.d.ts create mode 100644 packages/bun-polyfills/src/types/sync.d.ts create mode 100644 packages/bun-polyfills/src/types/v8heapsnapshot.d.ts create mode 100644 packages/bun-polyfills/src/utils/errors.ts create mode 100644 packages/bun-polyfills/src/utils/misc.ts create mode 100644 packages/bun-polyfills/tsconfig.json diff --git a/bun.lockb b/bun.lockb index 76f7802823d59a728f2010eda04c09028109ec02..da36ca965f28c0826ff6e0381edb6f92712175c0 100755 GIT binary patch delta 9893 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 QEqzN!D@#mV=p6cg0GPljp#T5? diff --git a/packages/bun-polyfills/.gitignore b/packages/bun-polyfills/.gitignore new file mode 100644 index 0000000000..7f4433ab4c --- /dev/null +++ b/packages/bun-polyfills/.gitignore @@ -0,0 +1,172 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* + +# Misc + +_* +.old +.vscode +!build diff --git a/packages/bun-polyfills/README.md b/packages/bun-polyfills/README.md new file mode 100644 index 0000000000..5d6a70042a --- /dev/null +++ b/packages/bun-polyfills/README.md @@ -0,0 +1,9 @@ +# Bun APIs Polyfills + +Polyfills for Bun's JavaScript runtime APIs for use in environments outside of Bun, such as Node.js or the browser¹. + +¹ **Note:** The current priority is Node.js, browser support will vary per polyfill. + +## Usage + +This is currently a work in progress and is not ready for general use. diff --git a/packages/bun-polyfills/bun.lockb b/packages/bun-polyfills/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..fdc201d28173427807d7b03ca672d471ba29f33f GIT binary patch literal 27871 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 dP}FCK?g8=j3~6QQh literal 0 HcmV?d00001 diff --git a/packages/bun-polyfills/lib/zighash/index.mjs b/packages/bun-polyfills/lib/zighash/index.mjs new file mode 100644 index 0000000000..f92ffb6d07 --- /dev/null +++ b/packages/bun-polyfills/lib/zighash/index.mjs @@ -0,0 +1,95 @@ +// @ts-check +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const { instance } = /** @type {ZighashInstance} */( + await WebAssembly.instantiate( + fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'zighash.wasm')), + { + env: { + /** @param {any} x */ + print(x) { console.log(x); }, + }, + } + ) +); +const exports = instance.exports; +const mem = exports.memory; +const memview = { + get u8() { return new Uint8Array(mem.buffer); }, + get u16() { return new Uint16Array(mem.buffer); }, + get u32() { return new Uint32Array(mem.buffer); }, + get u64() { return new BigUint64Array(mem.buffer); }, + get i8() { return new Int8Array(mem.buffer); }, + get i16() { return new Int16Array(mem.buffer); }, + get i32() { return new Int32Array(mem.buffer); }, + get i64() { return new BigInt64Array(mem.buffer); }, + get f32() { return new Float32Array(mem.buffer); }, + get f64() { return new Float64Array(mem.buffer); }, +}; + +const nullptr = { ptr: -1, size: 0 }; +const encoder = new TextEncoder(); + +const allocBuffer = ( + /** @type {ArrayBufferView | ArrayBuffer | SharedArrayBuffer} */ buf, + /** @type {boolean=} */ nullTerminate = false, +) => { + const size = buf.byteLength + +nullTerminate; + if (size === 0) return nullptr; + const ptr = exports.alloc(size); + if (ptr === -1) throw new Error('WASM memory allocation failed'); + const u8heap = memview.u8; + u8heap.set(new Uint8Array(ArrayBuffer.isView(buf) ? buf.buffer : buf), ptr); + if (nullTerminate) u8heap[ptr + buf.byteLength] = 0; + return { ptr, size }; +}; +const allocString = ( + /** @type {string} */ str, + /** @type {boolean=} */ nullTerminate = true, +) => { + const strbuf = encoder.encode(str); + return allocBuffer(strbuf, nullTerminate); +}; + +/** @type {JSSeededHash64Function} */ +export function wyhash(input = '', seed = 0n) { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return BigInt.asUintN(64, exports.wyhash(ptr, size, seed)); +} +/** @type {JSHash32Function} */ +export function adler32(input = '') { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return exports.adler32(ptr, size) >>> 0; +} +/** @type {JSHash32Function} */ +export function crc32(input = '') { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return exports.crc32(ptr, size) >>> 0; +} +/** @type {JSHash32Function} */ +export function cityhash32(input = '') { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return exports.cityhash32(ptr, size) >>> 0; +} +/** @type {JSSeededHash64Function} */ +export function cityhash64(input = '', seed = 0n) { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return BigInt.asUintN(64, exports.cityhash64(ptr, size, seed)); +} +/** @type {JSSeededHash32Function} */ +export function murmur32v3(input = '', seed = 0) { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return exports.murmur32v3(ptr, size, seed); //! Bun doesn't unsigned-cast this one, likely unintended but for now we'll do the same +} +/** @type {JSSeededHash32Function} */ +export function murmur32v2(input = '', seed = 0) { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return exports.murmur32v2(ptr, size, seed); //! Bun doesn't unsigned-cast this one, likely unintended but for now we'll do the same +} +/** @type {JSSeededHash64Function} */ +export function murmur64v2(input = '', seed = 0n) { + const { ptr, size } = typeof input === 'string' ? allocString(input, false) : allocBuffer(input); + return BigInt.asUintN(64, exports.murmur64v2(ptr, size, seed)); +} diff --git a/packages/bun-polyfills/lib/zighash/package.json b/packages/bun-polyfills/lib/zighash/package.json new file mode 100644 index 0000000000..cda5ce887f --- /dev/null +++ b/packages/bun-polyfills/lib/zighash/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "type": "module", + "name": "zighash-wasm", + "module": "index.mjs", + "scripts": { + "build": "bun run clean && zig build-lib src/main.zig --name zighash -target wasm32-freestanding -dynamic -rdynamic -OReleaseSmall", + "clean": "rm -f *.wasm *.o" + } +} diff --git a/packages/bun-polyfills/lib/zighash/src/main.zig b/packages/bun-polyfills/lib/zighash/src/main.zig new file mode 100644 index 0000000000..820557b438 --- /dev/null +++ b/packages/bun-polyfills/lib/zighash/src/main.zig @@ -0,0 +1,58 @@ +const std = @import("std"); + +extern fn print(*const u8) void; + +comptime { + std.debug.assert(@alignOf(u16) >= 2); + std.debug.assert(@alignOf(u32) >= 4); + std.debug.assert(@alignOf(u64) >= 8); + std.debug.assert(@alignOf(i16) >= 2); + std.debug.assert(@alignOf(i32) >= 4); + std.debug.assert(@alignOf(i64) >= 8); +} + +export fn alloc(size: u32) [*]const u8 { + const slice = std.heap.wasm_allocator.alloc(u8, size) catch @panic("wasm failed to allocate memory"); + return slice.ptr; +} + +export fn wyhash(input_ptr: [*]const u8, input_size: u32, seed: u64) u64 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Wyhash.hash(seed, input); +} +export fn adler32(input_ptr: [*]const u8, input_size: u32) u32 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Adler32.hash(input); +} +export fn crc32(input_ptr: [*]const u8, input_size: u32) u32 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Crc32.hash(input); +} +export fn cityhash32(input_ptr: [*]const u8, input_size: u32) u32 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.CityHash32.hash(input); +} +export fn cityhash64(input_ptr: [*]const u8, input_size: u32, seed: u64) u64 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.CityHash64.hashWithSeed(input, seed); +} +export fn murmur32v3(input_ptr: [*]const u8, input_size: u32, seed: u32) u32 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Murmur3_32.hashWithSeed(input, seed); +} +export fn murmur32v2(input_ptr: [*]const u8, input_size: u32, seed: u32) u32 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Murmur2_32.hashWithSeed(input, seed); +} +export fn murmur64v2(input_ptr: [*]const u8, input_size: u32, seed: u64) u64 { + const input: []const u8 = input_ptr[0..input_size]; + defer std.heap.wasm_allocator.free(input); + return std.hash.Murmur2_64.hashWithSeed(input, seed); +} diff --git a/packages/bun-polyfills/lib/zighash/types.d.ts b/packages/bun-polyfills/lib/zighash/types.d.ts new file mode 100644 index 0000000000..f0704ac943 --- /dev/null +++ b/packages/bun-polyfills/lib/zighash/types.d.ts @@ -0,0 +1,25 @@ +type WasmHash32Function = (input_ptr: number, input_size: number) => number; +type WasmHash64Function = (input_ptr: number, input_size: number) => bigint; +type WasmSeededHash32Function = (input_ptr: number, input_size: number, seed: number) => number; +type WasmSeededHash64Function = (input_ptr: number, input_size: number, seed: bigint) => bigint; +type JSHash32Function = (input: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => number; +type JSHash64Function = (input: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => bigint; +type JSSeededHash32Function = (input: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: number) => number; +type JSSeededHash64Function = (input: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: bigint) => bigint; + +type ZighashInstance = WebAssembly.WebAssemblyInstantiatedSource & { + instance: { + exports: { + memory: WebAssembly.Memory, + alloc(size: number): number, + wyhash: WasmSeededHash64Function, + adler32: WasmHash32Function, + crc32: WasmHash32Function, + cityhash32: WasmHash32Function, + cityhash64: WasmSeededHash64Function, + murmur32v3: WasmSeededHash32Function, + murmur32v2: WasmSeededHash32Function, + murmur64v2: WasmSeededHash64Function, + }; + }; +} diff --git a/packages/bun-polyfills/lib/zighash/zighash.wasm b/packages/bun-polyfills/lib/zighash/zighash.wasm new file mode 100755 index 0000000000000000000000000000000000000000..1c8603f324061aca803053c3e6d14269e81dc9a2 GIT binary patch literal 14943 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 literal 0 HcmV?d00001 diff --git a/packages/bun-polyfills/package.json b/packages/bun-polyfills/package.json new file mode 100644 index 0000000000..e1852eddc1 --- /dev/null +++ b/packages/bun-polyfills/package.json @@ -0,0 +1,27 @@ +{ + "type": "module", + "name": "bun-polyfills", + "module": "src/index.ts", + "devDependencies": { + "@types/node": "^20.4.5", + "@types/which": "^3.0.0", + "bun-types": "^0.7.0", + "copyfiles": "^2.4.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "scripts": { + "node": "node --enable-source-maps --import ./dist/src/repl.js", + "clean": "rm -rf dist", + "build": "bun run clean && bunx tsc && bunx copyfiles \"./**/*.wasm\" dist", + "build/wasm": "bun run build/zighash", + "build/zighash": "cd lib/zighash && bun run build && cd ../.." + }, + "dependencies": { + "js-md4": "^0.3.2", + "open-editor": "^4.0.0", + "supports-color": "^9.4.0", + "which": "^3.0.1" + } +} diff --git a/packages/bun-polyfills/src/global/console.ts b/packages/bun-polyfills/src/global/console.ts new file mode 100644 index 0000000000..4576f5a703 --- /dev/null +++ b/packages/bun-polyfills/src/global/console.ts @@ -0,0 +1,31 @@ +//? Implements: Red colored console.error from Bun +//if (Bun.enableANSIColors) { +// const RED = '\x1B[31m' as const; +// const RESET = '\x1B[0m' as const; +// const consoleError = console.error; +// console.error = (...args) => { +// if (typeof args[0] === 'string') args[0] = RED + args[0]; +// consoleError(...args, RESET); +// }; +//} + +//? Implements: for await (const line of console) { ... } +console[Symbol.asyncIterator] = async function* () { + while (true) yield await new Promise(resolve => { + process.stdin.on('data', (data: Buffer | string) => { + const str = data.toString('utf-8').replaceAll(/[\r\n]+/g, ''); + resolve(str); + }); + }); +} satisfies Console[typeof Symbol.asyncIterator]; + +//? Implements: Bun-exclusive console function +console.write = ((...data) => { + const str = data.map(val => { + if (val instanceof ArrayBuffer) val = new TextDecoder('utf-8').decode(val); + else if (typeof val === 'object') val = new TextDecoder('utf-8').decode(val.buffer); + return val; + }).join(''); + process.stdout.write(str); + return new TextEncoder('utf-8').encode(str).byteLength; +}) satisfies Console['write']; diff --git a/packages/bun-polyfills/src/global/importmeta.ts b/packages/bun-polyfills/src/global/importmeta.ts new file mode 100644 index 0000000000..ea8acad805 --- /dev/null +++ b/packages/bun-polyfills/src/global/importmeta.ts @@ -0,0 +1,34 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; + +// Without an ESM loader, this polyfill is impossible to apply automatically, +// due to the per-module nature of import.meta. In order to use this polyfill, +// you must import it in every module that uses import.meta, and call it with +// the import.meta object as the argument. When the polyfills are integrated +// with bun build, this could be done automatically by the build process at +// the top of every module file bundled. + +export default function polyfillImportMeta(metaIn: ImportMeta) { + const require2 = createRequire(metaIn.url); + const metapath = fileURLToPath(metaIn.url); + const meta: ImportMeta = { + url: metaIn.url, + main: metapath === process.argv[1], + path: metapath, + dir: path.dirname(metapath), + file: path.basename(metapath), + require: require2, + async resolve(id: string, parent?: string) { + return this.resolveSync(id, parent); + }, + resolveSync(id: string, parent?: string) { + return require2.resolve(id, { + paths: typeof parent === 'string' ? [ + path.resolve(parent.startsWith('file://') ? fileURLToPath(parent) : parent, '..') + ] : undefined, + }); + }, + }; + Object.assign(metaIn, meta); +} diff --git a/packages/bun-polyfills/src/global/index.ts b/packages/bun-polyfills/src/global/index.ts new file mode 100644 index 0000000000..661a2c3c16 --- /dev/null +++ b/packages/bun-polyfills/src/global/index.ts @@ -0,0 +1,45 @@ +import { version } from '../modules/bun.js'; +import './console.js'; +import './process.js'; +import os from 'node:os'; + +//? NodeJS Blob doesn't implement Blob.json(), so we need to polyfill it. +Blob.prototype.json = async function json(this: Blob): Promise { + try { + return JSON.parse(await this.text()) as T; + } catch (err) { + Error.captureStackTrace(err as Error, json); + throw err; + } +}; + +//? navigator global object polyfill +Reflect.set(globalThis, 'navigator', { + userAgent: `Bun/${version}`, + hardwareConcurrency: os.cpus().length, +}); + +//? method only available in Bun +// this isn't quite accurate, but it shouldn't break anything and is currently here just for matching bun and node types +const ReadableStreamDefaultReaderPrototype = Object.getPrototypeOf(new ReadableStream().getReader()); +Reflect.set( + ReadableStreamDefaultReaderPrototype, 'readMany', + function readMany(this: ReadableStreamDefaultReader): Promise> { + return new Promise((resolve, reject) => { + const result: ReadableStreamDefaultReadManyResult = { + value: [], + size: 0, + done: true + }; + this.read().then(({ done, value }) => { + if (done) resolve(result); + else { + result.value.push(value); + result.size = value.length; + result.done = false; + resolve(result); + } + }, reject); + }); + } +); diff --git a/packages/bun-polyfills/src/global/process.ts b/packages/bun-polyfills/src/global/process.ts new file mode 100644 index 0000000000..efc7d5cdd1 --- /dev/null +++ b/packages/bun-polyfills/src/global/process.ts @@ -0,0 +1,19 @@ + +if (typeof process === 'object' && process !== null) { + // process polyfills (node-only) + Reflect.set(process, 'isBun', 1 satisfies Process['isBun']); + Reflect.set(process, 'browser', false satisfies Process['browser']); + + const NULL_VERSION = '0'.repeat(39) + '1'; + process.versions.bun = '0.7.1' satisfies Process['versions'][string]; // TODO: This can probably be fetched from somewhere in the repo + process.versions.webkit = NULL_VERSION satisfies Process['versions'][string]; + process.versions.mimalloc = NULL_VERSION satisfies Process['versions'][string]; + process.versions.libarchive = NULL_VERSION satisfies Process['versions'][string]; + process.versions.picohttpparser = NULL_VERSION satisfies Process['versions'][string]; + process.versions.boringssl = NULL_VERSION satisfies Process['versions'][string]; + process.versions.zig = '0.10.0' satisfies Process['versions'][string]; + Reflect.set(process, 'revision', NULL_VERSION satisfies Process['revision']); + + // Doesn't work on Windows sadly + //Object.defineProperty(process, 'execPath', { value: path.resolve(root, 'cli.js') }); +} diff --git a/packages/bun-polyfills/src/index.ts b/packages/bun-polyfills/src/index.ts new file mode 100644 index 0000000000..b48a9b7722 --- /dev/null +++ b/packages/bun-polyfills/src/index.ts @@ -0,0 +1,3 @@ +export * from './modules/bun.js'; +export * as default from './modules/bun.js'; +import './global/index.js'; diff --git a/packages/bun-polyfills/src/modules/bun.ts b/packages/bun-polyfills/src/modules/bun.ts new file mode 100644 index 0000000000..ce0f6f6bed --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun.ts @@ -0,0 +1,489 @@ +import type { + BunPlugin, PluginConstraints, PluginBuilder, OnLoadCallback, OnResolveCallback, HeapSnapshot, + EditorOptions, SpawnOptions, Subprocess, SyncSubprocess, FileBlob as BunFileBlob, ArrayBufferView, Hash +} from 'bun'; +import { TextDecoderStream } from 'node:stream/web'; +import { NotImplementedError, type SystemError } from '../utils/errors.js'; +import { streamToBuffer, isArrayBufferView, isFileBlob, isOptions } from '../utils/misc.js'; +import dnsPolyfill from './bun/dns.js'; +import { FileSink } from './bun/filesink.js'; +import { + bunHash, bunHashProto, + MD4 as MD4Polyfill, MD5 as MD5Polyfill, + SHA1 as SHA1Polyfill, SHA224 as SHA224Polyfill, + SHA256 as SHA256Polyfill, SHA384 as SHA384Polyfill, + SHA512 as SHA512Polyfill, SHA512_256 as SHA512_256Polyfill +} from './bun/hashes.js'; +import { ArrayBufferSink as ArrayBufferSinkPolyfill } from './bun/arraybuffersink.js'; +import { FileBlob, NodeJSStreamFileBlob } from './bun/fileblob.js'; +import fs from 'node:fs'; +import v8 from 'node:v8'; +import path from 'node:path'; +import util from 'node:util'; +import zlib from 'node:zlib'; +import streams from 'node:stream'; +import workers from 'node:worker_threads'; +import chp, { type ChildProcess, type StdioOptions, type SpawnSyncReturns } from 'node:child_process'; +import { fileURLToPath as fileURLToPathNode, pathToFileURL as pathToFileURLNode } from 'node:url'; +import npm_which from 'which'; +import openEditor from 'open-editor'; + +export const main = path.resolve(process.cwd(), process.argv[1] ?? 'repl') satisfies typeof Bun.main; + +export const version = '0.7.1' satisfies typeof Bun.version; // TODO: This can probably be fetched from somewhere in the repo +export const revision = '0'.repeat(39) + '1' satisfies typeof Bun.revision; +//getter(bun, 'cwd', proc.cwd); //! Can't named export a getter +export const origin = '' satisfies typeof Bun.origin; +// @ts-expect-error --- +export const stdin = new NodeJSStreamFileBlob(process.stdin) satisfies typeof Bun.stdin; +// @ts-expect-error --- +export const stdout = new NodeJSStreamFileBlob(process.stdout) satisfies typeof Bun.stdout; +// @ts-expect-error --- +export const stderr = new NodeJSStreamFileBlob(process.stderr) satisfies typeof Bun.stderr; +export const argv = [process.argv0, ...process.execArgv, ...process.argv.slice(1)] satisfies typeof Bun.argv; +export const env = process.env satisfies typeof Bun.env; +Object.setPrototypeOf(env, { + toJSON(this: typeof env) { return { ...this }; } +}); +// @ts-expect-error supports-color types are unbelievably bad +export const enableANSIColors = (await import('supports-color')).createSupportsColor().hasBasic satisfies typeof Bun.enableANSIColors; + +export const hash = bunHash satisfies typeof Bun.hash; +Object.setPrototypeOf(hash, bunHashProto satisfies Hash); + +export const unsafe = { + gcAggressionLevel: () => 0, //! no-op + arrayBufferToString: (buf) => new TextDecoder().decode(buf), + segfault: () => { + const segfault = new Error(); + segfault.name = 'SegfaultTest'; + segfault.message = ''; + console.error(segfault); + process.exit(1); + } +} satisfies typeof Bun['unsafe']; + +export const SHA1 = SHA1Polyfill satisfies typeof Bun.SHA1; +export const MD5 = MD5Polyfill satisfies typeof Bun.MD5; +export const MD4 = MD4Polyfill satisfies typeof Bun.MD4; +export const SHA224 = SHA224Polyfill satisfies typeof Bun.SHA224; +export const SHA512 = SHA512Polyfill satisfies typeof Bun.SHA512; +export const SHA384 = SHA384Polyfill satisfies typeof Bun.SHA384; +export const SHA256 = SHA256Polyfill satisfies typeof Bun.SHA256; +export const SHA512_256 = SHA512_256Polyfill satisfies typeof Bun.SHA512_256; + +export const indexOfLine = ((data, offset) => { + if (data instanceof ArrayBuffer || data instanceof SharedArrayBuffer) data = new Uint8Array(data); + if (data instanceof DataView || !(data instanceof Uint8Array)) data = new Uint8Array(data.buffer); + return data.indexOf(10, offset); +}) satisfies typeof Bun.indexOfLine; + +const peek_ = function peek(promise: Parameters[0]) { + throw new NotImplementedError('Bun.peek', peek); +}; +peek_.status = (promise => { + return util.inspect(promise).includes('') ? 'pending' + : util.inspect(promise).includes('') ? 'rejected' : 'fulfilled'; +}) satisfies typeof Bun.peek.status; +export const peek = peek_ satisfies typeof Bun.peek; + +export const sleep = (ms => { + return new Promise(r => setTimeout(r, ms instanceof Date ? ms.valueOf() - Date.now() : ms)); +}) satisfies typeof Bun.sleep; +export const sleepSync = (ms => { + if (ms < 0) throw new TypeError('argument to sleepSync must not be negative'); + Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); +}) satisfies typeof Bun.sleepSync; + +//? This is not 1:1 matching, but no one should be relying on the exact output of this function anyway. +//? To quote Node's inspect itself: "The output of util.inspect() may change at any time and should not be depended upon programmatically." +//? Of course in Node's case some didn't listen and relied on the output of util.inspect() anyway, but hopefully this won't happen with this one. +export const inspect = ((arg: any): string => util.inspect(arg, { + breakLength: Infinity, + colors: false, + compact: true, + customInspect: false, + depth: Infinity, + getters: true, + maxArrayLength: Infinity, + maxStringLength: Infinity, + showHidden: false, + showProxy: false, + sorted: false +})) satisfies typeof Bun.inspect; + +export const resolveSync = ((id: string, parent: string) => import.meta.resolveSync(id, parent)) satisfies typeof Bun.resolveSync; +export const resolve = (async (id: string, parent: string) => import.meta.resolve!(id, parent)) satisfies typeof Bun.resolve; + +//? Yes, this is faster than new Uint8Array(Buffer.allocUnsafe(size).buffer) by about 2.5x in Node.js +export const allocUnsafe = ((size: number) => new Uint8Array(size)) satisfies typeof Bun.allocUnsafe; + +export const generateHeapSnapshot = (async (): Promise => { + process.emitWarning('The polyfill for Bun.generateHeapShot is asynchronous, unlike the original which is synchronous.', { + type: 'BunPolyfillWarning', + code: 'BUN_POLYFILLS_ASYNC_GENERATE_HEAP_SNAPSHOT', + detail: 'This is due to v8.getHeapSnapshot() returning a stream in Node.js. This is not a bug, but a limitation of the polyfill.' + }); + const raw = (await streamToBuffer(v8.getHeapSnapshot())).toString('utf8'); + const json = JSON.parse(raw) as V8HeapSnapshot; + return { + version: 2, + type: 'Inspector', + nodes: json.nodes, + edges: json.edges, + edgeTypes: json.snapshot.meta.edge_types.flat(), + edgeNames: json.snapshot.meta.edge_fields.flat(), + nodeClassNames: json.snapshot.meta.node_types.flat(), + }; + // @ts-expect-error Refer to the above emitWarning call +}) satisfies typeof Bun.generateHeapSnapshot; + +//! This is a no-op in Node.js, as there is no way to shrink the V8 heap from JS as far as I know. +export const shrink = (() => void 0) satisfies typeof Bun.shrink; + +export const openInEditor = ((file: string, opts?: EditorOptions) => { + const target = [{ file: path.resolve(process.cwd(), file), line: opts?.line, column: opts?.column }] as const; + if (opts?.editor) openEditor(target, opts); + else openEditor(target, { editor: process.env.TERM_PROGRAM ?? process.env.VISUAL ?? process.env.EDITOR ?? 'vscode' }); +}) satisfies typeof Bun.openInEditor; + +export const serve = (() => { throw new NotImplementedError('Bun.serve', serve); }) satisfies typeof Bun.serve; + +export const file = ((path: string | URL | Uint8Array | ArrayBufferLike | number, options?: BlobPropertyBag): BunFileBlob => { + if (typeof path === 'object') throw new NotImplementedError('Bun.file with typed array', file); + return new FileBlob(path, options); +}) satisfies typeof Bun.file; + +export const write = (async (dest: BunFileBlob | PathLike, input: string | Blob | TypedArray | ArrayBufferLike | BlobPart[] | Response | BunFileBlob): ReturnType => { + if (!isFileBlob(dest)) { + let fd: number; + if (dest instanceof ArrayBuffer || dest instanceof SharedArrayBuffer) fd = fs.openSync(Buffer.from(dest), 'w'); + // bun-types thought it'd be funny to make their own URL definition which doesnt match with the correct URL definition... + else if (typeof dest === 'string' || dest instanceof URL) fd = fs.openSync(dest as import('url').URL, 'w'); + else fd = fs.openSync(Buffer.from(dest.buffer), 'w'); + + if (input instanceof Response || input instanceof Blob) { + const data = await input.text(); + return new Promise((resolve, reject) => { + fs.write(fd, data, (err, written) => err ? reject(err) : resolve(written)); + }); + } + if (Array.isArray(input)) { + const data = await new Blob(input).text(); + return new Promise((resolve, reject) => { + fs.write(fd, data, (err, written) => err ? reject(err) : resolve(written)); + }); + } + return new Promise((resolve, reject) => { + if (typeof input === 'string') return fs.write(fd, input, (err, written) => err ? reject(err) : resolve(written)); + if (input instanceof Uint8Array) return fs.write(fd, input, (err, written) => err ? reject(err) : resolve(written)); + if (input instanceof ArrayBuffer) return fs.write(fd, new Uint8Array(input), (err, written) => err ? reject(err) : resolve(written)); + if (input instanceof SharedArrayBuffer) return fs.write(fd, new Uint8Array(input), (err, written) => err ? reject(err) : resolve(written)); + return write(dest, String(input)); // if all else fails, it seems Bun tries to convert to string and write that. + }); + } else { + const writer = dest.writer(); + if (Array.isArray(input)) input = new Blob(input); + if (input instanceof Blob || input instanceof Response) return writer.write(await input.arrayBuffer()); + if (input instanceof ArrayBuffer || input instanceof SharedArrayBuffer || ArrayBuffer.isView(input)) return writer.write(input); + if (typeof input === 'string') return writer.write(input); + else return write(dest, String(input)); // if all else fails, it seems Bun tries to convert to string and write that. + } +}) satisfies typeof Bun.write; + +export const sha = SHA512_256.hash satisfies typeof Bun.sha; + +export const nanoseconds = (() => Math.trunc(performance.now() * 1000000)) satisfies typeof Bun.nanoseconds; + +//? This just prints out some debug stuff in console, and as the name implies no one should be using it. +//? But, just in case someone does, we'll make it a no-op function so at least the program doesn't crash trying to run the function. +export const DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump = (() => { + console.warn('DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump called.'); +}) satisfies unknown; /* undocumented */ + +export const gzipSync = zlib.gzipSync satisfies typeof Bun.gzipSync; +export const deflateSync = zlib.deflateSync satisfies typeof Bun.deflateSync; +export const gunzipSync = zlib.gunzipSync satisfies typeof Bun.gunzipSync; +export const inflateSync = zlib.inflateSync satisfies typeof Bun.inflateSync; + +export const which = ((cmd: string, options) => { + const opts: npm_which.Options = { all: false, nothrow: true }; + if (options?.PATH) opts.path = options.PATH; + const result = npm_which.sync(cmd, opts) as string | null; + if (!result || !options?.cwd) return result; + if (path.normalize(result).includes(path.normalize(options.cwd))) return result; + else return null; +}) satisfies typeof Bun.which; + +export const spawn = ((...args) => { + let cmd: string; + let argv: string[]; + let opts: SpawnOptions.OptionsObject; + + if (args[0] instanceof Array) { + cmd = args[0][0]; + argv = args[0].slice(1); + opts = isOptions(args[1]) ? args[1] : {}; + } else { + cmd = args[0].cmd[0]; + argv = args[0].cmd.slice(1); + opts = args[0]; + Reflect.deleteProperty(opts, 'cmd'); + } + + let stdio: StdioOptions = []; + opts.stdio ??= [undefined, undefined, undefined]; + if (opts.stdin) opts.stdio[0] = opts.stdin; + if (opts.stdout) opts.stdio[1] = opts.stdout; + if (opts.stderr) opts.stdio[2] = opts.stderr; + for (let i = 1; i < 3; i++) { // this intentionally skips stdin + let std = opts.stdio[i]; + if (isArrayBufferView(std)) stdio[i] = streams.Readable.fromWeb(new Blob([std]).stream()); + else if (std instanceof Blob || isFileBlob(std)) stdio[i] = streams.Readable.fromWeb(std.stream()); + else if (std instanceof ReadableStream) stdio[i] = streams.Readable.fromWeb(std); + else if (std instanceof Response || std instanceof Request) stdio[i] = streams.Readable.fromWeb(std.body!); + else stdio[i] = std; + } + let stdinSrc: typeof opts.stdio[0] = null; + if (opts.stdio[0] && typeof opts.stdio[0] !== 'string') { + stdinSrc = opts.stdio[0]; + stdio[0] = 'pipe'; + } + + const subp = chp.spawn(cmd, argv, { + cwd: opts.cwd ?? process.cwd(), + // why is this set to (string | number) on env values... + env: { ...(opts.env as Record ?? process.env) }, + stdio + }) as unknown as Subprocess; + const subpAsNode = subp as unknown as ChildProcess; + const stdstreams = [subpAsNode.stdin, subpAsNode.stdout, subpAsNode.stderr] as const; + if (subpAsNode.stdout) { + const rstream = streams.Readable.toWeb(subpAsNode.stdout) as ReadableStream; + Reflect.set(rstream, 'destroy', function (this: ReadableStream, err?: Error) { + void (err ? this.cancel(String(err)) : this.cancel()).catch(() => { /* if it fails its already closed */ }); + return this; + }); + (>subp).stdout = rstream; + } + if (subpAsNode.stderr) { + const rstream = streams.Readable.toWeb(subpAsNode.stderr) as ReadableStream; + Reflect.set(rstream, 'destroy', function (this: ReadableStream, err?: Error) { + void (err ? this.cancel(String(err)) : this.cancel()).catch(() => { /* if it fails its already closed */ }); + return this; + }); + (>subp).stderr = rstream; + } + let internalStdinStream: streams.Writable; + if (subpAsNode.stdin) { + const wstream = subpAsNode.stdin; + Reflect.set(wstream, 'destroy', function (this: NodeJS.WritableStream, err?: Error) { + void this.end(); /* if it fails its already closed */ + return this; + }); + internalStdinStream = wstream; + (>subp).stdin = new FileSink(wstream); + + } + Object.defineProperty(subp, 'readable', { get(this: Subprocess) { return this.stdout; } }); + Object.defineProperty(subp, 'exited', { + value: new Promise((resolve, reject) => { + subpAsNode.once('exit', (code) => { + stdstreams[0]?.destroy(); + stdstreams[1]?.destroy(); + stdstreams[2]?.destroy(); + subp.kill(); + subp.unref(); + subpAsNode.disconnect?.(); + subpAsNode.removeAllListeners(); + resolve(code); + }); + }) + }); + if (stdinSrc) subpAsNode.once('spawn', () => { + const stdinWeb = streams.Writable.toWeb(internalStdinStream); + if (isArrayBufferView(stdinSrc)) stdinSrc = new Blob([stdinSrc]); + if (stdinSrc instanceof Blob) void stdinSrc.stream().pipeTo(stdinWeb); + else if (stdinSrc instanceof Response || stdinSrc instanceof Request) void stdinSrc.body!.pipeTo(stdinWeb); + else if (typeof stdinSrc === 'number') void fs.createReadStream('', { fd: stdinSrc }).pipe(internalStdinStream); + else void stdinSrc; + }); + // change the error stack to point to the spawn() call instead of internal Node.js callback stuff + const here = new Error('§__PLACEHOLDER__§'); + Error.captureStackTrace(here, spawn); + if (!subpAsNode.pid) return subpAsNode.once('error', (err: SystemError) => { + err.message = (err.syscall ?? `spawn ${err.path ?? ''}`) + ' ' + (err.code ?? String(err.errno ?? '')); + err.stack = here.stack!.replace('§__PLACEHOLDER__§', err.message); + throw err; + }) as unknown as Subprocess; + return subp; +}) satisfies typeof Bun.spawn; +export const spawnSync = ((...args): SyncSubprocess => { + let cmd: string; + let argv: string[]; + let opts: SpawnOptions.OptionsObject; + if (args[0] instanceof Array) { + cmd = args[0][0]; + argv = args[0].slice(1); + opts = isOptions(args[1]) ? args[1] : {}; + } else { + cmd = args[0].cmd[0]; + argv = args[0].cmd.slice(1); + opts = args[0]; + Reflect.deleteProperty(opts, 'cmd'); + } + + let stdio: StdioOptions = []; + opts.stdio ??= [undefined, undefined, undefined]; + if (opts.stdin) opts.stdio[0] = opts.stdin; + if (opts.stdout) opts.stdio[1] = opts.stdout; + if (opts.stderr) opts.stdio[2] = opts.stderr; + for (let i = 1; i < 3; i++) { // this intentionally skips stdin + let std = opts.stdio[i]; + if (isArrayBufferView(std)) stdio[i] = streams.Readable.fromWeb(new Blob([std]).stream()); + else if (std instanceof Blob || isFileBlob(std)) stdio[i] = streams.Readable.fromWeb(std.stream()); + else if (std instanceof ReadableStream) stdio[i] = streams.Readable.fromWeb(std); + else if (std instanceof Response || std instanceof Request) stdio[i] = streams.Readable.fromWeb(std.body!); + else stdio[i] = std; + } + let input: ArrayBufferView | string | undefined; + if (opts.stdio[0] && typeof opts.stdio[0] !== 'string') { + stdio[0] = null; // will be overriden by chp.spawnSync "input" option + //! Due to the fully async nature of Blobs, Responses and Requests, + //! we can't synchronously get the data out of them here in userland. + if (opts.stdio[0] instanceof Blob) throw new NotImplementedError('Bun.spawnSync({ stdin: })', spawnSync); + else if (opts.stdio[0] instanceof Response || opts.stdio[0] instanceof Request) throw new NotImplementedError('Bun.spawnSync({ stdin: })', spawnSync); + else if (typeof opts.stdio[0] === 'number') input = fs.readFileSync(opts.stdio[0]); + else input = opts.stdio[0] as ArrayBufferView; + } + + const subp = chp.spawnSync(cmd, argv, { + cwd: opts.cwd ?? process.cwd(), + env: { ...(opts.env as Record ?? process.env) }, + stdio, input + }) as unknown as SyncSubprocess; + const subpAsNode = subp as unknown as SpawnSyncReturns; + if (subpAsNode.error) throw subpAsNode.error; + + subp.exitCode = subpAsNode.status ?? NaN; //! not sure what Bun would return here (child killed by signal) + subp.success = subp.exitCode === 0; + return subp; +}) satisfies typeof Bun.spawnSync; + +export const escapeHTML = ((input) => { + const str = String(input); + let out = ''; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + switch (char) { + case '"': out += '"'; break; + case "'": out += '''; break; + case '&': out += '&'; break; + case '<': out += '<'; break; + case '>': out += '>'; break; + default: out += char; + } + } + return out; +}) satisfies typeof Bun.escapeHTML; + +export const readableStreamToArrayBuffer = ((stream: ReadableStream): ArrayBuffer | Promise => { + return (async () => { + const sink = new ArrayBufferSink(); + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + sink.write(value); + } + return sink.end() as ArrayBuffer; + })(); +}) satisfies typeof Bun.readableStreamToArrayBuffer; +export const readableStreamToText = (async (stream: ReadableStream) => { + let result = ''; + const reader = stream.pipeThrough(new TextDecoderStream()).getReader(); ReadableStreamDefaultReader + while (true) { + const { done, value } = await reader.read(); + //! for some reason "done" isnt being set to true so this is just infinitely looping at the moment... sigh + if (done || !value || !value?.length) break; + result += value; + } + return result; +}) satisfies typeof Bun.readableStreamToText; +export const readableStreamToBlob = (async (stream: ReadableStream) => { + const parts = await readableStreamToArray(stream); + return new Blob(parts as BlobPart[]); +}) satisfies typeof Bun.readableStreamToBlob; +export const readableStreamToArray = (async (stream: ReadableStream) => { + const array = new Array(); + const reader = stream.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done || !value || !(value)?.length) break; + array.push(value as unknown as T); + } + return array; +}) satisfies typeof Bun.readableStreamToArray; +export const readableStreamToJSON = (async (stream: ReadableStream) => { + const text = await readableStreamToText(stream); + try { + return JSON.parse(text) as T; + } catch (err) { + Error.captureStackTrace(err as Error, readableStreamToJSON); + throw err; + } +}) satisfies typeof Bun.readableStreamToJSON; + +export const concatArrayBuffers = ((buffers) => { + let size = 0; + for (const chunk of buffers) size += chunk.byteLength; + const buffer = new ArrayBuffer(size); + const view = new Uint8Array(buffer); + let offset = 0; + for (const chunk of buffers) { + view.set(new Uint8Array(chunk instanceof ArrayBuffer || chunk instanceof SharedArrayBuffer ? chunk : chunk.buffer), offset); + offset += chunk.byteLength; + } + return buffer; +}) satisfies typeof Bun.concatArrayBuffers; + +export const ArrayBufferSink = ArrayBufferSinkPolyfill satisfies typeof Bun.ArrayBufferSink; + +export const pathToFileURL = pathToFileURLNode satisfies typeof Bun.pathToFileURL; +export const fileURLToPath = fileURLToPathNode satisfies typeof Bun.fileURLToPath; + +export const dns = dnsPolyfill satisfies typeof Bun.dns; + +export const isMainThread = workers.isMainThread satisfies typeof Bun.isMainThread; + +//! It may be possible to implement plugins with Node ESM loaders, but it would take some effort and have some caveats. +//! For now, we'll simply make all calls to Bun.plugin no-op, such that manual implementation of an external ESM loader is possible, +//! but without needing to strip out all Bun.plugin calls from the source code for running on Node. +const dummyPluginBuilder: PluginBuilder = ({ + onLoad(constraints: PluginConstraints, callback: OnLoadCallback): void { + return; // stubbed + }, + onResolve(constraints: PluginConstraints, callback: OnResolveCallback): void { + return; // stubbed + }, + config: { plugins: [], entrypoints: [] }, +}) satisfies PluginBuilder; +const bunPlugin = (options: T) => options?.setup?.(dummyPluginBuilder) as ReturnType; +bunPlugin.clearAll = () => void 0; +export const plugin = bunPlugin satisfies typeof Bun.plugin; +/*void plugin({ + name: 'test', + target: 'bun', + setup(builder) { + if (builder.target !== 'bun') return; + builder.onResolve({ namespace: 'sample', filter: /.+/ }, args => { + args.importer; + if (args.path === 'foo') return { namespace: 'redirect', path: 'bar' }; + else return; + }); + builder.onLoad({ namespace: 'sample', filter: /.+/ }, args => { + args.path; + return { loader: 'object', exports: { foo: 'bar' }, contents: 'void 0;' }; + }); + } +});*/ diff --git a/packages/bun-polyfills/src/modules/bun/arraybuffersink.ts b/packages/bun-polyfills/src/modules/bun/arraybuffersink.ts new file mode 100644 index 0000000000..5144bafb91 --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/arraybuffersink.ts @@ -0,0 +1,67 @@ +type BunArrayBufferSink = InstanceType; + +export class ArrayBufferSink implements BunArrayBufferSink { + #started: boolean = true; + #closed: boolean = false; + #offset: number = 0; + #stream: boolean = false; + #asUint8: boolean = false; + #buffer: Buffer = Buffer.allocUnsafe(8192); + + get sinkId(): number { return 0; } //? undocumented, seems to always return 0 + + #ASSERT_NOT_CLOSED(caller: AnyFunction): void { + if (!this.#closed) return; + const err = new TypeError('Expected Sink'); + Error.captureStackTrace(err, caller); + throw err; + } + + start({ asUint8Array = false, highWaterMark = 8192, stream = false }: Parameters[0] = {}): void { + this.#ASSERT_NOT_CLOSED(this.start); + this.#started = true; + this.#offset = 0; + this.#stream = stream; + this.#asUint8 = asUint8Array; + if (highWaterMark !== this.#buffer.byteLength) this.#buffer = Buffer.allocUnsafe(highWaterMark); + } + + write(data: string | ArrayBufferView | SharedArrayBuffer | ArrayBuffer): number { + this.#ASSERT_NOT_CLOSED(this.write); + if (typeof data === 'string') data = new TextEncoder().encode(data); + const writedata = (data instanceof ArrayBuffer || data instanceof SharedArrayBuffer) ? new Uint8Array(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + // this is very bad API design to not throw an error here, but it's what Bun does + if (!this.#started) return writedata.byteLength; + + if (this.#offset + writedata.byteLength > this.#buffer.byteLength) { + const newLength = Math.ceil((this.#offset + writedata.byteLength) / 1024) * 1024; + const newBuffer = Buffer.allocUnsafe(newLength); + newBuffer.set(this.#buffer); + this.#buffer = newBuffer; + } + this.#buffer.set(writedata, this.#offset); + this.#offset += writedata.byteLength; + return writedata.byteLength; + } + + flush(): number | Uint8Array | ArrayBuffer { + this.#ASSERT_NOT_CLOSED(this.flush); + if (!this.#stream) return 0; //! brokenly seems to always return 0 and do nothing + const flushed = new Uint8Array(this.#offset); + flushed.set(this.#buffer.subarray(0, this.#offset)); // faster than Buffer.copy or Uint8Array.slice + this.#offset = 0; + return this.#asUint8 ? flushed : flushed.buffer as ArrayBuffer; + } + + end(): Uint8Array | ArrayBuffer { + this.#ASSERT_NOT_CLOSED(this.end); + const stream = this.#stream; + this.#stream = true; // force flush() to return the data + const buffer = this.flush() as Uint8Array | ArrayBuffer; + this.#stream = stream; + this.#started = false; + return buffer; + } + + close(): void { this.#closed = true; } //? undocumented +} diff --git a/packages/bun-polyfills/src/modules/bun/dns.ts b/packages/bun-polyfills/src/modules/bun/dns.ts new file mode 100644 index 0000000000..b87470f9c9 --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/dns.ts @@ -0,0 +1,21 @@ +import dns from 'node:dns'; + +const dnsObj: typeof Bun.dns = { + async lookup(hostname, options) { + const opts = { verbatim: true, all: true } as dns.LookupOptions; + if (options?.family) { + if (options.family === 'IPv4') opts.family = 4; + else if (options.family === 'IPv6') opts.family = 6; + else if (options.family === 'any') opts.family = 0; + else opts.family = options.family; + } + if (options?.flags) opts.hints = options.flags; + const records = ((await dns.promises.resolveAny(hostname)) + .filter(r => r.type === 'A' || r.type === 'AAAA') as (dns.AnyARecord | dns.AnyAaaaRecord)[]) + .map(r => ({ address: r.address, family: r.type === 'A' ? 4 as const : 6 as const, ttl: r.ttl })); + return records; + }, + // This has more properties but they're not documented on bun-types yet, oh well. +}; + +export default dnsObj; diff --git a/packages/bun-polyfills/src/modules/bun/fileblob.ts b/packages/bun-polyfills/src/modules/bun/fileblob.ts new file mode 100644 index 0000000000..abe2294da4 --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/fileblob.ts @@ -0,0 +1,195 @@ +import fs from 'node:fs'; +import tty from 'node:tty'; +import streams from 'node:stream'; +import { ReadableStream as NodeWebReadableStream } from 'node:stream/web'; +import { FileSink } from './filesink.js'; +import { SystemError } from '../../utils/errors.js'; +import type { FileBlob as BunFileBlob, FileSink as BunFileSink } from 'bun'; + +type NodeJSStream = streams.Readable | streams.Writable; + +function NodeJSReadableStreamToBlob(stream: NodeJS.ReadableStream | NodeJS.ReadWriteStream, iostream: boolean = false, type?: string): Promise { + if (stream.isPaused()) stream.resume(); + return new Promise((resolve, reject) => { + const chunks: any[] = []; + const dataHandler = (chunk: any) => { chunks.push(chunk); if (iostream) end(); }; + const end = () => { + resolve(new Blob(chunks, type != null ? { type } : undefined)); + stream.off('data', dataHandler); + stream.off('end', end); + stream.pause(); + }; + stream.once('data', dataHandler).once('end', end); + //.once('error', reject); Bun waits to error on actual operations on the stream, therefore so will we. + }); +} + +export const NodeJSStreamFileBlob = class FileBlob extends Blob { + constructor(source: NodeJSStream, slice: [number?, number?] = [undefined, undefined], type = 'application/octet-stream') { + super(undefined, { type }); + Reflect.deleteProperty(this, 'size'); + if (source === process.stdout || source === process.stdin || source === process.stderr) { + this.#iostream = true; + } + this.#readable = source instanceof streams.Readable && !(source instanceof tty.WriteStream); + this.#source = source; + this.#slice = slice; + this.#size = Infinity; + } + readonly #iostream: boolean = false; + readonly #readable: boolean; + readonly #source: NodeJSStream; + readonly #slice: [number?, number?]; + #size: number; + + slice(begin?: number, end?: number, contentType?: string): Blob; + slice(begin?: number, contentType?: string): Blob; + slice(contentType?: string): Blob; + slice(beginOrType?: number | string, endOrType?: number | string, contentType: string = this.type): Blob { + if (typeof beginOrType === 'string') return new FileBlob(this.#source, this.#slice, beginOrType); + if (typeof endOrType === 'string') return new FileBlob(this.#source, [beginOrType, undefined], endOrType); + return new FileBlob(this.#source, [beginOrType, endOrType], contentType); + } + + override stream(): ReadableStream { + // This makes no sense but Bun does it so we will too + if (!this.#readable) return new ReadableStream(); + return streams.Readable.toWeb(this.#source as streams.Readable); + } + + #blobStackFn: AnyFunction = this.#getBlob; + + async #getBlob(): Promise { + if (!this.#readable) { + const err = new SystemError(-1, 'read'); + Error.captureStackTrace(err, this.#blobStackFn); + throw err; + } + const blob = (await NodeJSReadableStreamToBlob(this.#source as streams.Readable, this.#iostream)).slice(...this.#slice); + this.#size = blob.size; + return blob; + } + + override async text(): Promise { + if (this.#blobStackFn !== this.json) this.#blobStackFn = this.text; + return (await this.#getBlob()).text(); + } + override async arrayBuffer(): Promise { + this.#blobStackFn = this.arrayBuffer; + return (await this.#getBlob()).arrayBuffer(); + } + override async json(): Promise { + this.#blobStackFn = this.json; + return JSON.parse(await this.text()) as Promise; + } + + override get size(): number { return this.#size; } + override set size(_) { return; } +}; + +export class FileBlob extends Blob implements BunFileBlob { + constructor(fdOrPath: number | string, opts: BlobPropertyBag = {}) { + opts.type ??= 'application/octet-stream'; // TODO: Get MIME type from file extension + super(undefined, opts); + Reflect.deleteProperty(this, 'size'); + if (Reflect.get(opts, '__data')) this.#data = Reflect.get(opts, '__data') as Blob; + const slice = Reflect.get(opts, '__slice') as [number?, number?] | undefined; + if (slice) { + slice[0] &&= slice[0] | 0; // int cast + slice[1] &&= slice[1] | 0; // int cast + this.#slice = slice; + slice[0] ??= 0; + if (typeof slice[1] === 'undefined') { + if (slice[0] < 0) this.#sliceSize = -slice[0]; + } + else if (slice[0] < 0 && slice[1] < 0) this.#sliceSize = -(slice[0] - slice[1]); + else if (slice[0] >= 0 && slice[1] >= 0) this.#sliceSize = slice[1] - slice[0]; + } + if (typeof fdOrPath === 'string') try { + this.#fd = fs.openSync(fdOrPath, 'r+'); + } catch (err) { + this.#error = err as SystemError; + } + else { + this.#fd = fdOrPath; + this.#error = Reflect.get(opts, '__error') as SystemError | undefined; + } + if (!this.#error) { + const rstream = fs.createReadStream('', { fd: this.#fd, start: this.#slice[0], end: this.#slice[1] }); + this.#readable = streams.Readable.toWeb(rstream); + } + } + readonly #readable?: NodeWebReadableStream; + readonly #error?: SystemError; + readonly #slice: [number?, number?] = []; + readonly #sliceSize: number = 0; + readonly #fd: number = NaN; + #data?: Blob; + + #read() { + if (this.#error) throw this.#error; + const read = fs.readFileSync(this.#fd); + this.#data = new Blob([read.subarray(...this.#slice)], { type: this.type }); + } + + //! Bun 0.2 seems to return undefined for this, this might not be accurate or it's broken on Bun's side + get readable(): ReadableStream { + if (this.#error) throw this.#error; + return this.#readable! as ReadableStream; + } + + get lastModified(): number { + if (this.#error) throw this.#error; + return fs.fstatSync(this.#fd).mtimeMs; + } + + async exists(): Promise { + return !this.#error; + } + + writer(): BunFileSink { + if (this.#error) throw this.#error; + return new FileSink(this.#fd); + } + + // TODO: what's contentType? + override slice(begin?: number | string, end?: number | string, contentType?: string): FileBlob { + if (typeof begin === 'string') { + contentType = begin; + begin = undefined; + } + if (typeof end === 'string') { + contentType = end; + end = undefined; + } + return new FileBlob(this.#fd, { + __error: this.#error, + __slice: [begin, end], + __data: this.#data?.slice(begin, end), + } as BlobPropertyBag); + } + override arrayBuffer(): Promise { + if (!this.#data) this.#read(); + return new Blob([this.#data ?? '']).arrayBuffer(); + } + override text(): Promise { + if (!this.#data) this.#read(); + return new Blob([this.#data ?? '']).text(); + } + override json(): Promise; + override json(): Promise; + override json(): Promise | Promise { + if (!this.#data) this.#read(); + return new Blob([this.#data ?? '']).json(); + } + override stream(): NodeJS.ReadableStream; + override stream(): ReadableStream; + override stream(): ReadableStream | NodeJS.ReadableStream { + if (!this.#data) this.#read(); + return new Blob([this.#data ?? '']).stream(); + } + + override get size(): number { + return this.#data?.size ?? (this.#sliceSize || 0); + } +} diff --git a/packages/bun-polyfills/src/modules/bun/filesink.ts b/packages/bun-polyfills/src/modules/bun/filesink.ts new file mode 100644 index 0000000000..4767814fdf --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/filesink.ts @@ -0,0 +1,87 @@ +import fs from 'node:fs'; +import { SystemError } from '../../utils/errors.js'; +import type { FileSink as BunFileSink } from 'bun'; + +export class FileSink implements BunFileSink { + constructor(fdOrPathOrStream: number | string | NodeJS.WritableStream) { + if (typeof fdOrPathOrStream === 'string') try { + this.#fd = fs.openSync(fdOrPathOrStream, 'a+'); + fs.ftruncateSync(this.#fd, 0); + } catch (err) { + throw err as SystemError; + } + else if (typeof fdOrPathOrStream === 'number') { + this.#fd = fdOrPathOrStream; // hope this fd is writable + fs.ftruncateSync(this.#fd, 0); + } + else { + this.#stream = fdOrPathOrStream; + } + } + #fd: number = NaN; + #stream: NodeJS.WritableStream | undefined; + #closed: boolean = false; + #writtenSinceFlush: number = 0; + #totalWritten: number = 0; + + start(options?: { highWaterMark?: number | undefined; } | undefined): void { + return; // TODO + } + + ref(): void { + return; // TODO + } + + unref(): void { + return; // TODO + } + + write(chunk: string | ArrayBufferView | SharedArrayBuffer | ArrayBuffer): number { + if (this.#closed) { + return typeof chunk === 'string' ? chunk.length : chunk.byteLength; + } + if (this.#stream) { + let data; + if (chunk instanceof ArrayBuffer || chunk instanceof SharedArrayBuffer) data = new Uint8Array(chunk); + else if (!(chunk instanceof Uint8Array) && typeof chunk !== 'string') data = new Uint8Array(chunk.buffer); + else data = chunk; + this.#stream.write(data); + const written = typeof data === 'string' ? data.length : data.byteLength; + this.#totalWritten += written; + return written; + } + if (typeof chunk === 'string') { + fs.appendFileSync(this.#fd, chunk, 'utf8'); + this.#writtenSinceFlush += chunk.length; + return chunk.length; + } + if (chunk instanceof ArrayBuffer || chunk instanceof SharedArrayBuffer) fs.appendFileSync(this.#fd, new Uint8Array(chunk)); + else fs.appendFileSync(this.#fd, new Uint8Array(chunk.buffer)); + this.#writtenSinceFlush += chunk.byteLength; + return chunk.byteLength; + } + + //! flushing after writing to a closed FileSink segfaults in Bun but I don't see the need to implement that behavior + flush(): number | Promise { + if (this.#closed) return 0; + // no-op because this is a synchronous implementation + const written = this.#writtenSinceFlush; + this.#writtenSinceFlush = 0; + return written; + } + + //! not sure what to do with this error + end(error?: Error): number | Promise { + if (this.#closed) return this.#totalWritten; + const flushed = this.flush(); + if (this.#stream) { + this.#stream.end(); + this.#closed = true; + return flushed; + } + this.#totalWritten = fs.fstatSync(this.#fd).size; + fs.closeSync(this.#fd); + this.#closed = true; + return flushed; + } +} diff --git a/packages/bun-polyfills/src/modules/bun/hashes.ts b/packages/bun-polyfills/src/modules/bun/hashes.ts new file mode 100644 index 0000000000..ec254c03ac --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/hashes.ts @@ -0,0 +1,185 @@ +import type { CryptoHashInterface, DigestEncoding, Hash } from 'bun'; +import nodecrypto from 'node:crypto'; +import os from 'node:os'; +import md4, { Md4 } from 'js-md4'; +import { wyhash, adler32, crc32, cityhash32, cityhash64, murmur32v3, murmur64v2, murmur32v2 } from '../../../lib/zighash/index.mjs'; + +export const bunHash = ((data, seed = 0): bigint => wyhash(data, BigInt(seed))) as typeof Bun.hash; +export const bunHashProto: Hash = { + wyhash(data, seed = 0n) { return wyhash(data, seed); }, + adler32(data) { return adler32(data); }, + crc32(data) { return crc32(data); }, + cityHash32(data) { return cityhash32(data); }, + cityHash64(data, seed = 0n) { return cityhash64(data, seed); }, + murmur32v3(data, seed = 0) { return murmur32v3(data, seed); }, + murmur32v2(data, seed = 0) { return murmur32v2(data, seed); }, + murmur64v2(data, seed = 0n) { return murmur64v2(data, seed); }, +}; + +type HashImpl = { + digest(): Buffer; + digest(encoding: nodecrypto.BinaryToTextEncoding): string; + update(data: nodecrypto.BinaryLike): HashImpl; + update(data: string, inputEncoding: nodecrypto.Encoding): HashImpl; +}; +abstract class BaseHash implements CryptoHashInterface { + readonly #hash: HashImpl | null; + constructor(algorithm: string | HashImpl) { + if (typeof algorithm === 'string') this.#hash = nodecrypto.createHash(algorithm); + // If no preset algorithm is given, expect the subclass to fully implement its own. + else this.#hash = algorithm; + } + update(data: StringOrBuffer) { + if (data instanceof ArrayBuffer || data instanceof SharedArrayBuffer) this.#hash!.update(new Uint8Array(data)); + else this.#hash!.update(data); + return this as unknown as T; // is there any good way to do this without asserting? + } + digest(encoding: DigestEncoding): string; + digest(hashInto?: TypedArray): TypedArray; + digest(encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + if (typeof encodingOrHashInto === 'string') { + const encoded = this.#hash!.digest(encodingOrHashInto); + // you'd think node would throw an error if the encoding is invalid, but nope! + // instead it silently returns as if you passed no encoding and gives a Buffer... + if (Buffer.isBuffer(encoded)) throw new TypeError(`Unknown encoding: "${encodingOrHashInto}"`); + else return encoded; + } + const digested = this.#hash!.digest(); + if (encodingOrHashInto === undefined) return new Uint8Array(digested.buffer, digested.byteOffset, digested.byteLength); + if (encodingOrHashInto.byteLength < this.byteLength) throw new TypeError(`TypedArray must be at least ${this.byteLength} bytes`); + if (encodingOrHashInto instanceof BigInt64Array || encodingOrHashInto instanceof BigUint64Array) { + // avoid checking endianness for every loop iteration + const endianAwareInsert = os.endianness() === 'LE' + ? (arr: string[], j: number, num: string) => arr[7 - j] = num + : (arr: string[], j: number, num: string) => arr[j] = num; + + for (let i = 0; i < digested.byteLength; i += 8) { + const bigintStrArr = ['', '', '', '', '', '', '', '']; + for (let j = 0; j < 8; j++) { + const byte = digested[i + j]; + if (byte === undefined) break; + endianAwareInsert(bigintStrArr, j, byte.toString(16).padStart(2, '0')); + } + encodingOrHashInto[i / 8] = BigInt(`0x${bigintStrArr.join('')}`); + } + } else { + const HashIntoTypedArray = encodingOrHashInto.constructor as TypedArrayConstructor; + // this will work as long as all hash classes have a byteLength that is a multiple of 4 bytes + encodingOrHashInto.set(new HashIntoTypedArray(digested.buffer, digested.byteOffset, digested.byteLength / HashIntoTypedArray.BYTES_PER_ELEMENT)); + } + return encodingOrHashInto; + } + static hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { return '' }; + static readonly byteLength: number; + abstract readonly byteLength: number; +} + +export class SHA1 extends BaseHash { + constructor() { super('sha1'); } + static override readonly byteLength = 20; + override readonly byteLength = 20; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class MD4 extends BaseHash { + constructor() { //! Not supported by nodecrypto + const hash = md4.create() as unknown as Omit & { _update: Md4['update'] }; + function digest(): Buffer; + function digest(encoding: nodecrypto.BinaryToTextEncoding): string; + function digest(encoding?: nodecrypto.BinaryToTextEncoding) { + const buf = Buffer.from(hash.arrayBuffer()); + if (encoding) return buf.toString(encoding); + else return buf; + } + function update(data: nodecrypto.BinaryLike) { + if (typeof data === 'string') hash._update(data); + else if (data instanceof ArrayBuffer || data instanceof SharedArrayBuffer) hash._update(new Uint8Array(data)); + else hash._update(new Uint8Array(data.buffer)); + return hash as unknown as MD4HashImpl; + } + type MD4HashImpl = Omit & { digest: typeof digest, update: typeof update }; + // @ts-expect-error patches to reuse the BaseHash methods + hash.digest = digest; hash._update = hash.update; hash.update = update; + super(hash as unknown as MD4HashImpl); + } + static override readonly byteLength = 16; + override readonly byteLength = 16; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class MD5 extends BaseHash { + constructor() { super('md5'); } + static override readonly byteLength = 16; + override readonly byteLength = 16; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class SHA224 extends BaseHash { + constructor() { super('sha224'); } + static override readonly byteLength = 28; + override readonly byteLength = 28; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class SHA512 extends BaseHash { + constructor() { super('sha512'); } + static override readonly byteLength = 64; + override readonly byteLength = 64; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class SHA384 extends BaseHash { + constructor() { super('sha384'); } + static override readonly byteLength = 48; + override readonly byteLength = 48; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class SHA256 extends BaseHash { + constructor() { super('sha256'); } + static override readonly byteLength = 32; + override readonly byteLength = 32; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} +export class SHA512_256 extends BaseHash { + constructor() { super('sha512-256'); } + static override readonly byteLength = 32; + override readonly byteLength = 32; + static override hash(data: StringOrBuffer, encoding?: DigestEncoding): string; + static override hash(data: StringOrBuffer, hashInto?: TypedArray): TypedArray; + static override hash(data: StringOrBuffer, encodingOrHashInto?: DigestEncoding | TypedArray): string | TypedArray { + const instance = new this(); instance.update(data); + return instance.digest(encodingOrHashInto as DigestEncoding & TypedArray); + } +} diff --git a/packages/bun-polyfills/src/modules/bun/transpiler.ts b/packages/bun-polyfills/src/modules/bun/transpiler.ts new file mode 100644 index 0000000000..b4b95dae14 --- /dev/null +++ b/packages/bun-polyfills/src/modules/bun/transpiler.ts @@ -0,0 +1,103 @@ +import type { JavaScriptLoader, TranspilerOptions, Transpiler as BunTranspiler, Import } from 'bun'; +import { NotImplementedError } from '../../utils/errors.js'; + +// TODO: Possible implementation with WASM builds of bun with just the transpiler? +// NOTE: This is possible to implement with something like SWC, and was previously done, +// but it has lots of quirks due to the differences between SWC and Bun, so the plan is +// to not do that unless there is actual demand for using Bun.Transpiler in Node.js before +// the WASM build is worked on. The signatures are here for now as a placeholder. + +export default class Transpiler implements BunTranspiler { + constructor(options?: TranspilerOptions) { + this.#options = options ?? {}; + } + + async transform(code: StringOrBuffer, loader: JavaScriptLoader): Promise { + if (typeof code !== 'string') code = new TextDecoder().decode(code); + throw new NotImplementedError('Bun.Transpiler', this.transform); + } + + transformSync(code: StringOrBuffer, ctx: object): string; + transformSync(code: StringOrBuffer, loader: JavaScriptLoader, ctx: object): string; + transformSync(code: StringOrBuffer, loader?: JavaScriptLoader | undefined): string; + transformSync(code: StringOrBuffer, loader?: JavaScriptLoader | object, ctx: object = {}): string { + if (typeof code !== 'string') code = new TextDecoder().decode(code); + if (typeof loader !== 'string') loader = 'js'; + throw new NotImplementedError('Bun.Transpiler', this.transformSync); + } + + scan(code: StringOrBuffer): { exports: string[]; imports: Import[]; } { + if (typeof code !== 'string') code = new TextDecoder().decode(code); + throw new NotImplementedError('Bun.Transpiler', this.scan); + //return { + // imports: this.scanImports(code), + // exports: this.#scanExports(code) + //}; + } + + scanImports(code: StringOrBuffer): { + kind: 'import-statement' | 'dynamic-import'; + path: string; + }[] { + if (typeof code !== 'string') code = new TextDecoder().decode(code); + throw new NotImplementedError('Bun.Transpiler', this.scanImports); + //const imports: { kind: 'import-statement' | 'dynamic-import', path: string }[] = []; + //this.#scanTopLevelImports(code).forEach(x => imports.push({ kind: 'import-statement', path: x })); + //this.#scanDynamicImports(code).forEach(x => imports.push({ kind: 'dynamic-import', path: x })); + //return imports; + } + + /*#scanDynamicImports(code: string): string[] { + return this.parseSync(code, { + syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx' + }).body.filter(x => x.type === 'ExpressionStatement' && x.expression.type === 'CallExpression' && x.expression.callee.type === 'Import') + .map(i => (((i as swc.ExpressionStatement).expression as swc.CallExpression).arguments[0].expression as swc.StringLiteral).value); + }*/ + + /*#scanTopLevelImports(code: string): string[] { + return this.parseSync(code, { + syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx' + }).body.filter(x => x.type === 'ImportDeclaration' || x.type === 'ExportAllDeclaration' || x.type === 'ExportNamedDeclaration') + .filter(i => !(i as swc.ImportDeclaration).typeOnly) + .map(i => (i as swc.ImportDeclaration).source.value); + }*/ + + /*#scanExports(code: string, includeDefault: boolean = false): string[] { + const parsed = this.parseSync(code, { + syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx' + }).body; + const exports = []; + exports.push(parsed.filter(x => x.type === 'ExportDeclaration' && !x.declaration.declare) + .flatMap(i => ((i as swc.ExportDeclaration).declaration as swc.ClassDeclaration).identifier?.value ?? + ((i as swc.ExportDeclaration).declaration as swc.VariableDeclaration).declarations.map(d => (d.id as swc.Identifier).value) + ) + ); + exports.push(parsed.filter(x => x.type === 'ExportNamedDeclaration') + .flatMap(i => (i as swc.ExportNamedDeclaration).specifiers + .filter(s => s.type === 'ExportSpecifier' && !s.isTypeOnly) + .map(s => (s as swc.NamedExportSpecifier).exported?.value ?? (s as swc.NamedExportSpecifier).orig.value) + ) + ); + if (includeDefault) exports.push(this.#scanDefaultExport(code) ?? []); + return exports.flat(); + }*/ + + /*#scanDefaultExport(code: string): 'default' | undefined { + const parsed = this.parseSync(code, { + syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx' + }).body; + + const defaultExportDecl = parsed.find(x => x.type === 'ExportDefaultDeclaration') as swc.ExportDefaultDeclaration | undefined; + if (!defaultExportDecl) { + const defaultExportExpr = parsed.find(x => x.type === 'ExportDefaultExpression') as swc.ExportDefaultExpression | undefined; + if (!defaultExportExpr) return undefined; + if (!defaultExportExpr.expression.type.startsWith('Ts')) return 'default'; + else return undefined; + } + + if (!defaultExportDecl.decl.type.startsWith('Ts') && !Reflect.get(defaultExportDecl.decl, 'declare')) return 'default'; + else return undefined; + }*/ + + #options: TranspilerOptions; +} diff --git a/packages/bun-polyfills/src/repl.ts b/packages/bun-polyfills/src/repl.ts new file mode 100644 index 0000000000..5cf6737017 --- /dev/null +++ b/packages/bun-polyfills/src/repl.ts @@ -0,0 +1,29 @@ +import bun from './index.js'; + +// This file serves two purposes: +// 1. It is the entry point for using the Bun global in the REPL. (--import this file) +// 2. It makes TypeScript check the full structural compatibility of the Bun global vs the polyfills object, +// which allows for the type assertion below to be used as a TODO list index. + +globalThis.Bun = bun as typeof bun & { + // TODO: Missing polyfills + readableStreamToFormData: typeof import('bun').readableStreamToFormData; + deepEquals: typeof import('bun').deepEquals; + deepMatch: typeof import('bun').deepMatch; + build: typeof import('bun').build; + mmap: typeof import('bun').mmap; + gc: typeof import('bun').gc; + connect: typeof import('bun').connect; + listen: typeof import('bun').listen; + Transpiler: typeof import('bun').Transpiler; + password: typeof import('bun').password; + CryptoHashInterface: typeof import('bun').CryptoHashInterface; + CryptoHasher: typeof import('bun').CryptoHasher; + FileSystemRouter: typeof import('bun').FileSystemRouter; + + //? Polyfilled but with broken types (See each one in ./src/modules/bun.ts for details) + generateHeapSnapshot: typeof import('bun').generateHeapSnapshot; + stdout: typeof import('bun').stdout; + stderr: typeof import('bun').stderr; + stdin: typeof import('bun').stdin; +}; diff --git a/packages/bun-polyfills/src/types/helpers.d.ts b/packages/bun-polyfills/src/types/helpers.d.ts new file mode 100644 index 0000000000..83726dadd1 --- /dev/null +++ b/packages/bun-polyfills/src/types/helpers.d.ts @@ -0,0 +1,13 @@ +type AnyFunction = (...args: any[]) => any; +type AnyClass = new (...args: any[]) => any; +type AnyCallable = AnyFunction | AnyClass; + +type MapKeysType> = T extends Map ? K : never; +type MapValuesType> = T extends Map ? V : never; + +type Mutable = { -readonly [K in keyof T]: T[K] }; + +/** Excluding the BigInt typed arrays */ +type TypedArrayConstructor = + | typeof Uint8Array | typeof Uint16Array | typeof Uint32Array | typeof Uint8ClampedArray + | typeof Int8Array | typeof Int16Array | typeof Int32Array | typeof Float32Array | typeof Float64Array; diff --git a/packages/bun-polyfills/src/types/md4.d.ts b/packages/bun-polyfills/src/types/md4.d.ts new file mode 100644 index 0000000000..30413396e2 --- /dev/null +++ b/packages/bun-polyfills/src/types/md4.d.ts @@ -0,0 +1,72 @@ +declare module 'js-md4' { + export type MD4Input = string | ArrayBuffer | Uint8Array | number[]; + + interface md4 { + /** + * # Broken, will throw an error. + * @deprecated Use {@link md4.hex} instead. + */ + (input: MD4Input): never; + /** Creates an `Md4` hasher instance. */ + create(): Md4; + /** Shortcut for `md4.create().update(...)` */ + update(message: MD4Input): Md4; + /** Hash `message` into a hex string. */ + hex(message: MD4Input): string; + /** Hash `message` into an Array. */ + array(message: MD4Input): number[]; + /** Identical to {@link md4.array}. */ + digest(message: MD4Input): number[]; + /** + * Identical to {@link md4.arrayBuffer}. + * @deprecated Use {@link md4.arrayBuffer} instead. + */ + buffer(message: MD4Input): ArrayBuffer; + /** Hash `message` into an ArrayBuffer. */ + arrayBuffer(message: MD4Input): ArrayBuffer; + } + + export type Md4 = Md4; + declare class Md4 { + private constructor(); + + private toString(): string; + private finalize(): void; + private hash(): void; + /** + * Append `message` to the internal hash source data. + * @returns A reference to `this` for chaining, or nothing if the instance has been finalized. + */ + update(message: MD4Input): this | void; + /** Hash into a hex string. Finalizes the hash. */ + hex(): string; + /** Hash into an Array. Finalizes the hash. */ + array(): number[]; + /** Identical to {@link Md4.array}. */ + digest(): number[]; + /** + * Identical to {@link Md4.arrayBuffer}. + * @deprecated Use {@link Md4.arrayBuffer} instead. + */ + buffer(): ArrayBuffer; + /** Hash into an ArrayBuffer. Finalizes the hash. */ + arrayBuffer(): ArrayBuffer; + + private buffer8: Uint8Array; + private blocks: Uint32Array; + private bytes: number; + private start: number; + private h3: number; + private h2: number; + private h1: number; + private h0: number; + readonly hashed: boolean; + /** If true, `update()` operations will silently fail. */ + readonly finalized: boolean; + readonly first: boolean; + private lastByteIndex?: number; + } + + const md4: md4; + export default md4; +} diff --git a/packages/bun-polyfills/src/types/sync.d.ts b/packages/bun-polyfills/src/types/sync.d.ts new file mode 100644 index 0000000000..87e6794e0c --- /dev/null +++ b/packages/bun-polyfills/src/types/sync.d.ts @@ -0,0 +1,15 @@ +// This file explicitly redefines global types used in order to enforce the correct types, +// regardless of the arbitrary order in which TSC/TSServer decide to load the type libraries in. +// Annoyingly, even this file can sometimes break, so if your types are inverted, try restarting TSServer. + +import '@types/node'; + +declare module 'stream/web' { + interface ReadableStreamDefaultReader { + readMany(): Promise>; + } +} + +declare global { + var performance: typeof import('perf_hooks').performance; +} diff --git a/packages/bun-polyfills/src/types/v8heapsnapshot.d.ts b/packages/bun-polyfills/src/types/v8heapsnapshot.d.ts new file mode 100644 index 0000000000..ed39cad065 --- /dev/null +++ b/packages/bun-polyfills/src/types/v8heapsnapshot.d.ts @@ -0,0 +1,24 @@ +interface V8HeapSnapshot { + snapshot: { + meta: { + node_fields: string[], + node_types: [string[], ...string[]], + edge_fields: string[], + edge_types: [string[], ...string[]], + trace_function_info_fields: string[], + trace_node_fields: string[], + sample_fields: string[], + location_fields: string[] + }, + node_count: number, + edge_count: number, + trace_function_count: number + }, + nodes: number[], + edges: number[], + trace_function_infos: unknown[], + trace_tree: unknown[], + samples: unknown[], + locations: number[], + strings: string[] +} diff --git a/packages/bun-polyfills/src/utils/errors.ts b/packages/bun-polyfills/src/utils/errors.ts new file mode 100644 index 0000000000..cc82efdf00 --- /dev/null +++ b/packages/bun-polyfills/src/utils/errors.ts @@ -0,0 +1,221 @@ +type PosixErrNo = MapKeysType>; +type Win32ErrNo = MapKeysType>; + +export function getPosixSystemErrorMap() { + return new Map([ + [ -7, [ 'E2BIG', 'argument list too long' ] ], + [ -13, [ 'EACCES', 'permission denied' ] ], + [ -98, [ 'EADDRINUSE', 'address already in use' ] ], + [ -99, [ 'EADDRNOTAVAIL', 'address not available' ] ], + [ -97, [ 'EAFNOSUPPORT', 'address family not supported' ] ], + [ -11, [ 'EAGAIN', 'resource temporarily unavailable' ] ], + [ -3000, [ 'EAI_ADDRFAMILY', 'address family not supported' ] ], + [ -3001, [ 'EAI_AGAIN', 'temporary failure' ] ], + [ -3002, [ 'EAI_BADFLAGS', 'bad ai_flags value' ] ], + [ -3013, [ 'EAI_BADHINTS', 'invalid value for hints' ] ], + [ -3003, [ 'EAI_CANCELED', 'request canceled' ] ], + [ -3004, [ 'EAI_FAIL', 'permanent failure' ] ], + [ -3005, [ 'EAI_FAMILY', 'ai_family not supported' ] ], + [ -3006, [ 'EAI_MEMORY', 'out of memory' ] ], + [ -3007, [ 'EAI_NODATA', 'no address' ] ], + [ -3008, [ 'EAI_NONAME', 'unknown node or service' ] ], + [ -3009, [ 'EAI_OVERFLOW', 'argument buffer overflow' ] ], + [ -3014, [ 'EAI_PROTOCOL', 'resolved protocol is unknown' ] ], + [ -3010, [ 'EAI_SERVICE', 'service not available for socket type' ] ], + [ -3011, [ 'EAI_SOCKTYPE', 'socket type not supported' ] ], + [ -114, [ 'EALREADY', 'connection already in progress' ] ], + [ -9, [ 'EBADF', 'bad file descriptor' ] ], + [ -16, [ 'EBUSY', 'resource busy or locked' ] ], + [ -125, [ 'ECANCELED', 'operation canceled' ] ], + [ -4080, [ 'ECHARSET', 'invalid Unicode character' ] ], + [ -103, [ 'ECONNABORTED', 'software caused connection abort' ] ], + [ -111, [ 'ECONNREFUSED', 'connection refused' ] ], + [ -104, [ 'ECONNRESET', 'connection reset by peer' ] ], + [ -89, [ 'EDESTADDRREQ', 'destination address required' ] ], + [ -17, [ 'EEXIST', 'file already exists' ] ], + [ -14, [ 'EFAULT', 'bad address in system call argument' ] ], + [ -27, [ 'EFBIG', 'file too large' ] ], + [ -113, [ 'EHOSTUNREACH', 'host is unreachable' ] ], + [ -4, [ 'EINTR', 'interrupted system call' ] ], + [ -22, [ 'EINVAL', 'invalid argument' ] ], + [ -5, [ 'EIO', 'i/o error' ] ], + [ -106, [ 'EISCONN', 'socket is already connected' ] ], + [ -21, [ 'EISDIR', 'illegal operation on a directory' ] ], + [ -40, [ 'ELOOP', 'too many symbolic links encountered' ] ], + [ -24, [ 'EMFILE', 'too many open files' ] ], + [ -90, [ 'EMSGSIZE', 'message too long' ] ], + [ -36, [ 'ENAMETOOLONG', 'name too long' ] ], + [ -100, [ 'ENETDOWN', 'network is down' ] ], + [ -101, [ 'ENETUNREACH', 'network is unreachable' ] ], + [ -23, [ 'ENFILE', 'file table overflow' ] ], + [ -105, [ 'ENOBUFS', 'no buffer space available' ] ], + [ -19, [ 'ENODEV', 'no such device' ] ], + [ -2, [ 'ENOENT', 'no such file or directory' ] ], + [ -12, [ 'ENOMEM', 'not enough memory' ] ], + [ -64, [ 'ENONET', 'machine is not on the network' ] ], + [ -92, [ 'ENOPROTOOPT', 'protocol not available' ] ], + [ -28, [ 'ENOSPC', 'no space left on device' ] ], + [ -38, [ 'ENOSYS', 'function not implemented' ] ], + [ -107, [ 'ENOTCONN', 'socket is not connected' ] ], + [ -20, [ 'ENOTDIR', 'not a directory' ] ], + [ -39, [ 'ENOTEMPTY', 'directory not empty' ] ], + [ -88, [ 'ENOTSOCK', 'socket operation on non-socket' ] ], + [ -95, [ 'ENOTSUP', 'operation not supported on socket' ] ], + [ -75, [ 'EOVERFLOW', 'value too large for defined data type' ] ], + [ -1, [ 'EPERM', 'operation not permitted' ] ], + [ -32, [ 'EPIPE', 'broken pipe' ] ], + [ -71, [ 'EPROTO', 'protocol error' ] ], + [ -93, [ 'EPROTONOSUPPORT', 'protocol not supported' ] ], + [ -91, [ 'EPROTOTYPE', 'protocol wrong type for socket' ] ], + [ -34, [ 'ERANGE', 'result too large' ] ], + [ -30, [ 'EROFS', 'read-only file system' ] ], + [ -108, [ 'ESHUTDOWN', 'cannot send after transport endpoint shutdown' ] ], + [ -29, [ 'ESPIPE', 'invalid seek' ] ], + [ -3, [ 'ESRCH', 'no such process' ] ], + [ -110, [ 'ETIMEDOUT', 'connection timed out' ] ], + [ -26, [ 'ETXTBSY', 'text file is busy' ] ], + [ -18, [ 'EXDEV', 'cross-device link not permitted' ] ], + [ -4094, [ 'UNKNOWN', 'unknown error' ] ], + [ -4095, [ 'EOF', 'end of file' ] ], + [ -6, [ 'ENXIO', 'no such device or address' ] ], + [ -31, [ 'EMLINK', 'too many links' ] ], + [ -112, [ 'EHOSTDOWN', 'host is down' ] ], + [ -121, [ 'EREMOTEIO', 'remote I/O error' ] ], + [ -25, [ 'ENOTTY', 'inappropriate ioctl for device' ] ], + [ -4028, [ 'EFTYPE', 'inappropriate file type or format' ] ], + [ -84, [ 'EILSEQ', 'illegal byte sequence' ] ], + [ -94, [ 'ESOCKTNOSUPPORT', 'socket type not supported' ] ] + ] as const); +} + +export function getWin32SystemErrorMap() { + return new Map([ + [ -4093, [ 'E2BIG', 'argument list too long' ] ], + [ -4092, [ 'EACCES', 'permission denied' ] ], + [ -4091, [ 'EADDRINUSE', 'address already in use' ] ], + [ -4090, [ 'EADDRNOTAVAIL', 'address not available' ] ], + [ -4089, [ 'EAFNOSUPPORT', 'address family not supported' ] ], + [ -4088, [ 'EAGAIN', 'resource temporarily unavailable' ] ], + [ -3000, [ 'EAI_ADDRFAMILY', 'address family not supported' ] ], + [ -3001, [ 'EAI_AGAIN', 'temporary failure' ] ], + [ -3002, [ 'EAI_BADFLAGS', 'bad ai_flags value' ] ], + [ -3013, [ 'EAI_BADHINTS', 'invalid value for hints' ] ], + [ -3003, [ 'EAI_CANCELED', 'request canceled' ] ], + [ -3004, [ 'EAI_FAIL', 'permanent failure' ] ], + [ -3005, [ 'EAI_FAMILY', 'ai_family not supported' ] ], + [ -3006, [ 'EAI_MEMORY', 'out of memory' ] ], + [ -3007, [ 'EAI_NODATA', 'no address' ] ], + [ -3008, [ 'EAI_NONAME', 'unknown node or service' ] ], + [ -3009, [ 'EAI_OVERFLOW', 'argument buffer overflow' ] ], + [ -3014, [ 'EAI_PROTOCOL', 'resolved protocol is unknown' ] ], + [ -3010, [ 'EAI_SERVICE', 'service not available for socket type' ] ], + [ -3011, [ 'EAI_SOCKTYPE', 'socket type not supported' ] ], + [ -4084, [ 'EALREADY', 'connection already in progress' ] ], + [ -4083, [ 'EBADF', 'bad file descriptor' ] ], + [ -4082, [ 'EBUSY', 'resource busy or locked' ] ], + [ -4081, [ 'ECANCELED', 'operation canceled' ] ], + [ -4080, [ 'ECHARSET', 'invalid Unicode character' ] ], + [ -4079, [ 'ECONNABORTED', 'software caused connection abort' ] ], + [ -4078, [ 'ECONNREFUSED', 'connection refused' ] ], + [ -4077, [ 'ECONNRESET', 'connection reset by peer' ] ], + [ -4076, [ 'EDESTADDRREQ', 'destination address required' ] ], + [ -4075, [ 'EEXIST', 'file already exists' ] ], + [ -4074, [ 'EFAULT', 'bad address in system call argument' ] ], + [ -4036, [ 'EFBIG', 'file too large' ] ], + [ -4073, [ 'EHOSTUNREACH', 'host is unreachable' ] ], + [ -4072, [ 'EINTR', 'interrupted system call' ] ], + [ -4071, [ 'EINVAL', 'invalid argument' ] ], + [ -4070, [ 'EIO', 'i/o error' ] ], + [ -4069, [ 'EISCONN', 'socket is already connected' ] ], + [ -4068, [ 'EISDIR', 'illegal operation on a directory' ] ], + [ -4067, [ 'ELOOP', 'too many symbolic links encountered' ] ], + [ -4066, [ 'EMFILE', 'too many open files' ] ], + [ -4065, [ 'EMSGSIZE', 'message too long' ] ], + [ -4064, [ 'ENAMETOOLONG', 'name too long' ] ], + [ -4063, [ 'ENETDOWN', 'network is down' ] ], + [ -4062, [ 'ENETUNREACH', 'network is unreachable' ] ], + [ -4061, [ 'ENFILE', 'file table overflow' ] ], + [ -4060, [ 'ENOBUFS', 'no buffer space available' ] ], + [ -4059, [ 'ENODEV', 'no such device' ] ], + [ -4058, [ 'ENOENT', 'no such file or directory' ] ], + [ -4057, [ 'ENOMEM', 'not enough memory' ] ], + [ -4056, [ 'ENONET', 'machine is not on the network' ] ], + [ -4035, [ 'ENOPROTOOPT', 'protocol not available' ] ], + [ -4055, [ 'ENOSPC', 'no space left on device' ] ], + [ -4054, [ 'ENOSYS', 'function not implemented' ] ], + [ -4053, [ 'ENOTCONN', 'socket is not connected' ] ], + [ -4052, [ 'ENOTDIR', 'not a directory' ] ], + [ -4051, [ 'ENOTEMPTY', 'directory not empty' ] ], + [ -4050, [ 'ENOTSOCK', 'socket operation on non-socket' ] ], + [ -4049, [ 'ENOTSUP', 'operation not supported on socket' ] ], + [ -4026, [ 'EOVERFLOW', 'value too large for defined data type' ] ], + [ -4048, [ 'EPERM', 'operation not permitted' ] ], + [ -4047, [ 'EPIPE', 'broken pipe' ] ], + [ -4046, [ 'EPROTO', 'protocol error' ] ], + [ -4045, [ 'EPROTONOSUPPORT', 'protocol not supported' ] ], + [ -4044, [ 'EPROTOTYPE', 'protocol wrong type for socket' ] ], + [ -4034, [ 'ERANGE', 'result too large' ] ], + [ -4043, [ 'EROFS', 'read-only file system' ] ], + [ -4042, [ 'ESHUTDOWN', 'cannot send after transport endpoint shutdown' ] ], + [ -4041, [ 'ESPIPE', 'invalid seek' ] ], + [ -4040, [ 'ESRCH', 'no such process' ] ], + [ -4039, [ 'ETIMEDOUT', 'connection timed out' ] ], + [ -4038, [ 'ETXTBSY', 'text file is busy' ] ], + [ -4037, [ 'EXDEV', 'cross-device link not permitted' ] ], + [ -4094, [ 'UNKNOWN', 'unknown error' ] ], + [ -4095, [ 'EOF', 'end of file' ] ], + [ -4033, [ 'ENXIO', 'no such device or address' ] ], + [ -4032, [ 'EMLINK', 'too many links' ] ], + [ -4031, [ 'EHOSTDOWN', 'host is down' ] ], + [ -4030, [ 'EREMOTEIO', 'remote I/O error' ] ], + [ -4029, [ 'ENOTTY', 'inappropriate ioctl for device' ] ], + [ -4028, [ 'EFTYPE', 'inappropriate file type or format' ] ], + [ -4027, [ 'EILSEQ', 'illegal byte sequence' ] ], + [ -4025, [ 'ESOCKTNOSUPPORT', 'socket type not supported' ] ] + ] as const); +} + +export function getPosixToWin32SystemErrorMap() { + const posixEntries = [...getPosixSystemErrorMap().entries()]; + const win32Entries = [...getWin32SystemErrorMap().entries()]; + const map: Map = new Map(); + posixEntries.forEach(([code, val]) => { + const found = win32Entries.find(([_, v]) => v[0] === val[0]); + if (!found) console.error(val[0]); + else map.set(code, found[0]); + }); + return map; +} + +export function getPlatformSystemErrorFromPosix(posixErrNo: PosixErrNo) { + if (process.platform === 'win32') { + const win32errno = getPosixToWin32SystemErrorMap().get(posixErrNo)!; + return getWin32SystemErrorMap().get(win32errno); + } else { + return getPosixSystemErrorMap().get(posixErrNo); + } +} + +export class SystemError extends Error { + constructor(errno: PosixErrNo, syscall?: string, errpath?: string) { + const [errname, errmsg] = getPlatformSystemErrorFromPosix(errno) ?? ['SystemError', 'Unknown system error']; + super(errmsg); + this.name = errname; + this.code = errname; + this.errno = errno; + if (syscall) this.syscall = syscall; + if (errpath) this.path = errpath; + } + errno?: number | undefined; + code?: string | undefined; + path?: string | undefined; + syscall?: string | undefined; +} + +export class NotImplementedError extends Error { + constructor(thing: string, func: AnyCallable = NotImplementedError, overrideMsg: boolean = false) { + super(overrideMsg ? thing : `A polyfill for ${thing} is not yet implemented by bun-polyfills.`); + this.name = 'NotImplementedError'; + Error.captureStackTrace(this, func); + } +} diff --git a/packages/bun-polyfills/src/utils/misc.ts b/packages/bun-polyfills/src/utils/misc.ts new file mode 100644 index 0000000000..74bb9aa010 --- /dev/null +++ b/packages/bun-polyfills/src/utils/misc.ts @@ -0,0 +1,36 @@ +import streams from 'node:stream'; +import type { SpawnOptions, FileBlob } from 'bun'; + +export const getter = (obj: T, key: string | symbol, get: () => any, enumerable = false, configurable = true): void => { + Object.defineProperty(obj, key, { get, configurable, enumerable }); +}; + +export const setter = (obj: T, key: string | symbol, set: () => any, enumerable = false, configurable = true): void => { + Object.defineProperty(obj, key, { set, configurable, enumerable }); +}; + +export const readonly = (obj: T, key: string | symbol, value: unknown, enumerable = false, configurable = true): void => { + Object.defineProperty(obj, key, { value, configurable, enumerable }); +}; + +export function streamToBuffer(stream: streams.Readable | streams.Duplex): Promise { + return new Promise((resolve, reject) => { + const buffers: Uint8Array[] = []; + stream.on("data", (chunk: Uint8Array) => buffers.push(chunk)); + stream.on("end", () => resolve(Buffer.concat(buffers))); + stream.on("error", (err: Error) => reject(err)); + }); +} + +export function isArrayBufferView(value: any): value is ArrayBufferView { + return value !== null && typeof value === 'object' && + value.buffer instanceof ArrayBuffer && typeof value.byteLength === 'number' && typeof value.byteOffset === 'number'; +} + +export function isOptions(options: any): options is SpawnOptions.OptionsObject { + return options !== null && typeof options === 'object'; +} + +export function isFileBlob(blob: any): blob is FileBlob { + return blob instanceof Blob && Reflect.get(blob, 'readable') instanceof ReadableStream && typeof Reflect.get(blob, 'writer') === 'function'; +} diff --git a/packages/bun-polyfills/tsconfig.json b/packages/bun-polyfills/tsconfig.json new file mode 100644 index 0000000000..b00abe3441 --- /dev/null +++ b/packages/bun-polyfills/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "nodenext", + "moduleDetection": "force", + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "inlineSourceMap": true, + "allowJs": true, + "outDir": "dist", + "types": ["node"] + }, + "include": [".", "../bun-types/index.d.ts"], +} diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 20f247d91c..7e8102c872 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -24,7 +24,7 @@ declare module "bun" { import { Encoding as CryptoEncoding } from "crypto"; export interface Env extends Dict, NodeJS.ProcessEnv { - NODE_ENV: string; + NODE_ENV?: string; /** * The timezone used by Intl, Date, etc. @@ -68,7 +68,7 @@ declare module "bun" { export function which( command: string, options?: { PATH?: string; cwd?: string }, - ): string; + ): string | null; export type Serve = | ServeOptions @@ -752,39 +752,40 @@ declare module "bun" { */ export const hash: (( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, + seed?: number | bigint, ) => number | bigint) & Hash; interface Hash { wyhash: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, - ) => number | bigint; - crc32: ( - data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, - ) => number | bigint; + seed?: bigint, + ) => bigint; adler32: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, - ) => number | bigint; + ) => number; + crc32: ( + data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, + ) => number; cityHash32: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, - ) => number | bigint; + ) => number; cityHash64: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - seed?: number, - ) => number | bigint; + seed?: bigint, + ) => bigint; murmur32v3: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: number, - ) => number | bigint; - murmur64v2: ( + ) => number; + murmur32v2: ( data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: number, - ) => number | bigint; + ) => number; + murmur64v2: ( + data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, + seed?: bigint, + ) => bigint; } export type JavaScriptLoader = "jsx" | "js" | "ts" | "tsx"; @@ -2428,8 +2429,8 @@ declare module "bun" { * If you have any ideas, please file an issue https://github.com/oven-sh/bun */ interface HeapSnapshot { - /** "2" */ - version: string; + /** 2 */ + version: number; /** "Inspector" */ type: string; @@ -2675,7 +2676,7 @@ declare module "bun" { * openssl sha512-256 /path/to/file *``` */ - export function sha(input: StringOrBuffer, hashInto?: Uint8Array): Uint8Array; + export function sha(input: StringOrBuffer, hashInto?: TypedArray): TypedArray; /** * diff --git a/packages/bun-types/globals.d.ts b/packages/bun-types/globals.d.ts index 71403eb805..1a10cc8e08 100644 --- a/packages/bun-types/globals.d.ts +++ b/packages/bun-types/globals.d.ts @@ -2648,10 +2648,6 @@ interface ReadableStream { options?: StreamPipeOptions, ): Promise; tee(): [ReadableStream, ReadableStream]; - forEach( - callbackfn: (value: any, key: number, parent: ReadableStream) => void, - thisArg?: any, - ): void; [Symbol.asyncIterator](): AsyncIterableIterator; values(options?: { preventCancel: boolean }): AsyncIterableIterator; } diff --git a/packages/bun-types/tests/hashing.test-d.ts b/packages/bun-types/tests/hashing.test-d.ts index bd9222541b..16d65737d0 100644 --- a/packages/bun-types/tests/hashing.test-d.ts +++ b/packages/bun-types/tests/hashing.test-d.ts @@ -1 +1 @@ -Bun.hash.wyhash("asdf", 1234); +const hash: bigint = Bun.hash.wyhash("asdf", 1234n); diff --git a/test/bun.lockb b/test/bun.lockb index f3d337bb08742e1bd6f1d4dd978c5240fcd12702..b717f5d1a3691570c9d2c7792b0fcfb2a1cad7fe 100755 GIT binary patch delta 21273 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 delta 21310 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 A4*&oF